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

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

[复制链接]
发表于 2014-8-7 17:41 | 显示全部楼层 |阅读模式
上次,已经完成了头部新闻分类栏目的拖动效果。& S1 m3 d) Y& l: E; d5 j
这篇文章是继续去完善APP 今日头条  这个新闻阅读器的其他功能。
( O8 I8 I: q( d这次所实现的功能清单:5 f+ T0 a0 c$ F& H
1.通过SlidingMenu实现左右侧拉菜单效果9 U9 d1 K2 A$ L3 b' E  b
2.通过重写CompoundButton实现--昼夜模式切换按钮效果。, B6 ?" S# j/ u( J& e+ x8 c" V. o( J
3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。! K9 A+ v5 Y1 h+ o- A2 U: Y
4.通过universal-image-loader库实现了图片的加载和缓存。5 O# |; T3 H5 ?/ I# j3 K
5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。
$ ?; N9 s& s3 L8 m
  I+ b+ j- T* U: R7 d+ v* |# o3 |
& [/ M6 _5 g! V3 R! \/ m( N/ K" Z
下面把每个功能和对应的实现方式相关博文都列出了,方便大家根据自身需求查看。* O( z: a& _" y3 ]! p7 ]

% L2 |0 r4 O8 z( u3 b下面就是官方的效果截图,详细操作请继续向下看。
* o) z6 b1 {9 }3 i9 q2 y 20140413211055046.jpg 20140413211118031.jpg
) `: V/ B0 J& [# o2 i一.通过SlidingMenu实现左右侧拉菜单效果在第一讲中,我们已经知道了,它使用了SlidingMenu 这个侧拉菜单开源库,所以我特地将 SlidingMenu的使用和配置写了一个文章,文章地址:Android SlidingMenu 开源项目 侧拉菜单的使用(详细配置)
0 s7 m/ P% ^: }- c2 [% j所以具体的配置就不在这里说明了,大家可以去看上面给的链接。
5 u: t8 M1 P4 i; d# e2 K6 L2 f0 z  j6 g6 @# w* ^& V0 {" g# ~# ^& F* k' B5 Y- G
注意:由于 今日头条用的是左右都可以侧拉的菜单,所以设置侧拉模式为:SlidingMenu.LEFT_RIGHT,代码如下6 v% D2 M$ G2 ]: c" _
  1.     localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单   
复制代码
由于左右可以拖出菜单,并且中间的ViewPager可以切换新闻页面,所以SlidingMenu的必须如此设置以下属性:
3 V4 R. L- H; V! m
  1.     localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围  
复制代码
SLIDEING_WINDOW就是触摸边缘才会出发,这样的话滑动操作就不用冲突了。
" O- G$ f7 Y5 p' L4 m
: \1 u0 E% w1 f& S5 r! U
为了方便代码的维护,我将SlidingMenu 自定义成了一个DrawerView类,这样的话所有菜单中的操作就和主界面分离开来,代码就不会显得那么臃肿了,并且在用到的地址直接实例化这个类就可以了。
2 |: z& L+ Z# G3 J4 H- H

- x6 ?8 g! k* @, r/ R0 l/ i1 J代码如下:
) R# I5 ]5 c. P& l6 ]7 N7 |
  1. public class DrawerView implements OnClickListener{* n1 Q; d, R- P# Y* t
  2.         private final Activity activity;9 u" c$ `0 J* v$ G! W  o
  3.         SlidingMenu localSlidingMenu;2 m5 ^- _. |9 B) \1 T3 f& i
  4.         private SwitchButton night_mode_btn;& }# Q' V5 H4 v! b7 e
  5.         private TextView night_mode_text;. \+ o( x7 l" j. N
  6.         private RelativeLayout setting_btn;; i7 d9 G& b& z1 u7 l
  7.         public DrawerView(Activity activity) {
    - v, E6 E3 T9 p' ~% r( d8 f
  8.                 this.activity = activity;1 f# e" F+ l4 \' h+ E2 x
  9.         }# x( R8 b. g4 \# a, Y& d
  10. ) c* w# W7 o# G
  11.         public SlidingMenu initSlidingMenu() {
    3 V" H4 Z/ q1 Z, p
  12.                 localSlidingMenu = new SlidingMenu(activity);
    0 @( X0 G# o- M$ M# [6 F" K
  13.                 localSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT);//设置左右滑菜单
    6 v5 c" A2 X: q
  14.                 localSlidingMenu.setTouchModeAbove(SlidingMenu.SLIDING_WINDOW);//设置要使菜单滑动,触碰屏幕的范围  L8 j- U. x: o. W
  15. //                localSlidingMenu.setTouchModeBehind(SlidingMenu.RIGHT);
    7 Y0 a% o6 J$ E7 W/ T3 P. A" }
  16.                 localSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);//设置阴影图片的宽度6 Y4 w- T9 B' M6 v; a
  17.                 localSlidingMenu.setShadowDrawable(R.drawable.shadow);//设置阴影图片" X3 V5 n3 x# F7 L
  18.                 localSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);//SlidingMenu划出时主页面显示的剩余宽度
    1 p8 N9 s* S: U3 j2 h& P  k) i+ `
  19.                 localSlidingMenu.setFadeDegree(0.35F);//SlidingMenu滑动时的渐变程度
    7 E( V* N3 e/ x9 D0 y0 o0 H
  20.                 localSlidingMenu.attachToActivity(activity, SlidingMenu.RIGHT);//使SlidingMenu附加在Activity右边
    ' r; A5 ]' H+ C- d5 Y9 L) B! ?9 n
  21. //                localSlidingMenu.setBehindWidthRes(R.dimen.left_drawer_avatar_size);//设置SlidingMenu菜单的宽度. V7 r8 T0 @$ |# M2 j- _
  22.                 localSlidingMenu.setMenu(R.layout.left_drawer_fragment);//设置menu的布局文件
    / ^7 i5 B9 f( ^* u2 ~9 S
  23. //                localSlidingMenu.toggle();//动态判断自动关闭或开启SlidingMenu
    0 v4 M* J) e; I6 l& b$ [
  24.                 localSlidingMenu.setSecondaryMenu(R.layout.profile_drawer_right);
    # A. P8 x5 o# e1 G8 h# I
  25.                 localSlidingMenu.setSecondaryShadowDrawable(R.drawable.shadowright);
    9 A: P6 D$ t, m1 Q
  26.                 localSlidingMenu.setOnOpenedListener(new SlidingMenu.OnOpenedListener() {2 ]5 T5 |4 I% F2 O: D
  27.                                         public void onOpened() {; g9 a) B2 A4 n! l
  28.                                                 % E- ?/ N' U) e$ ?
  29.                                         }& q* O1 |) S2 m
  30.                                 });% l, y2 i$ C& F' o2 r8 f+ n) |" b' k
  31.                
    1 b9 ]% U$ R7 a4 ?9 F* _3 v
  32.                 initView();2 B3 O. ?' y/ N
  33.                 return localSlidingMenu;) l6 F' t- o! h: g# f/ H9 b: s
  34.         }& x# z! @. J+ t1 y

  35. : e5 v' j: H: ^. ?! D
  36.         private void initView() {
    7 ~) H1 l5 q, J, N. K
  37.                 night_mode_btn = (SwitchButton)localSlidingMenu.findViewById(R.id.night_mode_btn);
    " F$ {# Y! P2 |2 l; C8 B2 I
  38.                 night_mode_text = (TextView)localSlidingMenu.findViewById(R.id.night_mode_text);6 [  @. @' O  a* B& X
  39.                 night_mode_btn.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    5 Y: e! m* W8 \
  40.                        
    * j0 V$ H5 k  O: o3 P" g) {
  41.                         @Override2 b, m: i; Z+ |) y# e0 k$ n
  42.                         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    / ^3 v: Q! D, B1 }% p9 h: f- K' L
  43.                                 // TODO Auto-generated method stub
    ( A, G- }& k1 |. `1 y, ?# T8 }
  44.                                 if(isChecked){
    . B9 W2 D# E7 ?- u' {- W
  45.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));
    ' E4 T" I3 E8 r! e3 z( c' a3 {
  46.                                 }else{6 ^! O" {9 p( `! }( `% J
  47.                                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));; {6 V; o  k/ w. j8 _
  48.                                 }( g6 t4 l$ U$ ?! |( ^2 B: @
  49.                         }
    - J, V/ f2 ~1 P" f7 {7 t
  50.                 });
    4 R$ C' A4 y+ l, S# C/ H
  51.                 night_mode_btn.setChecked(false);9 Y9 T6 Q3 Y7 |$ W3 N; {3 E
  52.                 if(night_mode_btn.isChecked()){2 n) [" y% Q' h( W) e) {3 C
  53.                         night_mode_text.setText(activity.getResources().getString(R.string.action_night_mode));! f9 M3 Y2 |4 k7 g3 G4 J3 d( _  u
  54.                 }else{
    2 H6 Y0 _+ V9 Y5 T* n
  55.                         night_mode_text.setText(activity.getResources().getString(R.string.action_day_mode));
    . h& T6 B4 H( x2 `+ u2 L* s
  56.                 }. ^9 V- C  ^) }8 n$ C3 b- ?3 ^- L
  57.                
    7 y0 ?( S- D2 \! ?, Q" K: t
  58.                 setting_btn =(RelativeLayout)localSlidingMenu.findViewById(R.id.setting_btn);2 v2 R5 U( W) B; p
  59.                 setting_btn.setOnClickListener(this);8 W. F% c1 ]$ c" w- g& @
  60.         }( ~9 t, |8 n/ F4 \1 h# G
  61. " k8 V1 F) D/ X9 C+ Z" \$ {# `
  62.         @Override7 u" H* ?8 w" H8 {- S, H( g/ o& f( U
  63.         public void onClick(View v) {
    3 u# @, X, ?, u8 P% L* Z: E4 ~
  64.                 switch (v.getId()) {
      i* c3 W+ z: h" x8 x4 _
  65.                 case R.id.setting_btn:+ @5 W" f; X* H: R3 h4 y" L* t8 Z: ~
  66.                         activity.startActivity(new Intent(activity,SettingsActivity.class));: U5 j4 k+ X5 m8 B" f# \# j: {
  67.                         activity.overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
    $ u. t5 N) \! L. G& {
  68.                         break;
    + o/ c  C1 G7 G

  69. ' M/ l- G6 u$ {9 O- e8 E  e
  70.                 default:
    & k% m  ?1 ~% o' h( u- ^0 Z
  71.                         break;  X: Q. o" k6 r; S1 M: G4 x
  72.                 }
    ; q0 v+ j2 d: b# B% ]0 f
  73.         }, O0 G/ |0 n# m& H" \1 U+ O. N" {
  74. }
复制代码
, N8 d7 N. C- L- U- M! D! s
2.通过重写CompoundButton实现--昼夜模式切换按钮效果看源码发现,今日头条的中的是SwitchButton昼夜模式选择控件,是通过重写CompoundButton实现的,以下便是实现对应方法的相关帖子:5 u! X( {4 _8 l, D3 ]: L
SwitchButton 开关按钮 的多种实现方式 (附源码DEMO): ?4 ?! `: A* F. R; y+ ?# C; @
' i- o# ^: o) e: Z
根据需求选择你喜欢的SwitchButton去修改和使用。
( z6 e! \0 U& u' A  J' R& Z3 w
$ N" ]1 h8 ]* b2 N( k# A: I

) b0 z4 K" H% S1 F3.通过PopupWindow控件实现了列表中更多菜单弹框操作效果。在开发中发现,每个新闻列表中对应的ITEM中按钮出发的弹框效果其实是通过PopupWindow实现的,相关博文如下
* L0 {0 ~4 q5 M, i2 m( xandroid 仿 新闻阅读器 菜单弹出效果(附源码DEMO)
2 c- J& f. q6 W6 `' k/ J9 Q# l# w4 E/ x' ?( `

  ~) R$ G/ Q) C+ {: y2 V4.通过universal-image-loader库实现了图片的加载和缓存。一个新闻客户端肯定涉及到一个图片异步加载缓存的方法,所以这里使用开发中最常用的开源库之一universal-image-loader来实现相关的需求。如果你对这个库的使用不是很了解的话,可以查看以下链接:Android-Universal-Image-Loader 图片异步加载类库的使用(超详细配置)
- C4 V# I% |8 A, j0 \7 u6 y
; t8 z0 S. e( W, K# M
---根据你需求配置相应的属性即可。
- T( k, S7 o8 [
% P/ O! U# z$ F# o# T4 d

7 ]! \0 I5 R1 I5 h: U- m. A( g
$ ~) Z6 x8 \' n( |1 ?6 m2 t# a
5.通过列表中各个属性的判断,实现了头条新闻列表的相应布局和显示效果。新闻客户端中,每个新闻的布局都可能不一样,所以你可以选择2种最常用的方式去根据需求改变一个LISTVIEW里面的布局。
7 r, L8 D. m; e6 E* Y4 D( g9 Z; z1.操作上会麻烦一点,维护起来很方便3 t  E" G- ~5 N2 H8 |, W0 [- Y) ?* U5 b
BaseAdapter中有个自带的方法,就是getViewTypeCount() ,用户可以使用这个方法获取ITEM的种类数量,之后在getView()的时候判断对应到额种类type之后设给他对应的布局即可,常见的例子有:唱吧中的名人榜, M) x9 N$ G/ w5 p. T
( V) Q5 a: J# `( T

" P- ~5 w/ r; \# j6 Y: J6 d

$ _& h  e1 g: N1 S2.操作上很方便,只用根据需求设置布局的显示隐藏效果,缺点:如果控件一多,不在注释的话,可能维护起来就很麻烦。
4 n5 K' z1 L5 ~$ o# i2 O; S0 M' K0 _' G1 d7 V5 k, f
; P* a4 M: W0 N- ]% k
在这里,其实采用第2种方式就可以了,分析它对应的以下布大致的5种局:
* m7 d8 g8 R5 J4 V6 ~9 A6 Z# L! S7 M, R8 m, y
20140416213230703.jpg
/ q. K! K  b$ I$ H% m  A布局---1 ; p/ o, J5 ?; M2 @# S8 ?' L
20140416213240781.jpg
* p) L/ k' ~" Y. @) ], P

# P  x7 e# ]( s9 n& N布局---2  \6 o8 g/ a# x3 d
20140416213247031.jpg ' R3 M# \% e; x- K
布局---3% ~( `. H; Q0 }5 a" e/ p# F
20140416213235765.jpg ; F7 T" [. C2 N5 M) q7 u
布局---4
( J& S" j7 X+ \* p9 C9 e 20140416213251921.jpg + l1 V8 e) d) z; _. R3 G
布局---55 `! f! F2 X1 u: c- \& j
8 C6 K2 x+ c2 L4 F8 q- U; {

/ ]4 y; g" P* ^$ L/ ~7 s
0 l' W. C! a( v. ~5 n5 R

& u0 e+ n4 B3 E2 }( ]0 T: q9 M) t. t" v/ u+ M

# m% S8 {! y0 B+ G# u4 s+ p
 楼主| 发表于 2014-8-7 17:44 | 显示全部楼层
分析上述布局,其实他们的大体布局是一样的,知识根据图片的张数,图片的大小,以及新闻类别的评论来判断布局的对应显示隐藏问题,只要根据相对应的属性便可以很好的实现这个效果,所以方法2是最合适的。
' A4 v2 ~+ R( }$ {! @4 p. T. ?
6 A. h- J% i: F

- n: \9 |: t* W# V" E- n; m

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

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

3 i- A9 o2 t5 w6 i# H1 }% ^
开发中的几个问题总结:
% d  O7 n) {3 K/ L6 [/ K$ ^8 _关于Fragment. Q6 T0 o8 p. q
1); ?- w! Z+ T: m! m7 w9 p
  1. @Override
    % X! u+ P. f0 x  e
  2. public void onAttach(Activity activity) {: g/ G0 c* z. |/ `5 D
  3. // TODO Auto-generated method stub
    % G5 q! }9 u. Q
  4. this.activity = activity;
    1 u5 n7 F1 S! P" ]; t9 J
  5. super.onAttach(activity);
    ( f; G# ~& A: }1 h3 v% B
  6. }
复制代码

! j; h1 @$ t1 r" N2 t1 F- U通过onAttach()方法获取父类ACTIVITY,如果在调用getActivity()方法获取的话,可能会在FRAGMENT被回收后报空指针错误。
. }& @% Z& [: i1 B. ?& M6 J2)
( R% _: i2 h, _$ f8 f2 P看今日头条的效果是,没切换至类型界面后,才去刷新数据,Fragment自带了这个方法,如下
/ \- ]# @9 n  ?! j3 [+ |
  1. public void setUserVisibleHint(boolean isVisibleToUser) {}
复制代码
你可以判断isVisibleToUser属性来知道是否是切换的界面已经完全可见,之后进行操作。代码中也注释了。/ A0 u2 s, e- j; U! y  D6 h/ X

下面看看这次的效果图:

20140417195420578.png 20140417195257890.png

20140417195320234.png 20140417195325640.png

( q3 L. H- s- n; {+ i% \

最后附上源码DEMO地址:

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

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

) n5 c% j, M6 ~9 ^5 {3 x

  c; d, @; m- o# j& s$ ^6 R
9 f" k- }. l* y' N5 a
发表于 2014-9-5 10:30 | 显示全部楼层
谢谢分享     
" N$ ?8 ?2 `" A/ v( n8 s
发表于 2014-10-27 22:04 | 显示全部楼层
继续支持楼主
发表于 2014-11-3 09:20 | 显示全部楼层
下载看看了下载看看了下载看看了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

© 2001-2014Comsenz Inc.

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