[Android开发]从Android官方Demo谈RecyclerView的用法
2016-11-06 16:40
405 查看
RecyclerView是Android5.0中出现的新控件,官方API解释就一句话:
A flexible view for providing a limited window into a large data set
整体架构如下图:
RecyclerView的灵活性体现在6个方面:
可以控制显示方式,包括三个内置的不觉管理器,也可以定制
LinearLayoutManager 以垂直或水平滚动列表方式显示项目
GridLayoutManager 在网格中显示项目
StaggeredGridLayoutManager 瀑布了流中显示项目
默认情况下显示增加删除的动画,也扩RecyclerView.ItemAnimator定制
默认情况无分割线,可以扩展ItemDecoration定制
官方Training:创建列表与卡片
深入理解 RecyclerView 系列之一:ItemDecoration
Android RecyclerView 使用完全解析 体验艺术般的控件
代码比较简单,重要的内容包括RecyclerView的初始化和其对应的Adapter的构造。
这里参考深入理解 RecyclerView 系列之一:ItemDecoration(http://www.tuicool.com/articles/fIbuYfI)来实现该接口。
第一步,需要在CustomAdapter中自己定义回调接口。
第二步,在CustomAdapter中的onBindViewHolder(ViewHolder viewHolder, final int position)中调用接口函数。
第三步,在CustomAdapter初始化的地方传入该接口实例。
这里配合上面的点击事件,仅仅模拟了多选的操作,而没有添加ActionMode。
其中,addOrRemove(int position)多选的逻辑。
CustomAdapter中有一个集合在记录多选模式下已经点选的位置,点击时判断该集合是否包含了该位置,如果已经包含,就取消颜色,否则改变颜色。
最后达成的效果是
长按RecyclerView中的某一项,会进入到多选模式
如果点击的项已经在CustomAdapter中的集合中,则去除这些项,如果集合清空,则退出多选模式
多选模式下再点击某些项,这些项会记录到CustomAdapter中的集合中
https://github.com/caoyanfeng/GoogleSamples
如果觉得写得不错,请给我的GitHub点star哦~
A flexible view for providing a limited window into a large data set
整体架构如下图:
RecyclerView的灵活性体现在6个方面:
可以控制显示方式,包括三个内置的不觉管理器,也可以定制
LinearLayoutManager 以垂直或水平滚动列表方式显示项目
GridLayoutManager 在网格中显示项目
StaggeredGridLayoutManager 瀑布了流中显示项目
默认情况下显示增加删除的动画,也扩RecyclerView.ItemAnimator定制
默认情况无分割线,可以扩展ItemDecoration定制
参考资料
官方API官方Training:创建列表与卡片
深入理解 RecyclerView 系列之一:ItemDecoration
Android RecyclerView 使用完全解析 体验艺术般的控件
官方Demo效果
官方提供了一个Demo(github地址)的运行效果是这样的:代码比较简单,重要的内容包括RecyclerView的初始化和其对应的Adapter的构造。
引申需求
设置分割线
分割线官方并没有提供默认的类型,默认也并没有分隔线。要提供分隔线必须自己实现RecyclerView.ItemDecoration。/** * An ItemDecoration allows the application to add a special drawing and layout offset * to specific item views from the adapter's data set. This can be useful for drawing dividers * between items, highlights, visual grouping boundaries and more. * * <p>All ItemDecorations are drawn in the order they were added, before the item * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()} * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView, * RecyclerView.State)}.</p> */ public static abstract class ItemDecoration { /** * Draw any appropriate decorations into the Canvas supplied to the RecyclerView. * Any content drawn by this method will be drawn before the item views are drawn, * and will thus appear underneath the views. * * @param c Canvas to draw into * @param parent RecyclerView this ItemDecoration is drawing into * @param state The current state of RecyclerView */ public void onDraw(Canvas c, RecyclerView parent, State state) { onDraw(c, parent); } /** * @deprecated * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)} */ @Deprecated public void onDraw(Canvas c, RecyclerView parent) { } /** * Draw any appropriate decorations into the Canvas supplied to the RecyclerView. * Any content drawn by this method will be drawn after the item views are drawn * and will thus appear over the views. * * @param c Canvas to draw into * @param parent RecyclerView this ItemDecoration is drawing into * @param state The current state of RecyclerView. */ public void onDrawOver(Canvas c, RecyclerView parent, State state) { onDrawOver(c, parent); } /** * @deprecated * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)} */ @Deprecated public void onDrawOver(Canvas c, RecyclerView parent) { } /** * @deprecated * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)} */ @Deprecated public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { outRect.set(0, 0, 0, 0); } /** * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies * the number of pixels that the item view should be inset by, similar to padding or margin. * The default implementation sets the bounds of outRect to 0 and returns. * * <p> * If this ItemDecoration does not affect the positioning of item views, it should set * all four fields of <code>outRect</code> (left, top, right, bottom) to zero * before returning. * * <p> * If you need to access Adapter for additional data, you can call * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the * View. * * @param outRect Rect to receive the output. * @param view The child view to decorate * @param parent RecyclerView this ItemDecoration is decorating * @param state The current state of RecyclerView. */ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent); } }
这里参考深入理解 RecyclerView 系列之一:ItemDecoration(http://www.tuicool.com/articles/fIbuYfI)来实现该接口。
import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; /** * 参考:https://android.googlesource.com/platform/development/+/master/samples/Support7Demos/src * /com/example/android/supportv7/widget/decorator/DividerItemDecoration.java#101 */ public class DividerItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; private Drawable mDivider; private int mOrientation; public DividerItemDecoration(Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } public void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } /** * outRect是用来设置left、top、right、bottom的padding值的 * */ @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } } }
点击事件
RecyclerView本身不提供单击和长按事件,需要自己实现。分为几个步骤。第一步,需要在CustomAdapter中自己定义回调接口。
/** * 点击事件接口 * */ public interface OnItemClickListener{ void onItemClick(View view,int position); void onItemLongClick(View view,int position); } private OnItemClickListener onItemClickListener; public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; }
第二步,在CustomAdapter中的onBindViewHolder(ViewHolder viewHolder, final int position)中调用接口函数。
@Override public void onBindViewHolder(ViewHolder viewHolder, final int position) { Log.d(TAG, "Element " + position + " set."); // Get element from your dataset at this position and replace the contents of the view // with that element viewHolder.getTextView().setText(mDataSet[position]); viewHolder.setColor(position); if (onItemClickListener!=null){ //可以获得每个item的包装类itemView viewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onItemClickListener.onItemClick(v,position); } }); viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { onItemClickListener.onItemLongClick(v,position); return true; } }); } }
第三步,在CustomAdapter初始化的地方传入该接口实例。
mAdapter.setOnItemClickListener(new CustomAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { ××××单击事件×××××× } @Override public void onItemLongClick(View view, int position) { ××××长按事件×××××× } });
多选模式
多选模式可以采用ActionMode来进行UI设计,本质上是通过长按进入ActionMode模式,可以点击按钮取消多选模式。这里配合上面的点击事件,仅仅模拟了多选的操作,而没有添加ActionMode。
mAdapter.setOnItemClickListener(new CustomAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { if (isOnLonCliked){ addOrRemove(position); Log.i(TAG, position+"OnLonCliked"); }else { Toast.makeText(getActivity(),position+" clicked",Toast.LENGTH_LONG).show(); } } @Override public void onItemLongClick(View view, int position) { isOnLonCliked=true; Log.i(TAG, position+"OnLonCliked"); } });
其中,addOrRemove(int position)多选的逻辑。
** * 模拟多选情况 * */ private void addOrRemove(int position){ if(mAdapter.positionSet.contains(position)){ mAdapter.positionSet.remove(position); }else { mAdapter.positionSet.add(position); } if (mAdapter.positionSet.size()==0){ isOnLonCliked=false; } mAdapter.notifyDataSetChanged(); }
CustomAdapter中有一个集合在记录多选模式下已经点选的位置,点击时判断该集合是否包含了该位置,如果已经包含,就取消颜色,否则改变颜色。
/** * Provide views to RecyclerView with data from mDataSet. */ ××××××省略无关代码×××××××××××××× public static Set<Integer> positionSet = new HashSet<>(); // BEGIN_INCLUDE(recyclerViewSampleViewHolder) /** * Provide a reference to the type of views that you are using (custom ViewHolder) */ public static class ViewHolder extends RecyclerView.ViewHolder { public final TextView textView; public ViewHolder(View v) { super(v); // Define click listener for the ViewHolder's View. v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "Element " + getAdapterPosition() + " clicked."); } }); textView = (TextView) v.findViewById(R.id.textView); Log.d(TAG, "ViewHolder "); } public TextView getTextView() { return textView; } public void setColor(int position){ if (positionSet.contains(position)){ textView.setTextColor(Color.GREEN); }else { textView.setTextColor(Color.BLACK); } } } ××××××省略无关代码××××××××××××××
最后达成的效果是
长按RecyclerView中的某一项,会进入到多选模式
如果点击的项已经在CustomAdapter中的集合中,则去除这些项,如果集合清空,则退出多选模式
多选模式下再点击某些项,这些项会记录到CustomAdapter中的集合中
GitHub地址
本文的工程地址为:https://github.com/caoyanfeng/GoogleSamples
如果觉得写得不错,请给我的GitHub点star哦~
相关文章推荐
- 微信在android平台上开发的注意事项(以官方提供的demo为例):
- Android动画学习Demo(1) 关于ViewAnimation的用法及总结
- 玩转Android Camera开发(一):Surfaceview预览Camera,基础拍照功能完整demo
- 玩转Android Camera开发(一):Surfaceview预览Camera,基础拍照功能完整demo
- Android动画学习Demo(1) 关于ViewAnimation的用法及总结
- 玩转Android Camera开发(一):Surfaceview预览Camera,基础拍照功能完整demo
- ANDROID L——RecyclerView,CardView导入和使用(Demo)
- ANDROID L——RecyclerView,CardView导入和使用(Demo)
- ANDROID L——RecyclerView,CardView导入和使用(Demo)
- Android开发技巧之viewstub用法详解及实现延迟加载
- Android开发--SurfaceView的基本用法
- 玩转Android Camera开发(一):Surfaceview预览Camera,基础拍照功能完整demo
- ANDROID L——RecyclerView,CardView导入和使用(Demo)
- android 开发:讯飞的离线命令识别器官方demo使用及demo下载
- Android开发--RecyclerView使用,看AndroidL新特性
- Android开发--RecyclerView使用,看AndroidL新特性
- android开发MediaPlayer用法demo
- Android开发--RecyclerView使用,看AndroidL新特性
- ANDROID L——RecyclerView,CardView导入和使用(Demo)
- Android开发--RecyclerView使用,看AndroidL新特性