RecyclerView学习笔记
来源:互联网 发布:power map for mac 编辑:程序博客网 时间:2024/06/05 05:32
自学习android以来,其实一直都有接触到 RecyclerView,今天便总结一下关于RecyclerView的相关知识,并不是非常全面。主要从以下几个方面:
- RecyclerView概述
- RecyclerView与ListView区别
- RecyclerView基本使用
- RecyclerView item单击与长按事件
- RecyclerView item长按拖拽和侧滑删除
源码地址: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。
@Overridepublic boolean isLongPressDragEnabled() { return true;}
ItemTouchHelper可以用于没有滑动的拖动操作(或者反过来),你必须指明你到底要支持哪一种。要支持长按RecyclerView item进入拖动操作,你必须在isLongPressDragEnabled()方法中返回true。或者,也可以调用ItemTouchHelper.startDrag(RecyclerView.ViewHolder) 方法来开始一个拖动。这会在后面讲到。
@Overridepublic 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
- RecyclerView学习笔记
- RecyclerView学习笔记
- RecyclerView学习笔记
- android RecyclerView学习笔记
- RecyclerView学习笔记
- [学习笔记]Android RecyclerView
- MaterialDesign之RecyclerView学习笔记
- Android学习笔记之RecyclerView
- RecyclerView+CardView+SwipeRefreshLayout---学习笔记
- MaterialDesign之RecyclerView学习笔记2
- Android学习笔记之RecyclerView详解
- RecyclerView学习笔记(1) ChildHelper.Bucket
- 学习android(RecyclerView)个人笔记二
- 《第一行代码》RecyclerView学习笔记
- RecyclerView笔记
- RecyclerView笔记
- RecyclerView学习
- RecyclerView学习
- 利用RxJava加载图片
- 周志华《机器学习》——第一章习题讨论
- 查看HDFS上存储的hive表
- openssl入门--2--对称算法指令
- caffe convolution layer 笔记
- RecyclerView学习笔记
- 矩阵 Fibonacci
- mysql-connector python几点问题
- ArrayDeque类源码解析
- iOS 开发中滑动视图的循环播放
- 最大公约数和最小公倍数(gcd的几种解法)
- 实现UWP ListBox横向展示内容
- Poj 2002
- crontab定时任务