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

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

[复制链接]
发表于 2014-8-7 17:41 | 显示全部楼层 |阅读模式
上次,已经完成了头部新闻分类栏目的拖动效果。
+ l2 f- L7 k: S2 ]* Y1 y/ r这篇文章是继续去完善APP 今日头条  这个新闻阅读器的其他功能。
- P' C6 P" P5 u& U这次所实现的功能清单:
8 U$ t- B, d  X+ _& f2 ~4 l1.通过SlidingMenu实现左右侧拉菜单效果
; p/ Z% N- R$ [2.通过重写CompoundButton实现--昼夜模式切换按钮效果。
+ x* O0 I& i( w1 K) F. T. D9 ]! T" J3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。
5 a# D6 Q7 X1 B4.通过universal-image-loader库实现了图片的加载和缓存。* h: P$ Y7 p/ K
5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。
( _9 G5 w5 f; a  @6 W4 i0 J7 X; [7 M$ U. u0 H/ _9 ~1 _; \: a+ F

$ h* ]& b: g3 U. j2 V( u下面把每个功能和对应的实现方式相关博文都列出了,方便大家根据自身需求查看。- N! B) m. S6 R( s0 d% U8 i) q

& ^4 Y! s* A# B: E/ c0 K下面就是官方的效果截图,详细操作请继续向下看。' O' L6 h2 R5 s4 k. ?7 c( ^- s% s1 i
20140413211055046.jpg 20140413211118031.jpg
  P6 ~* p* e2 a4 k' y: K一.通过SlidingMenu实现左右侧拉菜单效果在第一讲中,我们已经知道了,它使用了SlidingMenu 这个侧拉菜单开源库,所以我特地将 SlidingMenu的使用和配置写了一个文章,文章地址:Android SlidingMenu 开源项目 侧拉菜单的使用(详细配置)
, G. `: K$ h  e* h所以具体的配置就不在这里说明了,大家可以去看上面给的链接。
! i2 `7 u  |; d8 ], ]' W' [! o( E! \. B. T
注意:由于 今日头条用的是左右都可以侧拉的菜单,所以设置侧拉模式为:SlidingMenu.LEFT_RIGHT,代码如下
& ~! n" c! D* j- |
  1.     localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单   
复制代码
由于左右可以拖出菜单,并且中间的ViewPager可以切换新闻页面,所以SlidingMenu的必须如此设置以下属性:
! D; s9 |  o# y" w: \  r( P& b
  1.     localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围  
复制代码
SLIDEING_WINDOW就是触摸边缘才会出发,这样的话滑动操作就不用冲突了。
7 X# w" O4 O* ~. c- ?/ X% x& c; a3 W  G
4 I6 B( n2 `& q* I6 g) I* n
为了方便代码的维护,我将SlidingMenu 自定义成了一个DrawerView类,这样的话所有菜单中的操作就和主界面分离开来,代码就不会显得那么臃肿了,并且在用到的地址直接实例化这个类就可以了。
& q3 S( `' `. X5 F8 y6 F/ \( t
. N1 X: v) w; @0 p& \2 e. z
代码如下:
# X. c% g; `# t: {$ p9 I7 W; Z
  1. public class DrawerView implements OnClickListener{9 N9 ~# z+ w8 c4 c# T
  2.         private final Activity activity;
    ( j7 A$ v4 |2 j/ R
  3.         SlidingMenu localSlidingMenu;
    & {3 p2 G0 Q/ F
  4.         private SwitchButton night_mode_btn;
    1 Q* e2 E( l8 Y
  5.         private TextView night_mode_text;- M  g# F( q0 G$ s: w
  6.         private RelativeLayout setting_btn;, p: `3 s8 A8 t; G" v4 j. `
  7.         public DrawerView(Activity activity) {$ j5 k6 K  q8 s3 _8 H( c; p$ O
  8.                 this.activity = activity;
    + v% M$ k, D1 u' Z$ u( A
  9.         }
    1 _& w/ s7 u2 ]/ N* t
  10. $ J& Y6 H  {9 E3 {  @- _- e
  11.         public SlidingMenu initSlidingMenu() {
    / H9 b9 l. E3 d2 Z$ R. i$ D- a
  12.                 localSlidingMenu = new SlidingMenu(activity);; T9 o  [( ]9 v6 B6 `/ `2 d( z
  13.                 localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单# U6 u+ H- T0 l! B1 L  G8 B
  14.                 localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围! ~6 m% C& P1 M: U
  15. //                localSlidingMenu.setTouchModeBehind(SlidingMenu.RIGHT);
    ; V. J  ]& D! m/ m8 m" p( i
  16.                 localSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);//设置阴影图片的宽度
    & C) J* v+ C4 f, _/ C
  17.                 localSlidingMenu.setShadowDrawable(R.drawable.shadow);//设置阴影图片
    4 }0 F, I! X) j8 s+ i7 j1 S" b* m+ |
  18.                 localSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);//SlidingMenu划出时主页面显示的剩余宽度
    0 x" A  C/ w. @, E
  19.                 localSlidingMenu.setFadeDegree(0.35F);//SlidingMenu滑动时的渐变程度' ]6 |& L- z6 C+ K6 y0 U. `2 D
  20.                 localSlidingMenu.attachToActivity(activity, SlidingMenu.RIGHT);//使SlidingMenu附加在Activity右边
      C# A6 \9 X" [
  21. //                localSlidingMenu.setBehindWidthRes(R.dimen.left_drawer_avatar_size);//设置SlidingMenu菜单的宽度
    4 q  j9 E' I$ q
  22.                 localSlidingMenu.setMenu(R.layout.left_drawer_fragment);//设置menu的布局文件
    , w1 t6 @- V3 o$ E
  23. //                localSlidingMenu.toggle();//动态判断自动关闭或开启SlidingMenu7 v* e$ x4 \" p( g% }
  24.                 localSlidingMenu.setSecondaryMenu(R.layout.profile_drawer_right);; o1 t+ n  }% n  Q& Q9 Y
  25.                 localSlidingMenu.setSecondaryShadowDrawable(R.drawable.shadowright);! E: s' r8 o4 t7 p0 b- N
  26.                 localSlidingMenu.setOnOpenedListener(new SlidingMenu.OnOpenedListener() {# R" n, M; X6 `7 ~
  27.                                         public void onOpened() {
    % V/ L+ g& ^4 v3 f" a2 v1 t
  28.                                                
    " u: j7 T6 K7 s5 P" @1 |
  29.                                         }$ z% P3 Q) p6 x$ V
  30.                                 });
    & W1 g( U7 @& H* F9 J- A/ P4 |
  31.                 8 s. L: u4 R. y  V5 \; c
  32.                 initView();
    4 ^5 O6 }7 R2 `6 Q' \6 T1 G
  33.                 return localSlidingMenu;
    0 |* _5 m5 |. _/ K9 m. a  o& o% U5 d1 e
  34.         }
    & E0 Y; M0 m3 C2 n9 a5 B+ T
  35.   X, E( F. L. @  C
  36.         private void initView() {
    9 V7 u2 }3 C/ b! e8 b& z
  37.                 night_mode_btn = (SwitchButton)localSlidingMenu.findViewById(R.id.night_mode_btn);
    * W( V# f4 n! F4 C8 `+ p
  38.                 night_mode_text = (TextView)localSlidingMenu.findViewById(R.id.night_mode_text);) n0 o; L: O. `! f2 I7 b" t% q
  39.                 night_mode_btn.setOnCheckedChangeListener(new OnCheckedChangeListener() {8 O' c9 _" ]$ g9 n8 \1 u; [7 b
  40.                         + A7 A: X' A) \2 D
  41.                         @Override2 n3 s+ i# d: u7 x) }
  42.                         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    + j; F: t$ c' }7 X. L
  43.                                 // TODO Auto-generated method stub
    7 [+ r! u: L0 H# W9 S
  44.                                 if(isChecked){
    * b/ h* h/ E9 }
  45.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));4 W& ?. V3 l" |1 |+ _
  46.                                 }else{; V4 A" H. n! e0 K" h" f3 B- S
  47.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));6 K' T  ~5 @" ~( M/ `0 f7 `
  48.                                 }
    8 a1 d( B* x5 m, Y( i3 Z6 a
  49.                         }
    - ]  b- X$ w8 T) \
  50.                 });
    " _& U, W4 g+ G4 }4 O
  51.                 night_mode_btn.setChecked(false);
    # ~2 Z& b! D1 I) ^* R7 [# H. }, B
  52.                 if(night_mode_btn.isChecked()){
    9 S6 w' O3 |- v0 c: F
  53.                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));4 l7 M- p1 g7 Z" N
  54.                 }else{
      S% m5 I" u5 x8 [- P* Y3 y
  55.                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));1 x7 W4 G$ V( S# y7 Y* N
  56.                 }) n3 F& N: e" ?( Z6 ?, X( h
  57.                 $ M7 Z, r5 B! z
  58.                 setting_btn =(RelativeLayout)localSlidingMenu.findViewById(R.id.setting_btn);
    ( C! j+ Y9 m5 B% i" Q) k
  59.                 setting_btn.setOnClickListener(this);# K& i0 X, p# p+ B5 n
  60.         }
      t+ h+ a) h4 L) e6 I
  61. 0 K1 z- t' ?9 Q- b2 e6 O; `: j
  62.         @Override6 W+ F- s* o2 E! n$ ?- K
  63.         public void onClick(View v) {7 P5 G/ b- v, \+ V& _- Q' l5 X; [
  64.                 switch (v.getId()) {
    , J* M1 `" q  _" b  |
  65.                 case R.id.setting_btn:1 F2 u4 G& l* t
  66.                         activity.startActivity(new Intent(activity,SettingsActivity.class));! O/ z# Q$ W( B. B5 N+ `9 u" n
  67.                         activity.overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    / u% i2 N6 F2 k/ _
  68.                         break;: ^, c, @. {- S2 _# a" }: H: d1 q6 f) z
  69. 9 Q% q9 C7 D% G; W
  70.                 default:0 h+ |. A8 P8 N4 o4 @  B2 l
  71.                         break;
    , O, Z, t' v7 M$ L+ I
  72.                 }
    7 Q, O8 b4 o+ f* b; @, a0 U5 W; r
  73.         }
    2 k. e+ H0 O- h+ A" w0 N9 Y
  74. }
复制代码

; ~$ d& L! K% @' G* ?2.通过重写CompoundButton实现--昼夜模式切换按钮效果看源码发现,今日头条的中的是SwitchButton昼夜模式选择控件,是通过重写CompoundButton实现的,以下便是实现对应方法的相关帖子:
2 r5 Q* g+ r) VSwitchButton 开关按钮 的多种实现方式 (附源码DEMO)
0 U8 e+ h' @1 \( L: h& n4 H
6 E  t  u, G8 @  Z
根据需求选择你喜欢的SwitchButton去修改和使用。! ^3 x7 o' J: q' k, G% d0 h
3 \+ j" f+ r# h* O1 ^/ s
: Q. H- e, q% |) `. N8 u* Q
3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。在开发中发现,每个新闻列表中对应的ITEM中按钮出发的弹框效果其实是通过PopupWindow实现的,相关博文如下/ j# X* p" H. l/ R% g. b2 h
android 仿 新闻阅读器 菜单弹出效果(附源码DEMO)
) O$ }6 o! N3 K) o) f) ]& `( X2 ]; x$ E  _, x

- H# f0 Q+ ?; L' R4.通过universal-image-loader库实现了图片的加载和缓存。一个新闻客户端肯定涉及到一个图片异步加载缓存的方法,所以这里使用开发中最常用的开源库之一universal-image-loader来实现相关的需求。如果你对这个库的使用不是很了解的话,可以查看以下链接:Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)% z9 ~' W, T$ f) w! j: D

/ T3 k& j0 M- T" l8 y: H/ {, ^5 L5 N" D
---根据你需求配置相应的属性即可。
  }5 Y8 v  }! N) `' n

2 W: A5 O6 {0 G. j% n% z! w/ d* b- [+ J! I' l" V
6 _6 u* o$ T: r' R
5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。新闻客户端中,每个新闻的布局都可能不一样,所以你可以选择2种最常用的方式去根据需求改变一个LISTVIEW里面的布局。
  Y7 L" u  Y7 P; g1.操作上会麻烦一点,维护起来很方便
6 F+ C* }1 |3 W7 P6 k% L3 Y3 O/ b4 [BaseAdapter中有个自带的方法,就是getViewTypeCount() ,用户可以使用这个方法获取ITEM的种类数量,之后在getView()的时候判断对应到额种类type之后设给他对应的布局即可,常见的例子有:唱吧中的名人榜. J$ [8 n! x% P5 G/ {
9 m1 ?) O- s8 \) z+ N& Q  u
; Q( l( u/ \1 P$ @9 x! ~

( P2 p0 g; `! Y! K$ C. W2.操作上很方便,只用根据需求设置布局的显示隐藏效果,缺点:如果控件一多,不在注释的话,可能维护起来就很麻烦。
" g5 I6 Q+ ]1 c  B0 `6 y0 C2 P; g6 {3 U) y, X: e
6 s; {3 N0 z# o5 ^; R/ a: l
在这里,其实采用第2种方式就可以了,分析它对应的以下布大致的5种局:# c  s3 i, a" o5 ^+ X

+ V/ J4 a/ r$ [. T& T! u6 q 20140416213230703.jpg
" \: \6 m. h) d, t; y布局---1 ' T; J* I) D: @/ v
20140416213240781.jpg
) ~3 ^* d+ m. S# P4 h) e6 M( K! s/ E
1 j) D( q& t: j3 k" D* m5 y. P
布局---2; ]$ a7 e% ?. U
20140416213247031.jpg $ w9 ^+ G5 ~, t( ?. h
布局---3
$ T1 }& F+ l4 q9 Z/ g# t8 U+ N 20140416213235765.jpg ) Y3 u4 M# n$ \! x! v& V' Z( H
布局---4
. u) D# l- f4 G1 P 20140416213251921.jpg * W2 U! o. h' S( p
布局---5
* w0 F8 o. N: {2 h
; k: \& _  L# g% z! R5 p5 D: F. F6 |# I7 r) p3 U
/ q( O; _  U; y# j
. D) A* q, `' t5 o, j
6 @' W7 Q' m- q) O$ v+ S
6 C5 ]8 _7 S2 W9 Y0 \5 a8 H) I$ E
 楼主| 发表于 2014-8-7 17:44 | 显示全部楼层
分析上述布局,其实他们的大体布局是一样的,知识根据图片的张数,图片的大小,以及新闻类别的评论来判断布局的对应显示隐藏问题,只要根据相对应的属性便可以很好的实现这个效果,所以方法2是最合适的。
% @, e! ~* c4 E2 T0 \: f! I" k7 j7 q5 K* |( E8 K! D
* v( J! R* }0 z/ K6 f

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

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

, r- y# \1 U4 }' s0 b  m# p# D
开发中的几个问题总结:" ~6 t! O9 ?9 `+ Y
关于Fragment
/ O/ v" q) I1 t1)
# Q  j. x  D( a4 b( u. [
  1. @Override, Q7 t- Y! B4 z/ y
  2. public void onAttach(Activity activity) {5 c4 U0 h) M9 _6 H% Z! p
  3. // TODO Auto-generated method stub
    ) W, a' `; c- x7 F) l
  4. this.activity = activity;
    6 y6 ?: r* }8 h4 w2 H% ?
  5. super.onAttach(activity);
    " D1 G) w' [5 q3 Y0 S
  6. }
复制代码
% f; T# D7 F& R  U. z
通过onAttach()方法获取父类ACTIVITY,如果在调用getActivity()方法获取的话,可能会在FRAGMENT被回收后报空指针错误。
0 T  ?! c$ _, T9 o6 C$ F' f& K: x2)
' D" r$ w# D( T1 M看今日头条的效果是,没切换至类型界面后,才去刷新数据,Fragment自带了这个方法,如下
3 c( v* Y5 P# e
  1. public void setUserVisibleHint(boolean isVisibleToUser) {}
复制代码
你可以判断isVisibleToUser属性来知道是否是切换的界面已经完全可见,之后进行操作。代码中也注释了。
( _. `2 f% |2 c4 {+ n# n

下面看看这次的效果图:

20140417195420578.png 20140417195257890.png

20140417195320234.png 20140417195325640.png

! f( E* H6 h: w8 g; b9 j

最后附上源码DEMO地址:

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

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


5 A' U& k1 S$ A. w: z) m; ]' b7 u# k" }1 T% F8 c" C* h2 T8 Z
1 @$ i+ A5 e, x7 u
发表于 2014-9-5 10:30 | 显示全部楼层
谢谢分享     $ v5 c' T1 b- j$ v% q( `
发表于 2014-10-27 22:04 | 显示全部楼层
继续支持楼主
发表于 2014-11-3 09:20 | 显示全部楼层
下载看看了下载看看了下载看看了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

© 2001-2014Comsenz Inc.

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