以下内容为原创,转载请注明:
来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4268097.html
这次来使用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是被一个PinnedRecyclerViewLayout(https://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/support/recyclerview/pinnedlayout/PinnedRecyclerViewLayout.java)包含在里面的。这个在项目AndroidBucket(https://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