标签:
自学习android以来,其实一直都有接触到 RecyclerView,今天便总结一下关于RecyclerView的相关知识,并不是非常全面。主要从以下几个方面:
源码地址:https://github.com/Ti2Yuan/RecyclerViewDemo
1. RecyclerView概述
2014年Google IO的召开,Android L Preview版发布,对于开发者而言,它带来了性能上的改善。其中,一个全新的控件也进入开发者的视野中,并得到越来越多的使用,大有取代ListView的趋势,它就是RecyclerView。
A flexible view for providing a limited window into a large data set.
上面那句话是官网中对RecyclerView的描述:能在有限的窗口中显示大数据集的灵活视图。
RecyclerView是Google support-v7包下新增的控件,用来替代ListView的使用,RecyclerView标准化了ViewHolder类似于ListView中convertView用来做视图缓存。但是它却是ListView的增强版。
2.RecyclerView与ListView区别
正如官方文档所言,RecyclerView是ListView的豪华增强版。它主要包含以下几处新的特性,如ViewHolder,ItemDecorator,LayoutManager,SmoothScroller以及增加或删除item时item动画等。官方推荐我们采用RecyclerView来取代ListView。
ViewHolder
ViewHolder是用来保存视图引用的类,在ListView中,ViewHolder需要自己来定义,但只是一种推荐的使用方式,不是必须要使用的。取而代之的是ListView性能的迟缓,因为ListView每次getView的时候都会调用findViewById(int)方法。而在RecyclerView中则强调必须使用RecyclerView.ViewHolder,否则代码将不能运行。虽然这个过程实现起来稍显复杂,但是却避免了ListView不使用ViewHolder而带来的性能问题。
LayoutManager
ListView只能在垂直方向上滚动,google并没有给出ListView在水平方向上面滚动的Android API支持。但是RecyclerView相较于ListView,在滚动上可以支持多种类型列表,例如:
LinearLayoutManager,可以支持水平和竖直方向上滚动的列表。
StaggeredGridLayoutManager,可以支持交叉网格风格的列表,类似于瀑布流或者Pinterest。
GridLayoutManager,支持网格展示,可以水平或者竖直滚动,如展示图片的画廊。
ItemAnimator
列表动画是一个全新的、拥有无限可能的维度。起初的Android API中,删除或添加item时,item是无法产生动画效果的。后面随着Android的进化,Google的Chat Hasse推荐使用ViewPropertyAnimator属性动画来实现上述需求。
RecyclerView.ItemAnimator用于在RecyclerView中添加、删除或移动item时处理动画效果。同时也存在一个默认的动画效果DefaultItemAnimator。而ListView则不具备这种API。
Adapter
在ListView的Adapter中,getView方法将视图跟position绑定在一起。同时我们能通过registerDataObserver在Adapter中注册一个观察者。在RecyclerView中,我们也可以通过RecyclerView.AdapterDataObserver观察。ListView有三个Adapter的默认实现,分别是ArrayAdapter、CursorAdapter和SimpleCursorAdapter。然而,RecyclerView的Adapter则拥有除了内置的内DB游标和ArrayList的支持之外的所有功能。RecyclerView.Adapter的实现中,我们必须采取措施将数据提供给Adapter。
ItemDecoration
在ListView中我们可以通过divider和dividerHeight这些相关属性为item添加间隔符。但是在RecyclerView中可通过RecyclerView.ItemDecoration类来实现自定义间隔符。默认情况下item之间不会展示间隔符。这的确增加了开发人员的负担,如果你想要添加间隔符的话。你还可以参考官方示例中的DividerItemDecoration.java文件。
OnItemTouchListener
ListView通过AdapterView.OnItemClickListener接口来探测点击事件。而RecyclerView则可以通过RecyclerView.OnItemTouchListener接口来探测触摸事件。这种探测方式虽然增加了实现的难度,但是却给予开发人员拦截触摸事件更多的控制权限。
还有就是ListView可以添加MultiChoiceModeListener来设置选择模式,但是RecyclerView却没有。
总结:
recyclerView自定义强,可以实现复杂的布局,但过程稍显复杂。
3.RecyclerView基本使用
首先,在moudle下的build.gradle导入包
dependencies {
compile ‘com.android.support:recyclerview-v7:23.2.0‘
}
然后在布局中
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/recycleView"
android:layout_margin="10dp"
android:scrollbars="vertical"/>`
然后就是为RecyclerView设置LayoutManager,Adapter,ItemAnimator和ItemDecoration。而自定义的RecyclerViewAdapter中必须继承RecyclerView.Adapter,并且实现需要重写的方法,如onCreateViewHolder,onBindViewHolder,getItemCount()等等。并且可通过getItemViewType(int position)方法返回item 类型,可用于返回headItem和FootItem类型。
recyclerView =(RecyclerView)findViewById(R.id.recycleView);
mLayoutManager = new LinearLayoutManager(this);
adapter = new RecyclerViewAdapter(this, list);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}
});
@Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_HEAD;
} else if (position + 1 == getItemCount()) {
return TYPE_FOOT;
} else {
return TYPE_ITEM;
}
}
4. RecyclerView item单击与长按事件
一般情况下,我们可以在自定义RecyclerView.Adapter的Adapter类中为ViewHolder的rootView设置OnClickListener()来监听item的点击事件,但这种方式或多或少浪费性能,而存在一种更加高逼格的方式来实现诸如单击和长按事件,就是通过探测手势触摸的方式。一下是使用方法:
recyclerView.addOnItemTouchListener(new OnRecyclerViewItemTouchListener(recyclerView) {
@Override
protected void onItemClick(RecyclerView.ViewHolder vh) {
}
@Override
protected void onItemLongClick(RecyclerView.ViewHolder vh) {
}
});
public abstract class OnRecyclerViewItemTouchListener implements RecyclerView.OnItemTouchListener {
private RecyclerView recyclerView;
private GestureDetectorCompat gestureDetector;
public OnRecyclerViewItemTouchListener(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
gestureDetector = new GestureDetectorCompat(recyclerView.getContext(),
new ItemTouchHelperGestureListener());
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
gestureDetector.onTouchEvent(e);
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
gestureDetector.onTouchEvent(e);
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@Override
public void onShowPress(MotionEvent motionEvent) {
}
/**
* 单击事件
* @param motionEvent
* @return
*/
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
View child = recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
if(child != null){
RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
onItemClick(vh);
}
return true;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
/**
* 长按事件
* @param motionEvent
*/
@Override
public void onLongPress(MotionEvent motionEvent) {
View child = recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
if(child != null){
RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
onItemLongClick(vh);
}
}
@Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
}
protected abstract void onItemClick(RecyclerView.ViewHolder vh);
protected abstract void onItemLongClick(RecyclerView.ViewHolder vh);
}
RecyclerView提供了设置触摸监听的方法,那么我们定义一个类OnRecyclerViewItemTouchListener实现OnItemTouchListener。重写3个方法OnInterceptTouchEvent,onTouchEvent,onRequestDisallowInterceptTouchEvent。其中第三个方法是处理触摸事件冲突的,前两个方法就是View的事件分发机制里面的事件拦截和事件处理的两个方法,参数里为我们提供了触摸事件的数据MotionEvent,我们要做的就是去解析坐标点和触摸规律来识别触摸手势,然后获取触摸的是哪一个item,sdk已经为我们实现了手势的识别:
GestureDetectorCompat 就是处理手势的类:手势探测器,它比GestureDetector能更好兼容低版本的api,但使用方法是一致的,我们实例化一个手势探测器:
gestureDetector = new GestureDetectorCompat(recyclerView.getContext(),
new ItemTouchHelperGestureListener());
实例化手势探测器的时候需要提供一个手势监听器:OnGestureListener,探测器识别出手势后就会回调手势监听器中对应的方法,我们就可以在回调方法中做我们想做的事情了。
sdk为我们提供了两个手势监听器:OnGestureListener,OnDoubleTapListener。
OnGestureListener主要回调各种单击事件,而OnDoubleTapListener回调各种双击事件。而我们需要处理的点击事件其实就是上面的:onSingleTapUp()。而sdk 还提供了一个外部类SimpleOnGestureListener,这个类实现了上面两个接口的所有方法,但全都是空实现,函数体里什么也没写,其中就是把上面两个接口合并一下,给出默认的空实现,这样继承SimpleOnGestureListener的时候就不用实现每一个方法了。RecyclerView已经为我们提供了findChildViewUnder(),我们可以通过这个方法获得点击的item,同时我们调用RecyclerView的另一个方法getChildViewHolder(),可以获得该item的ViewHolder,最后再回调我们定义的虚方法onItemClick()就ok了,这样我们就可以在外部实现该方法来获得item的点击事件了。
5. RecyclerView item长按拖拽和侧滑删除
可以通过ItemTouchHelper这个类来实现RecyclerView item的拖拽和滑动。
This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
这是Google官方文档描述这个类的话:这是一个支持RecyclerView滑动删除和拖拽的实用工具类。
具体用法如下:
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelpCallback(this,list) {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getLayoutPosition();
if(position != 0 && position != recyclerView.getAdapter().getItemCount() - 1) {
adapter.notifyItemRemoved(position);
list.remove(position-1);
}
}
});
itemTouchHelper.attachToRecyclerView(recyclerView);
public abstract class ItemTouchHelpCallback extends ItemTouchHelper.Callback {
private List<String> list;
private Context context;
public ItemTouchHelpCallback(Context context, List<String> list) {
this.list = list;
this.context = context;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final int dragFlags, swipeFlags;
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
dragFlags = ItemTouchHelper.UP |
ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT |
ItemTouchHelper.RIGHT;
swipeFlags = 0;
} else {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
}
return makeMovementFlags(dragFlags, swipeFlags);
}
//上下移动item
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//得到拖动viewHolder的position
int fromPosition = viewHolder.getAdapterPosition();
//得到目标viewHolder的position
int toPosition = target.getAdapterPosition();
if (fromPosition != 0 && fromPosition != recyclerView.getAdapter().getItemCount() - 1 &&
toPosition != 0 && toPosition != recyclerView.getAdapter().getItemCount() - 1)
{
if (fromPosition < toPosition) {
for (int i = fromPosition - 1; i < toPosition - 1; ++i) {
Collections.swap(list, i, i + 1); //改变实际的数据集
}
} else {
for (int i = fromPosition - 1; i > toPosition - 1; i--) {
Collections.swap(list, i, i - 1); //改变实际的数据集
}
}
}
recyclerView.getAdapter().notifyItemMoved(fromPosition, toPosition);
return true;
}
//当长按选中item的时候(拖拽开始的时候)调用
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
viewHolder.itemView.setBackgroundColor(context.getResources().
getColor(R.color.colorPrimary));
}
Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
vib.vibrate(70); //震动70毫秒
super.onSelectedChanged(viewHolder, actionState);
}
//当手指松开的时候(拖拽完成的时候)调用
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setBackgroundColor(Color.WHITE);
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
//滑动时改变Item的透明度
final float alpha = 1 - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
}
}
}
要使用ItemTouchHelper,你需要创建一个ItemTouchHelper.Callback。这个接口可以让你监听“move”与 “swipe”事件。自定义类继承它时需要实现一些方法:getMovementFlags,onMove,onSwiped。另外还有一些方法如onSelectedChanged,clearView可以控制这个item被选中和被释放的状态,还有onChildDraw方法可以重写item滑动的默认动画。
ItemTouchHelper可以让你轻易得到一个事件的方向。重写getMovementFlags()方法来指定可以支持的拖放和滑动的方向。使用helperItemTouchHelper.makeMovementFlags(int, int)来构造返回的flag。
@Override
public boolean isLongPressDragEnabled() {
return true;
}
ItemTouchHelper可以用于没有滑动的拖动操作(或者反过来),你必须指明你到底要支持哪一种。要支持长按RecyclerView item进入拖动操作,你必须在isLongPressDragEnabled()方法中返回true。或者,也可以调用ItemTouchHelper.startDrag(RecyclerView.ViewHolder) 方法来开始一个拖动。这会在后面讲到。
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
而要在view任意位置触摸事件发生时启用滑动操作,则直接在sItemViewSwipeEnabled()中返回true就可以了。或者,你也主动调用ItemTouchHelper.startSwipe(RecyclerView.ViewHolder) 来开始滑动操作。
源码地址:https://github.com/Ti2Yuan/RecyclerViewDemo
参考:
http://www.jianshu.com/p/16712681731e
http://blog.csdn.net/u014497502/article/details/50917321
http://www.cnblogs.com/littlepanpc/p/4497290.html
http://www.voidcn.com/blog/liaoinstan/article/p-5785579.html
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0630/3123.html
标签:
原文地址:http://blog.csdn.net/t465882689/article/details/51924200