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

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

[复制链接]
发表于 2014-8-7 17:41 | 显示全部楼层 |阅读模式
上次,已经完成了头部新闻分类栏目的拖动效果。
; }* d3 q5 E  r: i这篇文章是继续去完善APP 今日头条  这个新闻阅读器的其他功能。
- c. Q) u! G% @6 d& P这次所实现的功能清单:3 \7 k& `/ ^2 P9 N$ H
1.通过SlidingMenu实现左右侧拉菜单效果3 L2 E( Q( U( C( U# H, t
2.通过重写CompoundButton实现--昼夜模式切换按钮效果。' d! P9 Y; O) I: p. v. N0 L
3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。
2 I* S# I. D8 m2 c3 n4.通过universal-image-loader库实现了图片的加载和缓存。
9 c+ }  o$ z- O4 q5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。0 f; c1 l! g. k
+ F! q! l9 v2 R
- m9 K5 H$ C# Q' q
下面把每个功能和对应的实现方式相关博文都列出了,方便大家根据自身需求查看。$ O, G/ X. u  c5 I. u. c- y
6 |' L* [# M5 o
下面就是官方的效果截图,详细操作请继续向下看。% I% h6 D6 H7 t
20140413211055046.jpg 20140413211118031.jpg
6 |! t. |$ ~' a& E; [9 x# U一.通过SlidingMenu实现左右侧拉菜单效果在第一讲中,我们已经知道了,它使用了SlidingMenu 这个侧拉菜单开源库,所以我特地将 SlidingMenu的使用和配置写了一个文章,文章地址:Android SlidingMenu 开源项目 侧拉菜单的使用(详细配置)+ V+ h( W+ e$ `1 ~& O5 f( h
所以具体的配置就不在这里说明了,大家可以去看上面给的链接。
6 r# d! }2 w8 J* i+ U- {, O$ k" W! S( j* X. A
注意:由于 今日头条用的是左右都可以侧拉的菜单,所以设置侧拉模式为:SlidingMenu.LEFT_RIGHT,代码如下; F6 g' b7 n7 Y& O! P5 ?
  1.     localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单   
复制代码
由于左右可以拖出菜单,并且中间的ViewPager可以切换新闻页面,所以SlidingMenu的必须如此设置以下属性:
( o/ Z& v  V; O5 I% G
  1.     localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围  
复制代码
SLIDEING_WINDOW就是触摸边缘才会出发,这样的话滑动操作就不用冲突了。% k3 X2 ^5 i) A- w- P$ f' ^% B/ X. M
+ b. J$ U& b/ d3 X( V
为了方便代码的维护,我将SlidingMenu 自定义成了一个DrawerView类,这样的话所有菜单中的操作就和主界面分离开来,代码就不会显得那么臃肿了,并且在用到的地址直接实例化这个类就可以了。7 d1 r1 y$ }4 x( q2 J' l& V

3 r' Z7 l* U: e代码如下:
# n- Z2 {8 M! _6 _
  1. public class DrawerView implements OnClickListener{' K$ T5 R! b& [8 U# h; r
  2.         private final Activity activity;
    3 S# i3 M" r7 x. Y
  3.         SlidingMenu localSlidingMenu;
    2 k+ k; r7 u- X! e
  4.         private SwitchButton night_mode_btn;: q: x$ c8 ^( A3 o. f. d) c+ r
  5.         private TextView night_mode_text;" ]7 \' R: n  j/ o& O; N
  6.         private RelativeLayout setting_btn;! u, \& o! `7 X! R
  7.         public DrawerView(Activity activity) {2 Q( A6 C& q9 d" s
  8.                 this.activity = activity;; q) _; B& A) b; [
  9.         }
    , Z1 x7 J- h" P. d! r
  10. 1 \" f; ?. Z# N% g
  11.         public SlidingMenu initSlidingMenu() {
    ) f7 W5 ?6 k6 W
  12.                 localSlidingMenu = new SlidingMenu(activity);( I) L2 s) g' U& P6 i+ I) v
  13.                 localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单# @5 Q! H8 d+ w' t" V
  14.                 localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围5 ]$ n3 G' e/ J8 k: @
  15. //                localSlidingMenu.setTouchModeBehind(SlidingMenu.RIGHT);
    $ O! M5 k9 c: [
  16.                 localSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);//设置阴影图片的宽度" H' y( W+ E+ |# K0 P$ Z1 W4 k  j6 t
  17.                 localSlidingMenu.setShadowDrawable(R.drawable.shadow);//设置阴影图片
    - c: n: y9 D* [/ [5 W$ T, U7 p/ M7 A
  18.                 localSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);//SlidingMenu划出时主页面显示的剩余宽度' N" `; o4 c- @  `, M% r
  19.                 localSlidingMenu.setFadeDegree(0.35F);//SlidingMenu滑动时的渐变程度1 ]  V1 d& r. A4 l0 Z. v' u
  20.                 localSlidingMenu.attachToActivity(activity, SlidingMenu.RIGHT);//使SlidingMenu附加在Activity右边; C" y! h' r* D7 d. ~- q2 z
  21. //                localSlidingMenu.setBehindWidthRes(R.dimen.left_drawer_avatar_size);//设置SlidingMenu菜单的宽度
    2 N( E+ A5 e+ S. I- e7 O6 t
  22.                 localSlidingMenu.setMenu(R.layout.left_drawer_fragment);//设置menu的布局文件+ j7 Y: T* `2 J% Q6 V" e
  23. //                localSlidingMenu.toggle();//动态判断自动关闭或开启SlidingMenu# x7 G- F4 ^5 J: g
  24.                 localSlidingMenu.setSecondaryMenu(R.layout.profile_drawer_right);. C3 K, X7 R2 h0 |. F5 j
  25.                 localSlidingMenu.setSecondaryShadowDrawable(R.drawable.shadowright);* |! E; D4 n# h+ j8 w. P
  26.                 localSlidingMenu.setOnOpenedListener(new SlidingMenu.OnOpenedListener() {8 I6 k) ]9 M: ?5 V: p, U+ ^
  27.                                         public void onOpened() {
      ?9 l& [8 K. q9 ?- D. @: G: E
  28.                                                 # z4 l8 K3 @! _, b
  29.                                         }% w8 c3 W* O! [  W/ x6 ~
  30.                                 });
    % I7 m* @) r# I6 ]5 X, w# O
  31.                
    3 _& y3 c& O2 p  {* _- q& J
  32.                 initView();$ [- {5 }5 W. z1 f8 G0 Y; t
  33.                 return localSlidingMenu;
    % C* n6 y9 D* q0 ^
  34.         }/ F3 o: {3 p8 ~0 w6 h, }

  35. + z  ]' g7 R4 v/ C
  36.         private void initView() {
    . Z+ j3 i0 T& O, |2 `6 n; B
  37.                 night_mode_btn = (SwitchButton)localSlidingMenu.findViewById(R.id.night_mode_btn);
    , e1 p2 f: G8 M4 b  Z
  38.                 night_mode_text = (TextView)localSlidingMenu.findViewById(R.id.night_mode_text);$ B' H6 Q! Q  }. @- a
  39.                 night_mode_btn.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    5 W. M: d9 }& Y: i2 ]
  40.                        
    - @$ _* l0 z. [; D% K! S& N0 k
  41.                         @Override
    # l8 I) ~- e+ j; r0 N
  42.                         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {% l5 u* P, ^2 D& k  l0 i; j+ L
  43.                                 // TODO Auto-generated method stub
    & N4 m4 V7 i8 S/ Q
  44.                                 if(isChecked){
      g) k2 L% Y* E: ^  }- e
  45.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));- D4 E7 c& J+ R& Z" d" |2 ^* t1 \
  46.                                 }else{
    , i% J, u  v: J  I
  47.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));$ c! o  j# d! p9 g
  48.                                 }
    0 W- P. U: z9 n% X; H! D1 l
  49.                         }
    / S+ [' }$ F9 X) D" o4 O/ S2 Y
  50.                 });- Q0 v- l2 T/ a  o* f
  51.                 night_mode_btn.setChecked(false);  w3 ]- C# F" s! |) P$ J, Y9 l
  52.                 if(night_mode_btn.isChecked()){
    ( S# C( u7 t$ R9 B2 ~5 V5 a  `6 l
  53.                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));
    0 s$ i# A, C* P# k. @
  54.                 }else{& I0 t3 g9 i8 s3 @9 w8 C, b& R
  55.                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));
    0 l* Z3 j, C: y& r7 ~& N
  56.                 }% ~0 a! ^% ~) w% {  |1 j
  57.                
    " b$ t. m/ [! h' i
  58.                 setting_btn =(RelativeLayout)localSlidingMenu.findViewById(R.id.setting_btn);3 p! t. z2 N/ H0 I- M; f+ I$ C, d
  59.                 setting_btn.setOnClickListener(this);3 ?8 c+ S& _/ Q& k
  60.         }
    ) g- T4 f  Q2 C( K9 ^
  61. $ {2 \8 B$ T' ]* B' f
  62.         @Override
    * s4 e2 t- o/ I; U) h. Z, P
  63.         public void onClick(View v) {
    9 T$ Q- a9 R0 r+ R
  64.                 switch (v.getId()) {# y/ \; L# t6 f6 e0 M
  65.                 case R.id.setting_btn:
    8 ^1 J( F, F7 N! T
  66.                         activity.startActivity(new Intent(activity,SettingsActivity.class));
    $ B& e$ O9 A! Q5 p1 D
  67.                         activity.overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    1 _! T; ~: k& b6 ^
  68.                         break;
    8 a5 g7 @. m& d0 Q& s5 i* b
  69. # i) k, p" R0 r; n$ B% c
  70.                 default:- B  ~% b. I0 n" r  V1 l; W
  71.                         break;- L5 @+ s) k6 K
  72.                 }7 h9 c8 }2 _" j% I. B2 e$ v1 a& t
  73.         }
    ' I% z  m, K8 N8 o
  74. }
复制代码

  O5 L) r# a3 x2.通过重写CompoundButton实现--昼夜模式切换按钮效果看源码发现,今日头条的中的是SwitchButton昼夜模式选择控件,是通过重写CompoundButton实现的,以下便是实现对应方法的相关帖子:# V& B  I2 ]; E2 E6 L8 b
SwitchButton 开关按钮 的多种实现方式 (附源码DEMO)
" K/ @8 L4 C0 n5 S7 }( {& H3 U

& X8 Q9 }: _) k* ?根据需求选择你喜欢的SwitchButton去修改和使用。
0 t/ m* s( U  _% p
7 f) E6 @2 k/ S
) e! A+ D' d, ?* {. ^0 Z! K+ V
3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。在开发中发现,每个新闻列表中对应的ITEM中按钮出发的弹框效果其实是通过PopupWindow实现的,相关博文如下
/ `; ^' K1 |  `# Dandroid 仿 新闻阅读器 菜单弹出效果(附源码DEMO)7 Y0 i0 |  g9 z* K, }3 H( v

! G) m1 j3 y. I- Y' _
: S; _, H+ G. \2 D
4.通过universal-image-loader库实现了图片的加载和缓存。一个新闻客户端肯定涉及到一个图片异步加载缓存的方法,所以这里使用开发中最常用的开源库之一universal-image-loader来实现相关的需求。如果你对这个库的使用不是很了解的话,可以查看以下链接:Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)/ Z) r5 J6 U5 C' p1 D

! u8 U# G/ U  `# B$ q. e
---根据你需求配置相应的属性即可。
. x+ W9 M' a. c, m* L

0 C" ?1 h1 R# j# s3 Q
8 B; _7 y, X9 e# \+ ?* T

! l8 G* ~- E2 _8 b" d$ M7 u5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。新闻客户端中,每个新闻的布局都可能不一样,所以你可以选择2种最常用的方式去根据需求改变一个LISTVIEW里面的布局。- g8 C  N7 F9 S! S" Y
1.操作上会麻烦一点,维护起来很方便
& \+ y- D% D" Y4 S9 a* E) [0 ZBaseAdapter中有个自带的方法,就是getViewTypeCount() ,用户可以使用这个方法获取ITEM的种类数量,之后在getView()的时候判断对应到额种类type之后设给他对应的布局即可,常见的例子有:唱吧中的名人榜! g5 t- M3 U2 `. e' O% @

" @, |; U! S+ g4 d# q. p& E. f2 G+ e) B! O; c  J
4 t% \/ c0 N! Z: x5 o
2.操作上很方便,只用根据需求设置布局的显示隐藏效果,缺点:如果控件一多,不在注释的话,可能维护起来就很麻烦。% P6 l$ g) L% p6 K( `. i
. Z! p4 f- A! w, I
9 U6 Q) z9 A; @' |" N3 `( e% @
在这里,其实采用第2种方式就可以了,分析它对应的以下布大致的5种局:: E1 c; y2 x( O8 [/ {. m

# I! z. Q, ~8 Z9 t& n) e8 M0 H 20140416213230703.jpg
* b1 K2 |) D0 w布局---1 ) |1 U' L1 ?; S9 N$ R% s2 Q
20140416213240781.jpg ; ^2 _( x8 q- _/ v, W& [2 o$ p3 _

9 [( e2 ], y, b+ C布局---2. O  P, u' F; O6 e* F5 U3 d- W1 n' H
20140416213247031.jpg
  @9 t. K8 G: ^& X9 w: o
布局---3
) B4 i8 Q& u7 ~4 z  \ 20140416213235765.jpg ! Q  ~, I; Q7 s2 \: t/ [
布局---4
9 m* @6 `* o6 j; a1 z 20140416213251921.jpg % Z9 b/ l+ z4 E2 L+ c( ~! _: h% z
布局---5
: H- X: r: r. X& Y' |. t4 a
( s" T6 {7 s% {+ s9 P9 k+ y  _. ]4 x1 O4 F

6 K# }$ d, ^: V1 u3 `6 L' X
/ h2 U$ U) \9 d8 r- ~, k
& k$ M# ]9 z  r* R% m# a' d
8 A% o! G% u& R5 j/ t
 楼主| 发表于 2014-8-7 17:44 | 显示全部楼层
分析上述布局,其实他们的大体布局是一样的,知识根据图片的张数,图片的大小,以及新闻类别的评论来判断布局的对应显示隐藏问题,只要根据相对应的属性便可以很好的实现这个效果,所以方法2是最合适的。
) A) X, _1 R- |/ H. O* C1 S% R8 Y( [5 c8 {

, ~5 b5 S/ N7 {& Q8 M4 w

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

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

; O7 ?/ u9 W. t8 P0 h
开发中的几个问题总结:4 L$ x, K4 ]8 p2 w
关于Fragment
& j) u. K' f7 o# n4 S1)- @5 X% e5 p- g1 Y* n( d
  1. @Override
    9 e1 y1 }6 h6 v- J& P8 E  J2 }
  2. public void onAttach(Activity activity) {
    - f4 c9 w, B% W5 F9 I
  3. // TODO Auto-generated method stub
    ; R! B0 _; @# X% s" A
  4. this.activity = activity;
    3 X4 _3 w$ P4 E! M
  5. super.onAttach(activity);
    " L9 F: C/ Z9 b; z7 Q0 Q
  6. }
复制代码

, p/ `4 a. c( T通过onAttach()方法获取父类ACTIVITY,如果在调用getActivity()方法获取的话,可能会在FRAGMENT被回收后报空指针错误。( E, m" b2 [( w* v; z8 b
2)
  L) l$ ^* Q( w6 A5 L" O看今日头条的效果是,没切换至类型界面后,才去刷新数据,Fragment自带了这个方法,如下5 r+ Z5 @' B) `" |
  1. public void setUserVisibleHint(boolean isVisibleToUser) {}
复制代码
你可以判断isVisibleToUser属性来知道是否是切换的界面已经完全可见,之后进行操作。代码中也注释了。, {' e' n4 V4 U

下面看看这次的效果图:

20140417195420578.png 20140417195257890.png

20140417195320234.png 20140417195325640.png


. v  f7 u" P" {1 ]% K, f+ O! ]

最后附上源码DEMO地址:

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

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

( G% T" Q1 v0 [( y! W/ V

8 N- {% M, p, c3 H+ _- P" i+ U5 D# S. x& Z6 ]; v9 _  O
发表于 2014-9-5 10:30 | 显示全部楼层
谢谢分享     6 |( N8 X! ^/ f2 Z9 Q0 t( F
发表于 2014-10-27 22:04 | 显示全部楼层
继续支持楼主
发表于 2014-11-3 09:20 | 显示全部楼层
下载看看了下载看看了下载看看了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

© 2001-2014Comsenz Inc.

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