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

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

[复制链接]
发表于 2014-8-7 17:41 | 显示全部楼层 |阅读模式
上次,已经完成了头部新闻分类栏目的拖动效果。
# B4 Z3 p: V4 C! x1 S' t' P这篇文章是继续去完善APP 今日头条  这个新闻阅读器的其他功能。
, L% p' P' l) T% d这次所实现的功能清单:& m; j5 v% e7 d& D& Y3 b" p$ P
1.通过SlidingMenu实现左右侧拉菜单效果* z1 u* G6 |5 Y0 F  j' N; w
2.通过重写CompoundButton实现--昼夜模式切换按钮效果。$ O7 p7 I& \4 a1 R! V- x
3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。3 u- m+ w, ^# X; |4 o! |
4.通过universal-image-loader库实现了图片的加载和缓存。
* S) P: O; F4 G) U/ G5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。
+ }# r# d% O: f4 u+ L, `( `% [' I, N

0 Z" B3 C5 o3 C2 M$ |1 ?  F下面把每个功能和对应的实现方式相关博文都列出了,方便大家根据自身需求查看。2 n9 O  A" \* w' `% W0 k, ]1 a! a
$ ~9 D* a! G- u
下面就是官方的效果截图,详细操作请继续向下看。, _: D4 w* w: p) ^+ f0 E) G
20140413211055046.jpg 20140413211118031.jpg 9 }2 @3 E& T# F/ V; ~; E
一.通过SlidingMenu实现左右侧拉菜单效果在第一讲中,我们已经知道了,它使用了SlidingMenu 这个侧拉菜单开源库,所以我特地将 SlidingMenu的使用和配置写了一个文章,文章地址:Android SlidingMenu 开源项目 侧拉菜单的使用(详细配置)
( v3 I5 z# x% J3 H5 d. Z( F所以具体的配置就不在这里说明了,大家可以去看上面给的链接。8 o' l3 H- l# r+ k& M. n9 B
1 ^9 G" X1 G  X: K
注意:由于 今日头条用的是左右都可以侧拉的菜单,所以设置侧拉模式为:SlidingMenu.LEFT_RIGHT,代码如下) S0 V2 x1 w6 Q- `3 B
  1.     localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单   
复制代码
由于左右可以拖出菜单,并且中间的ViewPager可以切换新闻页面,所以SlidingMenu的必须如此设置以下属性:
9 I( J. ^; G0 e6 k) B1 {) I
  1.     localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围  
复制代码
SLIDEING_WINDOW就是触摸边缘才会出发,这样的话滑动操作就不用冲突了。8 C5 R- [5 c6 R. U) J, G

! ]0 P' l  S- f' z7 ?: `4 P
为了方便代码的维护,我将SlidingMenu 自定义成了一个DrawerView类,这样的话所有菜单中的操作就和主界面分离开来,代码就不会显得那么臃肿了,并且在用到的地址直接实例化这个类就可以了。0 u+ x' j4 V6 B$ V3 v/ i5 n& V5 {

* o# Z% m" `$ b& {代码如下:! z( \7 K0 O! j  c' w1 @
  1. public class DrawerView implements OnClickListener{
    / J& i' x0 y- G1 s2 d
  2.         private final Activity activity;
    " `- s% g6 |8 Q& V' d- \
  3.         SlidingMenu localSlidingMenu;6 m, \7 v% W% A
  4.         private SwitchButton night_mode_btn;
    4 E& D. j1 [8 P3 u6 P/ S
  5.         private TextView night_mode_text;
    " a# h1 e% c4 C5 z) U1 u
  6.         private RelativeLayout setting_btn;  E( s- k' Y/ A+ T7 o& ?; k2 p( L/ p
  7.         public DrawerView(Activity activity) {4 X$ C; M9 X6 [" ~& J: T
  8.                 this.activity = activity;
    9 ^9 Z. e/ q$ O0 L$ G; P: Z* R
  9.         }
    / @3 i9 M5 A  T/ B

  10. 4 V: y( Y6 ~9 {$ b# M
  11.         public SlidingMenu initSlidingMenu() {
    . B* M) E5 E: j3 ~! i# p# {
  12.                 localSlidingMenu = new SlidingMenu(activity);
    , y4 L. y! X& |' S# k/ N# o( w
  13.                 localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单
    , i0 c# f# p0 y5 z- H, v, ~
  14.                 localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围
      ?- ?8 G. _9 X4 {
  15. //                localSlidingMenu.setTouchModeBehind(SlidingMenu.RIGHT);
    % q0 c. x" L/ S! F1 [$ a6 D
  16.                 localSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);//设置阴影图片的宽度+ {% V) ^5 {3 z" z/ s" l" ^* N! I
  17.                 localSlidingMenu.setShadowDrawable(R.drawable.shadow);//设置阴影图片& {; ~* G" E; Z! l% j
  18.                 localSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);//SlidingMenu划出时主页面显示的剩余宽度
    # N2 s) P- G% k% E( Z, f6 y3 M
  19.                 localSlidingMenu.setFadeDegree(0.35F);//SlidingMenu滑动时的渐变程度. h4 M5 H8 y% M7 H
  20.                 localSlidingMenu.attachToActivity(activity, SlidingMenu.RIGHT);//使SlidingMenu附加在Activity右边
    , X: }- Z2 A' v, t
  21. //                localSlidingMenu.setBehindWidthRes(R.dimen.left_drawer_avatar_size);//设置SlidingMenu菜单的宽度
    ! e! C7 |  H; ^) o- ^  v
  22.                 localSlidingMenu.setMenu(R.layout.left_drawer_fragment);//设置menu的布局文件* c& g: [: C4 z2 S
  23. //                localSlidingMenu.toggle();//动态判断自动关闭或开启SlidingMenu5 p% H& v8 Y$ u; S* u  R
  24.                 localSlidingMenu.setSecondaryMenu(R.layout.profile_drawer_right);
    : q) p9 C& ~, b8 C$ o
  25.                 localSlidingMenu.setSecondaryShadowDrawable(R.drawable.shadowright);
    ! u' |) o% m5 Q# {1 V: L
  26.                 localSlidingMenu.setOnOpenedListener(new SlidingMenu.OnOpenedListener() {- ?4 A* O' h4 b: E
  27.                                         public void onOpened() {  w* W, [, E6 F7 A. V$ H+ o
  28.                                                 : M" ^  R( ^1 A; n
  29.                                         }
    ( e( O7 H2 k: ^! j- b% d$ H* g
  30.                                 });; f6 t7 V5 w  G1 m, X
  31.                 0 z* v! G, V% O. i) ?8 y
  32.                 initView();
    / V! r. x' T+ A( z
  33.                 return localSlidingMenu;  ?# j. Z' p$ I. U% ~4 a
  34.         }# M  O! m3 _5 i8 i& V/ g% W( N

  35. / N- W( w' M  q- e! i$ v/ N9 b
  36.         private void initView() {
    8 \: p% x( N' j( E  ]
  37.                 night_mode_btn = (SwitchButton)localSlidingMenu.findViewById(R.id.night_mode_btn);# e* R( K( Q5 j+ V% z- ]8 f
  38.                 night_mode_text = (TextView)localSlidingMenu.findViewById(R.id.night_mode_text);
    ! D4 }0 A' j( k# M  A4 o5 o& L
  39.                 night_mode_btn.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    2 F; ^/ M" F1 e9 F  s0 P
  40.                        
    4 e, ^8 _4 c/ U, h$ O5 D7 x) f
  41.                         @Override
    $ o6 [* Q; L- z" I( R: ]
  42.                         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    3 A0 ?- A/ H& B$ w* @' V9 S/ }
  43.                                 // TODO Auto-generated method stub/ T8 B* i5 X& ~
  44.                                 if(isChecked){; t: ?. p5 o+ F# T2 z
  45.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));
    % ]$ f0 O3 w+ ]3 l  i. W
  46.                                 }else{
    . s, E% }. {: f/ k: g! ~) u' B/ A
  47.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));  f- k0 B1 w  R& H5 T5 C* {
  48.                                 }
    1 q9 ~4 v5 L/ Z/ E( _3 H
  49.                         }$ K3 I4 m7 Z+ B* v6 Z
  50.                 });
    7 m, u4 x8 [+ n9 ~" l5 O
  51.                 night_mode_btn.setChecked(false);
    . v% K, ]) p9 t( W
  52.                 if(night_mode_btn.isChecked()){
    ' v9 n: Q- V! r8 }5 ~9 j
  53.                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));
    # G! N9 d  W3 M" Y7 x
  54.                 }else{# J) N% |  W. |, h+ j
  55.                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));& b* T: p; Z! l2 `
  56.                 }& P9 e7 J% c2 E3 x; Q* _: Q
  57.                
    # |2 E; a: s! Q2 }
  58.                 setting_btn =(RelativeLayout)localSlidingMenu.findViewById(R.id.setting_btn);, c) q5 z! y& Y+ w& O
  59.                 setting_btn.setOnClickListener(this);3 z! j0 d, V1 P( z5 Z6 y1 u, Q
  60.         }
    . g: E$ c8 U+ r  G# s6 H0 m
  61. + k) j5 V4 s. D% U
  62.         @Override0 z2 z% [. B& A: W2 t* c' Z: B
  63.         public void onClick(View v) {
    / f3 Z- n7 O5 B4 \9 |9 }% g% q
  64.                 switch (v.getId()) {
    : k4 }4 u; V4 e. M" P
  65.                 case R.id.setting_btn:! _, d* z. ~! m
  66.                         activity.startActivity(new Intent(activity,SettingsActivity.class));
    3 l+ O- X1 Z7 a' M/ Z  i
  67.                         activity.overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    # E2 ?( p" J# F) {! @+ v
  68.                         break;
    ( D6 g5 K0 p. {: m0 B$ L
  69. . Y$ b: `0 i6 r+ M0 y, b
  70.                 default:) X5 u8 k9 a9 y4 L& k' P
  71.                         break;3 W6 h3 t4 c4 f: B
  72.                 }$ P" P* g' ^) j- Q9 x
  73.         }. m- G' g& O/ o9 q: x8 |; y
  74. }
复制代码

: \% ^, n% {/ V" B4 y4 S; E2.通过重写CompoundButton实现--昼夜模式切换按钮效果看源码发现,今日头条的中的是SwitchButton昼夜模式选择控件,是通过重写CompoundButton实现的,以下便是实现对应方法的相关帖子:* _/ T& w3 p( M: f& _3 J
SwitchButton 开关按钮 的多种实现方式 (附源码DEMO)
+ @9 d/ O( U- V" ?* h

, E7 T1 [- f) E8 y; ?根据需求选择你喜欢的SwitchButton去修改和使用。7 s/ H& R- K- Y& [/ v9 t0 r

5 q) W# X$ [* U8 d1 r- D/ H; J

  b) ^) j: B4 M3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。在开发中发现,每个新闻列表中对应的ITEM中按钮出发的弹框效果其实是通过PopupWindow实现的,相关博文如下
0 I7 |, N- P: l+ }' E& ]" fandroid 仿 新闻阅读器 菜单弹出效果(附源码DEMO)1 w) j5 g+ D5 y0 x* @

: v: F) ^$ m/ h7 {$ p

/ s" g( t* {5 o7 n9 N4.通过universal-image-loader库实现了图片的加载和缓存。一个新闻客户端肯定涉及到一个图片异步加载缓存的方法,所以这里使用开发中最常用的开源库之一universal-image-loader来实现相关的需求。如果你对这个库的使用不是很了解的话,可以查看以下链接:Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)
, B- B3 z7 j) ^; {! R

) q) M% v; W5 Z# w0 Q) m2 T# |
---根据你需求配置相应的属性即可。

+ u3 B3 W( y7 m7 b( Q2 q8 d1 C  q- v( D
0 K, a' z$ r8 n1 D& w# d- s: X$ t' s' ?

% e' h1 L! y7 j5 i) Y( l9 V5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。新闻客户端中,每个新闻的布局都可能不一样,所以你可以选择2种最常用的方式去根据需求改变一个LISTVIEW里面的布局。
0 K. a: O& p. V  v1 o% s, Y& J1.操作上会麻烦一点,维护起来很方便
- D  P) Q- t" C$ g- w! yBaseAdapter中有个自带的方法,就是getViewTypeCount() ,用户可以使用这个方法获取ITEM的种类数量,之后在getView()的时候判断对应到额种类type之后设给他对应的布局即可,常见的例子有:唱吧中的名人榜  g" J1 D9 q5 ^

/ n  c; V0 S" t
' P0 ~, C" y$ m5 _, t2 y7 H
% k" ~7 K( X8 }8 m: V# q2 }
2.操作上很方便,只用根据需求设置布局的显示隐藏效果,缺点:如果控件一多,不在注释的话,可能维护起来就很麻烦。2 w/ L+ b- D, i( _
! U& }6 v& o; e; U7 ~* z: h" o
8 r; v8 K- a* o8 c
在这里,其实采用第2种方式就可以了,分析它对应的以下布大致的5种局:
4 |" V! t! f, D- ^
* ?& {4 `2 Z# d- D6 E( f! M9 S 20140416213230703.jpg ! K! y" R% F8 q3 W0 L8 x2 b
布局---1
) a  G; h- D0 O: {! @* W+ G* k 20140416213240781.jpg
5 {: n) |$ l/ U* M

2 H, G) C9 J) u( y  M8 Y- p布局---24 T0 Q& K6 U  I" F
20140416213247031.jpg
0 W8 Y; m. w+ W$ K
布局---3$ _% \6 s  \) H, H4 l
20140416213235765.jpg : x7 r  ^/ U+ y
布局---4
* \* c* C' W! L$ }8 k* N 20140416213251921.jpg
2 \/ p. t9 R. y0 x7 Q; ]' ]/ m
布局---5
7 J" H& E: X+ h* r4 c9 r) C3 F  x
* k. ?$ W0 x8 X3 A

% V+ j/ N" J/ A2 m7 W3 {* {: q7 p7 Y& j9 T
( c* k- V  U$ p( x* ]3 a% Y- ?
4 p5 n6 X: B9 T/ E8 `. I
 楼主| 发表于 2014-8-7 17:44 | 显示全部楼层
分析上述布局,其实他们的大体布局是一样的,知识根据图片的张数,图片的大小,以及新闻类别的评论来判断布局的对应显示隐藏问题,只要根据相对应的属性便可以很好的实现这个效果,所以方法2是最合适的。
" F! ~, n8 R9 _; b* V2 p% ?$ E
; ~2 `# h. @7 ^( {/ C' Y
! W. \$ K& k0 S, Y6 n; H3 @* H/ q

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

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


  v2 n9 C: ?3 w开发中的几个问题总结:7 O$ O$ K; J; e# o' l% q8 s. \
关于Fragment: Q8 }5 ~% i- H; ~
1)0 k+ y0 f, f( M0 Y! n
  1. @Override0 b5 p$ q: p0 ~9 K/ L
  2. public void onAttach(Activity activity) {% k. y, I- h  a* O7 t
  3. // TODO Auto-generated method stub
    ) p# `1 {0 b5 M, i- I, N- n9 W
  4. this.activity = activity;" L/ G! f& L% O% H$ U
  5. super.onAttach(activity);1 O* C! [4 K1 x9 o# E2 J
  6. }
复制代码
& e6 |9 X9 ]7 n+ O' p+ O
通过onAttach()方法获取父类ACTIVITY,如果在调用getActivity()方法获取的话,可能会在FRAGMENT被回收后报空指针错误。6 k" a7 q" T2 Y4 i) @" m
2)* e* i" x1 y* }# k2 p$ q$ H
看今日头条的效果是,没切换至类型界面后,才去刷新数据,Fragment自带了这个方法,如下
3 S- `5 x8 E$ p: w& M
  1. public void setUserVisibleHint(boolean isVisibleToUser) {}
复制代码
你可以判断isVisibleToUser属性来知道是否是切换的界面已经完全可见,之后进行操作。代码中也注释了。
# p5 B: k8 S' R8 a8 R- M! [/ Q

下面看看这次的效果图:

20140417195420578.png 20140417195257890.png

20140417195320234.png 20140417195325640.png


7 g8 B! |* l! m9 u) r

最后附上源码DEMO地址:

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

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

* g. X- S/ m) H0 |" V
, f; o- p: V6 F6 v% ]5 n0 o

1 T9 H/ O+ D- T
发表于 2014-9-5 10:30 | 显示全部楼层
谢谢分享     * `0 j  _" j1 H( z* g
发表于 2014-10-27 22:04 | 显示全部楼层
继续支持楼主
发表于 2014-11-3 09:20 | 显示全部楼层
下载看看了下载看看了下载看看了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

© 2001-2014Comsenz Inc.

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