[Android]使用RecyclerView替代ListView(三)


 

这次来使用RecyclerView实现PinnedListView的效果,效果很常见:

开发的代码建立在上一篇([Android]使用RecyclerView替代ListView(二)http://www.cnblogs.com/tiantianbyconan/p/4242541.html)基础之上。

修改布局如下:


 1 <?xml version=”1.0” encoding=”utf-8”?>
2
3 <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android
4 android:orientation=”vertical”
5 android:layout_width=”match_parent”
6 android:layout_height=”match_parent”>
7
8 <android.support.v7.widget.Toolbar
9 android:id=”@+id/recycler_view_pinned_toolbar”
10 android:layout_height=”wrap_content”
11 android:layout_width=”match_parent”
12 android:background=”?attr/colorPrimary”
13 />
14 <android.support.v4.widget.SwipeRefreshLayout
15 android:id=”@+id/recycler_view_pinned_srl”
16 android:layout_width=”match_parent”
17 android:layout_height=”wrap_content”
18 >
19
20 <com.wangjie.androidbucket.support.recyclerview.pinnedlayout.PinnedRecyclerViewLayout
21 android:id=”@+id/recycler_view_pinned_layout”
22 android:layout_width=”match_parent” android:layout_height=”match_parent”>
23 <android.support.v7.widget.RecyclerView
24 android:id=”@+id/recycler_view_pinned_rv”
25 android:scrollbars=”vertical”
26 android:layout_width=”match_parent”
27 android:layout_height=”match_parent”
28 android:background=”#bbccaa”
29 />
30 <Button
31 android:id=”@+id/recycler_view_pinned_add_btn”
32 android:layout_width=”wrap_content” android:layout_height=”wrap_content”
33 android:layout_centerVertical=”true”
34 android:background=”#abcabc”
35 android:text=”add”
36 />
37
38 </com.wangjie.androidbucket.support.recyclerview.pinnedlayout.PinnedRecyclerViewLayout>
39
40 </android.support.v4.widget.SwipeRefreshLayout>
41
42 </LinearLayout>

可以看到RecyclerView是被一个PinnedRecyclerViewLayouthttps://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/pinnedlayout/PinnedRecyclerViewLayout.java包含在里面的。这个在项目AndroidBuckethttps://github.com/wangjiegulu/AndroidBucket中。先看看代码中怎么使用吧,具体实现待会说。


1 pinnedLayout.initRecyclerPinned(recyclerView, layoutManager, LayoutInflater.from(context).inflate(R.layout.recycler_view_item_float, null));
2 pinnedLayout.setOnRecyclerViewPinnedViewListener(this);

如上,使用方式很简单:

Line1:初始化绑定PinnedRecyclerViewLayout和RecyclerView,并设置需要被顶上去的pinnedView

Line2:设置OnRecyclerViewPinnedViewListener,作用是在顶部被顶上去替换掉的时候,会回调重新渲染数据,传入的OnRecyclerViewPinnedViewListener是this,显然,此Activity实现了这个接口,实现代码如下:


 1 // 渲染pinnedView数据
2 @Override
3 public void onPinnedViewRender(PinnedRecyclerViewLayout pinnedRecyclerViewLayout, View pinnedView, int position) {
4 switch (pinnedRecyclerViewLayout.getId()) {
5 case R.id.recycler_view_pinned_layout:
6 TextView nameTv = (TextView) pinnedView.findViewById(R.id.recycler_view_item_float_name_tv);
7 nameTv.setText(personList.get(position).getName());
8 TextView ageTv = (TextView) pinnedView.findViewById(R.id.recycler_view_item_float_age_tv);
9 ageTv.setText(personList.get(position).getAge() + “岁”);
10 break;
11 }
12 }

然后,我们来看看PinnedRecyclerViewLayout是怎么实现的。


  1 /*
2 Author: wangjie
3 Email: tiantian.china.2@gmail.com
4 Date: 2/2/15.
5 */
6 public class PinnedRecyclerViewLayout extends RelativeLayout {
7
8 private static final String TAG = PinnedRecyclerViewLayout.class.getSimpleName();
9
10 public static interface OnRecyclerViewPinnedViewListener {
11 void onPinnedViewRender(PinnedRecyclerViewLayout pinnedRecyclerViewLayout, View pinnedView, int position);
12 }
13
14 private OnRecyclerViewPinnedViewListener onRecyclerViewPinnedViewListener;
15
16 public void setOnRecyclerViewPinnedViewListener(OnRecyclerViewPinnedViewListener onRecyclerViewPinnedViewListener) {
17 this.onRecyclerViewPinnedViewListener = onRecyclerViewPinnedViewListener;
18 }
19
20 public PinnedRecyclerViewLayout(Context context) {
21 super(context);
22 init(context);
23 }
24
25 public PinnedRecyclerViewLayout(Context context, AttributeSet attrs) {
26 super(context, attrs);
27 init(context);
28 }
29
30 public PinnedRecyclerViewLayout(Context context, AttributeSet attrs, int defStyleAttr) {
31 super(context, attrs, defStyleAttr);
32 init(context);
33 }
34
35 private void init(Context context) {
36 }
37
38 private View pinnedView;
39 private ABaseLinearLayoutManager layoutManager;
40
41 public void initRecyclerPinned(RecyclerView recyclerView, ABaseLinearLayoutManager layoutManager, View pinnedView) {
42 this.pinnedView = pinnedView;
43 this.layoutManager = layoutManager;
44 this.addView(this.pinnedView);
45 RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
46 this.pinnedView.setLayoutParams(lp);
47 layoutManager.getRecyclerViewScrollManager().addScrollListener(recyclerView, new OnRecyclerViewScrollListener() {
48 @Override
49 public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
50 }
51
52 @Override
53 public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
54 refreshPinnedView();
55 }
56 });
57 pinnedView.setVisibility(GONE);
58 }
59
60 // 保存上次的position
61 private int lastPosition = RecyclerView.NO_POSITION;
62
63 public void refreshPinnedView() {
64 if (null == pinnedView || null == layoutManager) {
65 Logger.e(TAG, “Please init pinnedView and layoutManager with initRecyclerPinned method first!”);
66 return;
67 }
68 if (VISIBLE != pinnedView.getVisibility()) {
69 pinnedView.setVisibility(VISIBLE);
70 }
71 int curPosition = layoutManager.findFirstVisibleItemPosition();
72 if (RecyclerView.NO_POSITION == curPosition) {
73 return;
74 }
75 View curItemView = layoutManager.findViewByPosition(curPosition);
76 if (null == curItemView) {
77 return;
78 }
79 // 如果当前的curPosition和上次的lastPosition不一样,则说明需要重新刷新数据,避免curPosition一样的情况下重复刷新相同数据
80 if (curPosition != lastPosition) {
81 if (null != onRecyclerViewPinnedViewListener) {
82 onRecyclerViewPinnedViewListener.onPinnedViewRender(this, pinnedView, curPosition);
83 }
84 lastPosition = curPosition;
85 }
86
87 int displayTop;
88 int itemHeight = curItemView.getHeight();
89 int curTop = curItemView.getTop();
90 int floatHeight = pinnedView.getHeight();
91 if (curTop < floatHeight - itemHeight) {
92 displayTop = itemHeight + curTop - floatHeight;
93 } else {
94 displayTop = 0;
95 }
96 RelativeLayout.LayoutParams lp = (LayoutParams) pinnedView.getLayoutParams();
97 lp.topMargin = displayTop;
98 pinnedView.setLayoutParams(lp);
99 pinnedView.invalidate();
100 }
101
102
103 }

 

这个PinnedRecyclerViewLayout 是继承RelativeLayout的,因为我们需要在里面添加一个被顶上去的pinnedView,需要覆盖在RecyclerView上面。

Line44:把传进来的pinnedView增加到PinnedRecyclerViewLayout 里面

Line47~56:在ABaseLinearLayoutManager中增加一个滚动的监听器,因为我们需要在滚动的时候动态的改变pinnedView的位置,这样才能模拟顶上去的效果。并滚动时调用refreshPinnedView来刷新pinnedView的位置。

Line57:因为在调用initRecyclerPinned方法时,RecyclerView可能还没有数据源,所以不需要显示这个pinnedView,等到真正滚动的时候再显示就可以了。

refreshPinnedView()方法的作用是在滚动的同时用来刷新pinnedView的位置和显示的数据:

Line71~78:通过layoutManager获取当前第一个显示的数据position,然后根据position获取当前第一个显示的View。

Line79~85:如果当前的curPosition和上次的lastPosition不一样,则说明需要重新刷新数据,避免curPosition一样的情况下重复刷新相同数据。

Line87~95:根据当前第一个显示的View,根据它的top、它的高度和pinnedView的高度计算出pinnedView需要往上移动的距离(画个几何图一目了然了)。

Line96~99:刷新pinnedView的位置

 

示例代码:

https://github.com/wangjiegulu/RecyclerViewSample

 

[Android]使用RecyclerView替代ListView(一)

http://www.cnblogs.com/tiantianbyconan/p/4232560.html

 

[Android]使用RecyclerView替代ListView(二) 

http://www.cnblogs.com/tiantianbyconan/p/4242541.html



来源博客:Wang Jie's Blog
本文链接:https://blog.wangjiegulu.com/2015/02/02/Android-使用RecyclerView替代ListView(三)/
版权声明:本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处。