概述
RecyclerView
出来已经很长时间了,关于其的介绍也非常的多.作为ListView
的升级版,它更加强大和灵活.可以轻松的实现各种布局和动画.
见其名,知其意.RecyclerView
主要用于在有限窗口中展示大量数据集合的可复用的视图.
这里主要梳理一下Recyclerview
的常用方法,示例Demo:BoBoMEe/AndroidDev
相关类
RecyclerView
的灵活性,主要体现在各个类的职责分离上,RecyclerView
本身并不负责子View的展示与布局,只负责子view的回收与复用.其他的比如,子View的布局,装饰及动画都交由其他类来处理.
RecyclerView
相关类及其作用
类 | 用途 |
---|---|
RecyclerView.ViewHolder | 装载子view数据的容器 |
RecyclerView.LayoutManager | 布局管理器,负责子View的布局 |
RecyclerView.Adapter | 适配器,负责处理数据与绑定视图 |
RecyclerView.ItemDecoration | 装饰子view,如分割线、偏移等 |
RecyclerView.ItemAnimator | 子view改变时的动画 |
基本使用
RecyclerView
最简单的使用需要设置LayoutManager
和Adapter
,其他的都是可选项.简单使用流程可分为以下几步.
- 引入
compile 'com.android.support:recyclerview-v7:21.3.2'
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/item_margin"
android:clipToPadding="false"/>
- 设置
Adapter
和LayoutManager
RecyclerView mRecyclerView = (RecyclerView)findViewById(R.id.recycler_view);
mLinearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mRecyclerStringAdapter = new RecyclerStringAdapter(Datas.getDatas());
mRecyclerView.setAdapter(mRecyclerStringAdapter);
其中LinearLayoutManager
代表线性显示.默认是方向为VERTICAL
,LayoutManager
抽象类有三个官方实现版本,分别是LinearLayoutManager
(线性布局),GridLayoutManager
(网格布局),StaggeredGridLayoutManager
(瀑布流布局).
Adapter
不同于ListView
的Adapter
,Recyclerview
的适配器强制使用ViewHolder
模式.如下一个简单的Adapter
实现
public class RecyclerStringAdapter extends RecyclerView.Adapter<RecyclerViewholder> {
List<String> datas;
public RecyclerStringAdapter(List<String> datas) {
this.datas = datas;
}
@Override public void onBindViewHolder(RecyclerViewholder holder, int position) {
holder.textView.setText(datas.get(position));
}
@Override public RecyclerViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
View view =
LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);
return new RecyclerViewholder(view);
}
@Override public int getItemCount() {
return datas.size();
}
}
# RecyclerViewholder
public class RecyclerViewholder extends RecyclerView.ViewHolder {
public TextView textView;
public RecyclerViewholder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.text);
}
}
其他用法
LayoutManager
布局切换
我们知道LayoutManager
主要负责子View的布局.通过设置不同的LayoutManager
即可实现展现方式的改变.
如果不设置LayoutManager
,数据将不会展现出来.
//gridview的列数为3,orientation = VERTICAL
mGridLayoutManager = new GridLayoutManager(this, 3);
mRecyclerView.setLayoutManager(mGridLayoutManager);
// 瀑布流的列数为3,orientation = VERTICAL
mStaggeredGridLayoutManager =
new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mStaggeredGridLayoutManager);
int orientation = mLinearLayoutManager.getOrientation();
mLinearLayoutManager.setOrientation(orientation == LinearLayoutManager.VERTICAL ? LinearLayoutManager.HORIZONTAL: LinearLayoutManager.VERTICAL);
LayoutManager
常用方法
//返回当前第一个可见 Item 的 position
findFirstVisibleItemPosition()
//返回当前第一个完全可见 Item 的 position
findFirstCompletelyVisibleItemPosition()
//返回当前最后一个可见 Item 的 position
findLastVisibleItemPosition()
//返回当前最后一个完全可见 Item 的 position.
findLastCompletelyVisibleItemPosition()
ItemAnimator
Item动画
用于设置子view的添加、移动、删除的相关动画效果.官方默认提供了一个DefaultItemAnimator
,如果我们没有设置ItemAnimator
,则默认就是DefaultItemAnimator
,Recyclerview.Adapter
提供了notifyDataSetChanged()
、notifyItemInserted(index)
、 notifyItemRemoved(position)
、 notifyItemChanged(position)
方法来刷新Recyclerview
.这里可以看一下wasabeef/recyclerview-animators:实现了多种Recyclerview
的Item动画,当然我们也可以通过继承DefaultItemAnimator
来实现更加复杂的动画效果.
ItemDecoration
Item装饰
通过mRecyclerView.addItemDecoration(new MarginDecoration(this));
来给Item添加装饰,
ItemDecoration
可以叠加,Recyclerview
展示的时候会遍历所有的ItemDecoration
并调用其绘制方法来对Item进行装饰.
ItemDecoration
提供了三个方法用于定制装饰器:
// 在Item条目绘制之前调用,如果不是指offset则被Item的内容所遮挡
public void onDraw(Canvas c, RecyclerView parent);
// 在Item条目绘制之后调用,浮于Item之上
public void onDrawOver(Canvas c, RecyclerView parent);
// 计算并设置Item偏移量
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent);
Recyclerview多种布局
在ListView
中为了实现多种布局,我们需要重写Adapter
中的 getItemViewType
和getViewTypeCount
方法
而在Recyclerview
中保留了getItemViewType
方法,同时我们可以在onCreateViewHolder
中根据不同的ViewType
来创建不同的Holder
其中添加HeaderView
及FooterwView
的处理,同样也是通过ViewType
来实现的,
接下来我们来看一下带有HeaderView
的 Adapter
的实现方式
public class RecyclerWithHeaderAdapter extends BaseRecyclerAdapter {
private final View header;
private static final int ITEM_VIEW_TYPE_HEADER = 0;
private static final int ITEM_VIEW_TYPE_ITEM = 1;
public boolean isHeader(int position) {
return position == 0;
}
@Override public int getItemViewType(int position) {
return isHeader(position) ? ITEM_VIEW_TYPE_HEADER : ITEM_VIEW_TYPE_ITEM;
}
public RecyclerWithHeaderAdapter(List<String> datas, View header) {
super(datas);
this.header = header;
}
@Override public RecyclerViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_VIEW_TYPE_HEADER) {
return new RecyclerViewholder(header);
}
return super.onCreateViewHolder(parent, viewType);
}
@Override public void onBindViewHolder(RecyclerViewholder holder, int position) {
if (isHeader(position)) return;
holder.textView.setText(datas.get(position - 1));
}
@Override public int getItemCount() {
return datas.size() + 1;
}
}
其中界面的主要代码
View header = LayoutInflater.from(this).inflate(R.layout.recycler_header, mRecyclerView, false);
mRecyclerWithHeaderAdapter = new RecyclerWithHeaderAdapter(Datas.getDatas(), header);
mRecyclerView.setAdapter(mRecyclerWithHeaderAdapter);
mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override public int getSpanSize(int position) {
return mRecyclerWithHeaderAdapter.isHeader(position) ? mGridLayoutManager.getSpanCount() : 1;
}
});
在网格布局中,依靠了GridLayoutManager#setSpanSizeLookup
方法来设置每个Item占多少个span,
如果是在瀑布流布局中,则可以通过给StaggeredGridLayoutManager.LayoutParams
设置setFullSpan(true);
来占满一行.
参考自: RecyclerView添加Header的正确方式
ItemTouchHelper
用于Recyclerview
的触摸辅助操作,比如选中,移动,删除等事件的监听.
这里可以看一下: 可拖拽的RecyclerView,通过跟踪代码我们可以看到,ItemTouchHelper
内部
主要是通过GestureDetectorCompat
来处理触摸事件,并通过给Recyclerview
设置OnItemTouchListener
来实现效果
但是遗憾的是其中只 实现了onLongPress
的监听.但是为我们设置Recyclerview
的Item监听提供了一种新的思路
//ItemTouchHelper#ItemTouchHelperGestureListener
private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
//...
}
}
通过ItemTouchHelper
实现Recyclerview
的Click
,LongClick
,Select
,Swipe
,Drag
等效果,
主要通过一个叫做ItemTouchHelper.Callback
的类,示例:RvItemTouchHelperCallback
Recyclerview 选中模式
遗憾的是Recyclerview
并不像Listview
那样提供selectMode
,但是在我们最初不适用selectMode
的时候,我们是否采用过一种通过刷新Adapter
来实现的方式呢
在Recyclerview
中要实现选中模式,我们当然也要从Adapter
下手,这里可以看一下Multi-Selection Mode for RecyclerView
其中的核心代码
private SparseBooleanArray mSelectedItems;
public void switchSelectedState(int position) {
if (mSelectedItems.get(position)) { // item has been selected, de-select it.
mSelectedItems.delete(position);
} else {
mSelectedItems.put(position, true);
}
notifyItemChanged(position);
}
public void clearSelectedState() {
List<Integer> selection = getSelectedItems();
mSelectedItems.clear();
for (Integer i : selection) {
notifyItemChanged(position);
}
}
可以看到作者也是通过在Adapter
中维护一个选中的集合来实现Recyclerview
的选中模式的,
最后,这里是从 其他项目中 收集的 Recyclerview 好用的 Utils :RecyclerViewHelper
总结
Recyclerview
不仅在灵活度和扩展性上都较AbsListView
好,在项目中也得到了大量的使用,也还有大量的未知方法等着去实践,
比如Recyclerview
的Scroll
操作,RecycledViewPool
.Recyclerview
的上下拉刷新,
自定义LayoutManager
、itemDecoration
、itemAnimator
等待
参考:
Android RecyclerView 使用完全解析 体验艺术般的控件
可拖拽的RecyclerView
Multi-Selection Mode for RecyclerView
Using the RecyclerView
RecyclerView添加Header的正确方式