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

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

[复制链接]
发表于 2014-8-7 17:41 | 显示全部楼层 |阅读模式
上次,已经完成了头部新闻分类栏目的拖动效果。; V! M  a/ p- ~) k
这篇文章是继续去完善APP 今日头条  这个新闻阅读器的其他功能。& ~5 Y2 M( \, d! I  }1 W4 [
这次所实现的功能清单:- h3 M: ]( x1 h9 Y
1.通过SlidingMenu实现左右侧拉菜单效果" E9 t. E( A! j$ }2 y. }  `/ A
2.通过重写CompoundButton实现--昼夜模式切换按钮效果。8 [# `% R) ^1 D0 F( ~' c5 l9 ^
3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。
: m/ h2 O9 `& |3 k. f0 Z9 I; U. v' u4.通过universal-image-loader库实现了图片的加载和缓存。
) p7 Y# _( a" K$ T. J" y! U5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。7 i# P( p5 B6 G) G/ d5 N

! U  i( \7 n0 a6 |
, T. O: U, \  u. U/ \* ]" ^$ Y
下面把每个功能和对应的实现方式相关博文都列出了,方便大家根据自身需求查看。* R) Z) v2 k" s6 h  H; B& T( D8 _4 _
1 k9 a  [8 _4 ?- u' S* L
下面就是官方的效果截图,详细操作请继续向下看。5 Z, X: }( q3 x8 b, o  @/ T
20140413211055046.jpg 20140413211118031.jpg
  u2 r3 L0 {3 [9 Y* x6 j一.通过SlidingMenu实现左右侧拉菜单效果在第一讲中,我们已经知道了,它使用了SlidingMenu 这个侧拉菜单开源库,所以我特地将 SlidingMenu的使用和配置写了一个文章,文章地址:Android SlidingMenu 开源项目 侧拉菜单的使用(详细配置). o) Q2 j- O5 W" ^/ X  g. z
所以具体的配置就不在这里说明了,大家可以去看上面给的链接。
0 j* d! H1 ]+ R+ a
- g* K$ M" \( {2 g2 u8 [注意:由于 今日头条用的是左右都可以侧拉的菜单,所以设置侧拉模式为:SlidingMenu.LEFT_RIGHT,代码如下8 ?, z0 \  ^7 l- i
  1.     localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单   
复制代码
由于左右可以拖出菜单,并且中间的ViewPager可以切换新闻页面,所以SlidingMenu的必须如此设置以下属性:! Z1 @& U0 Z, u
  1.     localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围  
复制代码
SLIDEING_WINDOW就是触摸边缘才会出发,这样的话滑动操作就不用冲突了。2 H% D+ w2 ?" M9 m+ [" V5 l/ G

8 o* q0 k# m0 U7 c
为了方便代码的维护,我将SlidingMenu 自定义成了一个DrawerView类,这样的话所有菜单中的操作就和主界面分离开来,代码就不会显得那么臃肿了,并且在用到的地址直接实例化这个类就可以了。
1 D' a6 g5 u1 e& ^$ o

. g: M4 x% ~% `  o6 a2 ~+ ?代码如下:
/ p, l/ A. l% S' D, y- k* S
  1. public class DrawerView implements OnClickListener{% `6 K- D. O! u6 G! |. X
  2.         private final Activity activity;# x6 O# e3 u' x
  3.         SlidingMenu localSlidingMenu;
    " b, L0 r  ]/ y5 @# j0 |* ]) l
  4.         private SwitchButton night_mode_btn;
    5 i7 d3 [. s, f7 h
  5.         private TextView night_mode_text;$ J( a" W6 n7 L) S6 s( @
  6.         private RelativeLayout setting_btn;; l  |8 o$ o$ c* c, ~" l& S
  7.         public DrawerView(Activity activity) {
    - |9 l5 d8 ], a% u) p$ a
  8.                 this.activity = activity;
    * a5 N" Q- \* K  j. _! [& r: E( n
  9.         }
    4 `! S$ ?6 J4 `9 Y
  10. , P8 A4 i5 ^( M, u" b+ r8 R
  11.         public SlidingMenu initSlidingMenu() {9 S& B. I/ w6 t' k$ R" V2 k% b
  12.                 localSlidingMenu = new SlidingMenu(activity);
    ; n/ c$ x. X4 |% x# S! @
  13.                 localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单% O% ]) a2 s& r& {" A$ [. ^
  14.                 localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围
    * c5 X# e5 H$ A& ?
  15. //                localSlidingMenu.setTouchModeBehind(SlidingMenu.RIGHT);( a! p( d, B, [* Z' P
  16.                 localSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);//设置阴影图片的宽度8 `  ^+ C# [6 x3 W$ P
  17.                 localSlidingMenu.setShadowDrawable(R.drawable.shadow);//设置阴影图片$ U$ ^) v+ N' e2 m2 l8 c
  18.                 localSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);//SlidingMenu划出时主页面显示的剩余宽度
    ) \7 j+ s  T- C1 |
  19.                 localSlidingMenu.setFadeDegree(0.35F);//SlidingMenu滑动时的渐变程度
    ( e: N5 Z( \" J+ V1 l+ {6 H
  20.                 localSlidingMenu.attachToActivity(activity, SlidingMenu.RIGHT);//使SlidingMenu附加在Activity右边
    3 e' O2 i0 d( w3 p6 U$ j9 ^$ O
  21. //                localSlidingMenu.setBehindWidthRes(R.dimen.left_drawer_avatar_size);//设置SlidingMenu菜单的宽度
    ' x: H( N0 }2 K, ^9 p
  22.                 localSlidingMenu.setMenu(R.layout.left_drawer_fragment);//设置menu的布局文件2 V7 ^9 \, _( P0 i# a. z7 x
  23. //                localSlidingMenu.toggle();//动态判断自动关闭或开启SlidingMenu
    # E  u! f8 T; ?, v, B- b, }
  24.                 localSlidingMenu.setSecondaryMenu(R.layout.profile_drawer_right);. H. g6 t" y: x9 k4 h
  25.                 localSlidingMenu.setSecondaryShadowDrawable(R.drawable.shadowright);2 z8 |- x- X5 P- Z6 a
  26.                 localSlidingMenu.setOnOpenedListener(new SlidingMenu.OnOpenedListener() {
    - ~  L. x( A% w1 z
  27.                                         public void onOpened() {
    3 @5 g6 V. w3 ~, ?' z, G7 m2 n
  28.                                                
    & e2 a& N2 u5 c: E" X2 Q
  29.                                         }# @: F6 S/ ?: s6 ]8 m4 E
  30.                                 });1 R  @1 Q/ n' F; i( C8 Y
  31.                
    0 {- x4 z2 l! [# i) Z
  32.                 initView();9 R( p2 y1 Q9 j
  33.                 return localSlidingMenu;  o( M7 G/ `! d1 r0 c; }
  34.         }* x% l% ^- Y& y% N. g$ G
  35. + F! b2 s% W. u% B. v% M
  36.         private void initView() {
    7 Q: j* Z" ]: z" G- g% f; o
  37.                 night_mode_btn = (SwitchButton)localSlidingMenu.findViewById(R.id.night_mode_btn);
    7 R  ]2 `' d' j3 |+ y5 t; G
  38.                 night_mode_text = (TextView)localSlidingMenu.findViewById(R.id.night_mode_text);
    ! d! a$ P( O. w
  39.                 night_mode_btn.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    + g8 u  F9 h2 l: K; F6 U: t
  40.                        
    1 |4 z9 c* y  I- G- ]& }' `
  41.                         @Override$ t* x$ D1 ~; y( s; u% T* S
  42.                         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {5 o* n, @, d; M$ i( ^
  43.                                 // TODO Auto-generated method stub
    5 V9 S( d. }/ e8 }
  44.                                 if(isChecked){
    ; b% u: H. t/ {0 ]
  45.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));* d3 {+ b; r* V- ^: r7 Q
  46.                                 }else{1 A+ B7 s1 B/ |0 A' F) g
  47.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));
    $ }3 Z' V. z4 ^+ q& R8 q
  48.                                 }+ o. D% c& v! ~
  49.                         }, \: B% r8 b& S* ~
  50.                 });- F0 a1 r& ~0 Q# S5 `
  51.                 night_mode_btn.setChecked(false);
    & y0 l5 z1 \% R0 g' G0 I( h
  52.                 if(night_mode_btn.isChecked()){9 N$ G9 K. w: M4 n; r
  53.                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));
    ) L$ p" U' [: X3 I. L
  54.                 }else{4 T! R& N: h  F$ q5 }
  55.                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));1 e3 M$ u" f  {6 A( d5 @
  56.                 }
    0 F% q* y$ b0 k. g
  57.                
    9 J  D. ]  _' x* o! D
  58.                 setting_btn =(RelativeLayout)localSlidingMenu.findViewById(R.id.setting_btn);
    6 }/ {) u! A, D+ t# ^( a
  59.                 setting_btn.setOnClickListener(this);* K4 ?/ p0 B) n5 O0 m( S$ w" C
  60.         }7 _. a) M4 p6 f# q3 h8 ~: s. V
  61. ; @! h& G/ I  i8 ?. _9 Z
  62.         @Override
      q7 N# E8 E0 j1 z* [4 h
  63.         public void onClick(View v) {
    : m7 h/ b2 ]- d4 r
  64.                 switch (v.getId()) {
    . N2 S1 q) l# d' A& [
  65.                 case R.id.setting_btn:# Y, f; ]" F; @; r: j
  66.                         activity.startActivity(new Intent(activity,SettingsActivity.class));8 C( I7 x7 k" A1 Q$ G; ]
  67.                         activity.overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    : m9 {2 b" n6 f3 y" q
  68.                         break;
    ; g* t3 p6 D1 x+ t
  69. 3 I3 a: [0 l0 y4 N! P- h* |$ X
  70.                 default:
    ) q( X8 w+ t+ U% K! G
  71.                         break;
    ) K/ R# A  N; r/ y1 e) I
  72.                 }
    . e# M. ~% I0 j7 g! ?
  73.         }
    6 x4 C, V% n# Y$ x
  74. }
复制代码

5 b; |. O6 O: ?" h2.通过重写CompoundButton实现--昼夜模式切换按钮效果看源码发现,今日头条的中的是SwitchButton昼夜模式选择控件,是通过重写CompoundButton实现的,以下便是实现对应方法的相关帖子:& F5 e3 a) o+ H$ E, X+ k1 \
SwitchButton 开关按钮 的多种实现方式 (附源码DEMO)
0 m  o0 R6 \$ ^* f- `. ]
5 U, A8 _+ e, J+ T' W; p4 j
根据需求选择你喜欢的SwitchButton去修改和使用。
1 `+ j: B, f- m: Q& f# A7 q) T3 t+ U, c% I9 Z

) |0 N  x3 e4 @% C. t3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。在开发中发现,每个新闻列表中对应的ITEM中按钮出发的弹框效果其实是通过PopupWindow实现的,相关博文如下
- N/ [& h6 ^8 wandroid 仿 新闻阅读器 菜单弹出效果(附源码DEMO)4 K9 j! j: a' A- Y

  i9 j! A" b  I
0 q0 e/ S4 w6 r- E4 ]
4.通过universal-image-loader库实现了图片的加载和缓存。一个新闻客户端肯定涉及到一个图片异步加载缓存的方法,所以这里使用开发中最常用的开源库之一universal-image-loader来实现相关的需求。如果你对这个库的使用不是很了解的话,可以查看以下链接:Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)4 [, H$ t# _5 }

6 c" E& m( f+ x0 J' y7 f1 l$ i
---根据你需求配置相应的属性即可。
/ ^& J2 R2 g$ y* u

! i9 L) P* ]+ x& T
- G7 q3 G" X+ Y7 `- _% H& s. v

1 @3 ?# S- L2 ^  U) z+ D5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。新闻客户端中,每个新闻的布局都可能不一样,所以你可以选择2种最常用的方式去根据需求改变一个LISTVIEW里面的布局。
( C; Z$ C# v# @. S4 {2 B0 G9 W1.操作上会麻烦一点,维护起来很方便2 r' h4 i6 x. X! G* q" m; o
BaseAdapter中有个自带的方法,就是getViewTypeCount() ,用户可以使用这个方法获取ITEM的种类数量,之后在getView()的时候判断对应到额种类type之后设给他对应的布局即可,常见的例子有:唱吧中的名人榜
" T& Q& X8 ~" X% W3 I

! N/ t% g) `7 U9 Z5 d8 l
; x; N- X* r: f1 d+ N, N
, Y+ i2 R6 ?6 j9 F. G
2.操作上很方便,只用根据需求设置布局的显示隐藏效果,缺点:如果控件一多,不在注释的话,可能维护起来就很麻烦。
$ M+ L  `. V  _
0 y) p) P/ V2 q3 i$ S* ~3 g

! g2 x6 p6 G( J  l' Z9 F: X在这里,其实采用第2种方式就可以了,分析它对应的以下布大致的5种局:
; J( C. ^, A7 E: i7 i
1 h1 V) u5 C2 C# O% l# H3 }6 V 20140416213230703.jpg . V3 d6 s- K  ^; J# Q& g2 p
布局---1 & Q8 _# k6 W! j$ x5 V/ N
20140416213240781.jpg / M2 Y6 ^' I" ?
0 ?) Q7 P6 t9 y5 a
布局---2
$ e8 f9 E, o" Z8 s 20140416213247031.jpg 9 `  c) ^* E2 f; q4 W
布局---3
, c8 S6 P3 n. K7 R9 e 20140416213235765.jpg   a! Y! y# T; k( e
布局---4
, S, L0 q, ]+ [& h) t- [ 20140416213251921.jpg
( ]/ k6 ?8 F! Y: x  Z4 d" [8 X
布局---51 l3 m! d) ^- i. u0 v

* M- P2 m; e' e. w* u- }/ Z/ ]/ J
$ d* j: v8 p: S' @7 Q- H* _& T

2 p+ q- f! X7 u3 d
8 Q* O7 u" [: l: ^4 x  R
" ?  O4 ?2 c6 ^$ r3 A  @2 E( i5 a" s$ |( M2 n" d9 O5 D
 楼主| 发表于 2014-8-7 17:44 | 显示全部楼层
分析上述布局,其实他们的大体布局是一样的,知识根据图片的张数,图片的大小,以及新闻类别的评论来判断布局的对应显示隐藏问题,只要根据相对应的属性便可以很好的实现这个效果,所以方法2是最合适的。
8 B* a( i4 w" d7 k) s1 E# J$ `+ g- }/ t. q
3 l7 v6 B; g$ Y4 C! y$ o4 }

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

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


2 U: t9 e1 g& b2 z  t9 U; w, k, e# a开发中的几个问题总结:
+ p; g! T( v8 t0 e关于Fragment  i. |5 I7 o; u7 {/ m
1)
- W* b: }9 _! D5 }) I' l/ d
  1. @Override7 B. y+ m5 I, G' F4 Q  x" D2 e* _
  2. public void onAttach(Activity activity) {8 U+ N, S- y7 ^  b
  3. // TODO Auto-generated method stub
    0 ^5 p9 O' k6 a1 o- ?' |% o
  4. this.activity = activity;
    & n2 |. |9 L9 x8 M4 h( m8 g# H
  5. super.onAttach(activity);) h! b; h4 G- R: b$ z
  6. }
复制代码

5 F% B* s$ y# D通过onAttach()方法获取父类ACTIVITY,如果在调用getActivity()方法获取的话,可能会在FRAGMENT被回收后报空指针错误。
( h% f; p( l8 y, j2)# }8 u, T$ w  \+ q, a
看今日头条的效果是,没切换至类型界面后,才去刷新数据,Fragment自带了这个方法,如下
2 f5 M- q5 j3 I- |  {( _/ g' K+ U5 j
  1. public void setUserVisibleHint(boolean isVisibleToUser) {}
复制代码
你可以判断isVisibleToUser属性来知道是否是切换的界面已经完全可见,之后进行操作。代码中也注释了。
5 X$ i, h- J  F) k1 i/ V

下面看看这次的效果图:

20140417195420578.png 20140417195257890.png

20140417195320234.png 20140417195325640.png


, E0 N& z2 X  U% N. ~

最后附上源码DEMO地址:

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

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

3 n8 L: h% J' n" `, ~

  T8 v( F8 z0 S4 E4 m& p1 D
; q0 p8 K& i7 C$ j3 q- P
发表于 2014-9-5 10:30 | 显示全部楼层
谢谢分享     ! R4 U. [6 K# s, h
发表于 2014-10-27 22:04 | 显示全部楼层
继续支持楼主
发表于 2014-11-3 09:20 | 显示全部楼层
下载看看了下载看看了下载看看了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

© 2001-2014Comsenz Inc.

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