请选择 进入手机版 | 继续访问电脑版
查看: 13589|回复: 8

[Android开发资源分享] Android高仿系列项目:今日头条—新闻阅读器(2)

[复制链接]
发表于 2014-8-7 17:41 | 显示全部楼层 |阅读模式
上次,已经完成了头部新闻分类栏目的拖动效果。8 _3 o. Q7 v  ]; x' g
这篇文章是继续去完善APP 今日头条  这个新闻阅读器的其他功能。; P2 u  g" p/ O0 c: I8 g# I7 U. Y
这次所实现的功能清单:
  ?8 F9 C+ W8 x; P- I1.通过SlidingMenu实现左右侧拉菜单效果6 o  ~2 c8 }& m2 u( Y% m
2.通过重写CompoundButton实现--昼夜模式切换按钮效果。1 l& n' u4 ]# \9 ?7 Y8 E
3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。
# a9 d5 K% _" d; j+ d  x: E$ v4.通过universal-image-loader库实现了图片的加载和缓存。
0 E' d. m0 M7 K5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。0 S# P- z. J& [4 x$ u4 W* w& t

9 d# O3 ^+ v: \# U0 O

- }: S) b" E+ l+ t% T! c0 Q* g下面把每个功能和对应的实现方式相关博文都列出了,方便大家根据自身需求查看。0 l; Y% F6 Z5 W5 n7 r3 s

; a& O) {: L! r' o6 m下面就是官方的效果截图,详细操作请继续向下看。
, Z1 d/ j' \8 a, M3 ^- j 20140413211055046.jpg 20140413211118031.jpg
7 ?7 R8 ~4 A4 l/ D, N0 \: s6 @一.通过SlidingMenu实现左右侧拉菜单效果在第一讲中,我们已经知道了,它使用了SlidingMenu 这个侧拉菜单开源库,所以我特地将 SlidingMenu的使用和配置写了一个文章,文章地址:Android SlidingMenu 开源项目 侧拉菜单的使用(详细配置)
  [% o: r" B; O6 y7 U4 ]! ?所以具体的配置就不在这里说明了,大家可以去看上面给的链接。0 R8 A7 |' c, q

: U+ s% A# x& y7 S0 v, l. I注意:由于 今日头条用的是左右都可以侧拉的菜单,所以设置侧拉模式为:SlidingMenu.LEFT_RIGHT,代码如下( z# |+ m$ P. D! I
  1.     localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单   
复制代码
由于左右可以拖出菜单,并且中间的ViewPager可以切换新闻页面,所以SlidingMenu的必须如此设置以下属性:: r9 b3 _& \$ s7 M8 W8 A
  1.     localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围  
复制代码
SLIDEING_WINDOW就是触摸边缘才会出发,这样的话滑动操作就不用冲突了。
; ^/ ~# Y" z7 M  C8 p: q( M
$ D& U" v/ Y' u" |+ X) }
为了方便代码的维护,我将SlidingMenu 自定义成了一个DrawerView类,这样的话所有菜单中的操作就和主界面分离开来,代码就不会显得那么臃肿了,并且在用到的地址直接实例化这个类就可以了。9 K, E2 s: ?8 s

$ I2 @4 w0 }. @代码如下:
' G1 p+ g6 f  r/ a( l: q9 H
  1. public class DrawerView implements OnClickListener{  V# l) Z" G" _* x/ M- }; i9 n) D
  2.         private final Activity activity;
    * s  j! T. ?2 z
  3.         SlidingMenu localSlidingMenu;$ t7 r$ q9 Z3 U5 l1 ?9 o9 S
  4.         private SwitchButton night_mode_btn;! A* ]! l6 Q; b  y, k
  5.         private TextView night_mode_text;
    + f' K0 Z1 A3 R$ X7 _8 Z
  6.         private RelativeLayout setting_btn;
      C9 @# g) H, x
  7.         public DrawerView(Activity activity) {; S& ~, }: a3 U( E
  8.                 this.activity = activity;
    ; h7 A4 r2 c# G& z; S* R" o
  9.         }* {* U9 C1 c% k# {2 Y: j

  10. : ?; x0 u% q/ w) ^5 V) s
  11.         public SlidingMenu initSlidingMenu() {
    ) s& ?1 r% o. a5 ]. \$ q: i& }
  12.                 localSlidingMenu = new SlidingMenu(activity);
    6 A; ]4 I. w1 ?  Z& U
  13.                 localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单
    . W6 ~) p! b# m$ U- n6 P
  14.                 localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围2 d5 C& J% O; o! E
  15. //                localSlidingMenu.setTouchModeBehind(SlidingMenu.RIGHT);# W7 I( C  b! h8 N+ G8 ?6 m
  16.                 localSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);//设置阴影图片的宽度& ^+ w1 p2 {  F3 O& q
  17.                 localSlidingMenu.setShadowDrawable(R.drawable.shadow);//设置阴影图片
    # P4 C' j. H1 L6 r, ?# U
  18.                 localSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);//SlidingMenu划出时主页面显示的剩余宽度* w& N, o" P! z1 j( O+ f8 ~
  19.                 localSlidingMenu.setFadeDegree(0.35F);//SlidingMenu滑动时的渐变程度
    5 h/ d6 I' H6 k4 P. @5 j6 P& B9 s
  20.                 localSlidingMenu.attachToActivity(activity, SlidingMenu.RIGHT);//使SlidingMenu附加在Activity右边7 i) _5 t6 S" r: g5 b) @( H% g9 E
  21. //                localSlidingMenu.setBehindWidthRes(R.dimen.left_drawer_avatar_size);//设置SlidingMenu菜单的宽度3 S% s4 @, }' g/ l' m1 J
  22.                 localSlidingMenu.setMenu(R.layout.left_drawer_fragment);//设置menu的布局文件
    7 r# o; w) k" S2 T8 @6 p! h7 ?; g
  23. //                localSlidingMenu.toggle();//动态判断自动关闭或开启SlidingMenu4 Y' b. }+ p* @/ n+ E. y
  24.                 localSlidingMenu.setSecondaryMenu(R.layout.profile_drawer_right);
      x! H6 I/ U( `7 w
  25.                 localSlidingMenu.setSecondaryShadowDrawable(R.drawable.shadowright);
    $ K( L' n0 N) n  _# R8 S
  26.                 localSlidingMenu.setOnOpenedListener(new SlidingMenu.OnOpenedListener() {
    : ?# r1 H2 K0 L" g. J, z/ l; Z
  27.                                         public void onOpened() {
    ) R' d1 t( ~. \! z9 ]+ v  S9 M
  28.                                                
    5 C$ L. u8 i" N# V  S
  29.                                         }
    . [( s7 C, }" X
  30.                                 });
    0 g% y$ f: m% a& m* c
  31.                 : K9 R4 ~, {9 H' I, f9 t7 _
  32.                 initView();
    2 I+ m# p$ {, ?; ^' m0 B' l5 B7 N
  33.                 return localSlidingMenu;
    9 [+ l0 Q( P; N/ g% `( Q
  34.         }
    % a( D5 z$ Z$ @7 f
  35. ( h5 i# e; z! `, ?9 Q
  36.         private void initView() {
    ' G: C- r7 V" K+ o( P1 @: W3 j8 X
  37.                 night_mode_btn = (SwitchButton)localSlidingMenu.findViewById(R.id.night_mode_btn);
    3 [) E- g3 J' S  K
  38.                 night_mode_text = (TextView)localSlidingMenu.findViewById(R.id.night_mode_text);  c+ m+ i: ]" V$ U5 _
  39.                 night_mode_btn.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    0 n  k- K6 N% n! z
  40.                         , B% S) n2 n0 J  x
  41.                         @Override
    1 S, [5 s4 T# m" z& u
  42.                         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {' L0 \$ v5 h3 e1 p4 a6 j  f; D* z
  43.                                 // TODO Auto-generated method stub9 D2 D0 `) _8 `
  44.                                 if(isChecked){
    * M6 ~% b0 W/ t0 a( m. K
  45.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));- a, ^- ~$ u: ^1 t0 B0 D
  46.                                 }else{
    / B0 O! Z& o: @2 K! F9 q) f: E
  47.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));
    # O5 ]  D- u0 A" O. I/ ^
  48.                                 }
    $ \* [1 s. J% a7 e! s' X
  49.                         }
    ( F' q2 t9 d5 T% k; F$ v$ J
  50.                 });
    " X' a' g( [! W/ ^$ \, H
  51.                 night_mode_btn.setChecked(false);% h- E* f# d8 k, ^- Y$ L
  52.                 if(night_mode_btn.isChecked()){" {) o+ n/ y1 d
  53.                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));0 P. c) I; |" b7 ~; {
  54.                 }else{+ n: x: X+ e- E- U) z$ H
  55.                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));
    7 c9 o7 N8 M7 Z3 l0 s2 l* I
  56.                 }6 B8 y5 I. ~6 d6 j' S" V
  57.                
    # [9 B2 ]# l/ L1 x
  58.                 setting_btn =(RelativeLayout)localSlidingMenu.findViewById(R.id.setting_btn);
    0 Y5 k7 x- Q  \; f/ G! L5 {" Y5 S1 o
  59.                 setting_btn.setOnClickListener(this);
    . b6 x+ m6 L3 W* q# w
  60.         }
    + [; m+ b- x; R0 z0 Z

  61. % B# [1 M% s" t; u. a: Q
  62.         @Override
    8 x8 E/ B7 }+ Q
  63.         public void onClick(View v) {; F, B: }+ j: S
  64.                 switch (v.getId()) {
      h% t, N7 Q8 y& ^% ^- o
  65.                 case R.id.setting_btn:
    % s7 C( J5 K6 ^  |
  66.                         activity.startActivity(new Intent(activity,SettingsActivity.class));+ a7 b/ \; {- O5 d, {% g0 H3 f
  67.                         activity.overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    9 L2 n7 k6 B4 }/ Y" |: f
  68.                         break;- P; M$ t% S$ p' I& w

  69. * m) _/ g( E- ~/ p$ G+ k
  70.                 default:' p% J2 Q9 F1 G9 d  H; c
  71.                         break;5 h3 `1 X/ ^, g6 J, |' |$ Y  N
  72.                 }
    1 U9 v& x0 t8 V
  73.         }3 d' o& `& `% X; o
  74. }
复制代码

5 F9 p& \% c% ]% D6 }/ F0 P2.通过重写CompoundButton实现--昼夜模式切换按钮效果看源码发现,今日头条的中的是SwitchButton昼夜模式选择控件,是通过重写CompoundButton实现的,以下便是实现对应方法的相关帖子:
2 x$ W8 S$ D" d( USwitchButton 开关按钮 的多种实现方式 (附源码DEMO)
+ _2 F5 ?; D$ |/ V
. t& P* L7 h) O; k8 @
根据需求选择你喜欢的SwitchButton去修改和使用。
( `! W' I5 y) A: Y; u" C, P, s) I
( H5 B' F, e+ k' w* w$ m( Q1 Y
3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。在开发中发现,每个新闻列表中对应的ITEM中按钮出发的弹框效果其实是通过PopupWindow实现的,相关博文如下
9 |  y% c2 O4 n$ l& Vandroid 仿 新闻阅读器 菜单弹出效果(附源码DEMO)  d3 ?0 g7 ]; \+ }

: y3 h- ^( Q2 ?# z' a, C" K8 P

( u, P, S: k) d. g0 t, u( \4.通过universal-image-loader库实现了图片的加载和缓存。一个新闻客户端肯定涉及到一个图片异步加载缓存的方法,所以这里使用开发中最常用的开源库之一universal-image-loader来实现相关的需求。如果你对这个库的使用不是很了解的话,可以查看以下链接:Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)7 I1 p/ ?" Y" G  V! n9 B/ B9 @

0 \1 z7 U! o9 Z/ B. L
---根据你需求配置相应的属性即可。
6 E3 B2 f' D( f5 u) G' m6 X

4 G5 T# f3 m& u# Z5 O& {( r
$ I% {6 B2 V2 X4 s- m

% g( s' `" M) @* K% f( v5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。新闻客户端中,每个新闻的布局都可能不一样,所以你可以选择2种最常用的方式去根据需求改变一个LISTVIEW里面的布局。
6 y9 O+ i6 s( o) b: h1.操作上会麻烦一点,维护起来很方便$ t8 e6 X9 Y* @
BaseAdapter中有个自带的方法,就是getViewTypeCount() ,用户可以使用这个方法获取ITEM的种类数量,之后在getView()的时候判断对应到额种类type之后设给他对应的布局即可,常见的例子有:唱吧中的名人榜
# J* V. D: t1 s1 n5 H% u3 k
. e# G$ W. b! G. ~

, I, z& H& {8 J) }0 }) _8 Y3 N

' u2 V' O' D; @2.操作上很方便,只用根据需求设置布局的显示隐藏效果,缺点:如果控件一多,不在注释的话,可能维护起来就很麻烦。8 D, [- s( g5 E" R
, L5 R% _7 O* p* d1 Z- N8 Y
+ W1 Z* b9 t# [) K* O; S
在这里,其实采用第2种方式就可以了,分析它对应的以下布大致的5种局:
, z8 t& P5 U6 ?6 V' _2 l" C; |5 o  @1 |0 M  }
20140416213230703.jpg
  x4 y) p6 d. V2 X1 S布局---1
& J: q5 p( G- o, Q9 u 20140416213240781.jpg
8 M8 I2 @6 r- n) o" p3 O" D

1 u) Y6 a6 O6 V0 C2 k布局---2
9 v: p8 d" z- @  x2 r; g- H" I! h( B0 b 20140416213247031.jpg 9 S  a& I" A0 \4 n9 v' O" D
布局---3
, A% A! P( e  M. J  P 20140416213235765.jpg $ Y# S8 \  G0 \, N" r+ X7 Z
布局---4  s+ U8 z2 x% O% @4 J# W
20140416213251921.jpg
$ O. u# p/ q$ x( ^0 w2 e% T
布局---5
' z1 G# `, U& t3 L+ p9 ?8 V' \1 B7 b' ^- k5 K# U2 o) H5 k

' x" T6 u2 |/ H, V% A, u

' S; n: D# ^8 w  Y
* A$ }$ P8 m- q, L6 `$ N% ]3 @
( J2 I) @% M$ i8 n( S& S0 b5 }" Y) V
 楼主| 发表于 2014-8-7 17:44 | 显示全部楼层
分析上述布局,其实他们的大体布局是一样的,知识根据图片的张数,图片的大小,以及新闻类别的评论来判断布局的对应显示隐藏问题,只要根据相对应的属性便可以很好的实现这个效果,所以方法2是最合适的。
* I1 M* w# k9 i+ b
! [" c8 T/ _0 `; ^7 [. r( B- v
% j0 F3 L& e& m, _: e6 f8 i. s

其他操作就是根据服务器返回的数据类型,是否为空等作相应操作即可。

上述就是目前完成的进度,虽然数据啥都是在本地写死的,可是已经大体的算得上是的一个新闻阅读器了,之后会继续完善其他为完成的功能。


0 R$ T: ~$ h; S. q: D开发中的几个问题总结:9 j, ^$ q. v7 R- G4 H; L: d
关于Fragment
% K2 {+ v  \1 D; g9 r1)
6 A  G% ^& z* h
  1. @Override1 l9 M0 h) E5 _3 @% v
  2. public void onAttach(Activity activity) {
    # `6 R3 v% w8 B. _3 t
  3. // TODO Auto-generated method stub  b* x( F# O  f$ V' V$ s
  4. this.activity = activity;
    / R' [$ ]) u2 k
  5. super.onAttach(activity);( _8 P& w, w6 m( o
  6. }
复制代码
5 M8 r' f9 a: K0 ~$ \
通过onAttach()方法获取父类ACTIVITY,如果在调用getActivity()方法获取的话,可能会在FRAGMENT被回收后报空指针错误。/ |# u% q" c' W
2)
! }0 Z5 q! F, q! A) O2 Y) K看今日头条的效果是,没切换至类型界面后,才去刷新数据,Fragment自带了这个方法,如下" S, t6 ]* |' Q  _
  1. public void setUserVisibleHint(boolean isVisibleToUser) {}
复制代码
你可以判断isVisibleToUser属性来知道是否是切换的界面已经完全可见,之后进行操作。代码中也注释了。
6 t3 P9 i& @" k, c

下面看看这次的效果图:

20140417195420578.png 20140417195257890.png

20140417195320234.png 20140417195325640.png

$ \3 e5 ^2 u' G( z& B6 X

最后附上源码DEMO地址:

游客,如果您要查看本帖隐藏内容请回复

可能在代码方面写的不是很好,优化不足,希望大家提出来,共同进步。


1 ^& R9 U' f& \1 h3 s+ a) U' d, D$ ^3 }% ^. ?1 \% T0 b. p+ [

  m+ X+ g; u$ S( M0 i/ j( t; n
发表于 2014-9-5 10:30 | 显示全部楼层
谢谢分享     
9 F, x! F' ?4 G4 m4 \% ?' n" T9 ~
发表于 2014-10-27 22:04 | 显示全部楼层
继续支持楼主
发表于 2014-11-3 09:20 | 显示全部楼层
下载看看了下载看看了下载看看了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

© 2001-2014Comsenz Inc.

快速回复 返回顶部 返回列表