RecyclerView 详解
2016-06-14 20:23
405 查看
概述
RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。 据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView、GridView。那么有了ListView、GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。你想要控制其显示的方式,请通过布局管理器LayoutManager 你想要控制Item间的间隔(可绘制),请通过ItemDecoration 你想要控制Item增删的动画,请通过ItemAnimator 你想要控制点击、长按事件,请自己写! RecyclerView基本的使用代码: mRecyclerView = findView(R.id.id_recyclerview); mRecyclerView.setLayoutManager(layout);//设置布局管理器 mRecyclerView.setAdapter(adapter)//设置adapter mRecyclerView.setItemAnimator(new DefaultItemAnimator());//设置Item增加、移除动画 mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));//添加分割线 相比较于ListView的代码,ListView可能只需要去设置一个adapter就能正常使用了。而RecyclerView基本需要上面一系列的步骤,那么为什么会添加这么多的步骤呢?那么就必须解释下RecyclerView的这个名字了,从它类名上看,RecyclerView代表的意义是,我只管Recycler View,也就是说RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)。
分割线 ItemDecoration
RecyclerView并没有支持divider这样的属性,你可以给Item的布局去设置margin、background等方式来间接添加分割线,但这种方式不够优雅,我们的分割线可以在代码中通过以下方法添加。 mRecyclerView.addItemDecoration() 该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类。 public static abstract class ItemDecoration { public void onDraw(Canvas c, RecyclerView parent, State state) { onDraw(c, parent); } public void onDrawOver(Canvas c, RecyclerView parent, State state) { onDrawOver(c, parent); } public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent); } @Deprecated public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { outRect.set(0, 0, 0, 0); }} 若我们调用mRecyclerView.addItemDecoration()方法添加分割线,则RecyclerView在绘制的时候,会去绘制Decoration,即调用该类的onDraw和onDrawOver方法:onDraw 方法在drawChildren之前 onDrawOver在drawChildren之后,一般我们选择复写其中一个即可。 getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。
代码 Activity
public class MainActivity extends ActionBarActivity implements MyOnItemClickLitener { private RecyclerView mRecyclerView; private List<String> mDatas;//数据 private List<Integer> mHeights;//高度 private MyRecyclerViewAdapter mRecyclerViewAdapter;//适配器 private MyStaggeredAdapter mStaggeredAdapter; private ItemDecoration decoration1;//分割线 private ItemDecoration decoration2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRecyclerView = new android.support.v7.widget.RecyclerView(this); setContentView(mRecyclerView); initData(); initAdapter(); initRecylerView(); } protected void initData() { mDatas = new ArrayList<String>(); for (int i = 'A'; i < 'z'; i++) { mDatas.add("" + (char) i); } mHeights = new ArrayList<Integer>(); for (int i = 0; i < mDatas.size(); i++) { mHeights.add((int) (100 + Math.random() * 500)); } } private void initAdapter() { mRecyclerViewAdapter = new MyRecyclerViewAdapter(this, mDatas); mStaggeredAdapter = new MyStaggeredAdapter(this, mDatas, mHeights); mRecyclerViewAdapter.setOnItemClickLitener(this); mStaggeredAdapter.setOnItemClickLitener(this); } private void initRecylerView() { decoration1 = new DividerItemDecoration(this, 0); decoration2 = new DividerItemDecoration(this, 1); mRecyclerView.setPadding(10, 10, 10, 10); mRecyclerView.setAdapter(mRecyclerViewAdapter);//设置adapter mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));//设置布局管理器 mRecyclerView.addItemDecoration(decoration1);//添加一个分割线 mRecyclerView.addItemDecoration(decoration2);//还可以再添加一个分割线 mRecyclerView.setItemAnimator(new DefaultItemAnimator());//设置Item增加、移除动画。github上有很多动画效果,如RecyclerViewItemAnimators } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { mRecyclerView.removeItemDecoration(decoration1);//移除分割线,即使没有显示也可以移除 mRecyclerView.removeItemDecoration(decoration2); switch (item.getItemId()) { case R.id.add: mRecyclerViewAdapter.addData(new Random().nextInt(5)); break; case R.id.delete: mRecyclerViewAdapter.removeData(new Random().nextInt(5)); break; //通过RecyclerView去实现ListView、GridView、瀑布流的效果基本上没有什么区别,仅仅通过设置不同的LayoutManager即可实现 case R.id.listview: mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); break; case R.id.gridview: mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3)); break; case R.id.staggeredHorizontalGridView://Staggered:错列的,叉排的。 mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL));//5行 break; case R.id.staggeredVerticalGridview: mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));//3列 break; case R.id.staggeredAdapter: mRecyclerView.setAdapter(mStaggeredAdapter); break; case R.id.recyclerViewAdapter: mRecyclerView.setAdapter(mRecyclerViewAdapter); break; } return true; } @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, position + " 被点击了", Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(MainActivity.this, position + "被长按了", Toast.LENGTH_SHORT).show(); }}
固定宽高的Adapter
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> { private Context context; private List<String> mDatas; private MyOnItemClickLitener mOnItemClickLitener; public void setOnItemClickLitener(MyOnItemClickLitener mOnItemClickLitener) { this.mOnItemClickLitener = mOnItemClickLitener; } public MyRecyclerViewAdapter(Context context, List<String> datas) { this.context = context; mDatas = datas; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item, parent, false)); } @Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.tv.setText(mDatas.get(position)); RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.tv.getLayoutParams(); lp.setMargins(5, 5, 5, 5);//设置边距 holder.tv.setLayoutParams(lp); // 如果设置了回调,则设置点击事件 if (mOnItemClickLitener != null) { holder.itemView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemClick(holder.itemView, pos); } }); holder.itemView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemLongClick(holder.itemView, pos); removeData(pos); return false; } }); } } @Override public int getItemCount() { return mDatas.size(); } /**添加并更新数据,同时具有动画效果*/ public void addData(int position) { mDatas.add(position, "Insert One"); notifyItemInserted(position);//更新数据集,注意不是用adapter.notifyDataSetChanged(),否则没有动画效果 } /**移除并更新数据,同时具有动画效果*/ public void removeData(int position) { mDatas.remove(position); notifyItemRemoved(position); } class MyViewHolder extends RecyclerView.ViewHolder { TextView tv; public MyViewHolder(View view) { super(view); tv = (TextView) view.findViewById(R.id.id_num); } }}
随机宽高的Adapter[/b]
/**和MyAdapter唯一的区别就是:在代码中动态设置了TextView的高度(宽度)*/public class MyStaggeredAdapter extends RecyclerView.Adapter<MyStaggeredAdapter.MyViewHolder> { private Context context; private List<String> mDatas; private List<Integer> mHeights;//高度 private MyOnItemClickLitener mOnItemClickLitener; public void setOnItemClickLitener(MyOnItemClickLitener mOnItemClickLitener) { this.mOnItemClickLitener = mOnItemClickLitener; } public MyStaggeredAdapter(Context context, List<String> datas, List<Integer> heights) { this.context = context; this.mDatas = datas; this.mHeights = heights; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item, parent, false)); } @Override public void onBindViewHolder(final MyViewHolder holder, final int position) { RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) holder.tv.getLayoutParams(); lp.setMargins(5, 5, 5, 5); //横向时,item的宽度需要设置;纵向时,item的高度需要设置 lp.height = mHeights.get(position);//******************************************************************************************唯一的区别在这里! lp.width = mHeights.get(position);//*******************************************************************************************唯一的区别在这里! holder.tv.setLayoutParams(lp); holder.tv.setText(mDatas.get(position)); // 如果设置了回调,则设置点击事件 if (mOnItemClickLitener != null) { holder.itemView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemClick(holder.itemView, pos); } }); holder.itemView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemLongClick(holder.itemView, pos); removeData(pos); return false; } }); } } @Override public int getItemCount() { return mDatas.size(); } public void addData(int position) { mDatas.add(position, "Insert One"); mHeights.add((int) (100 + Math.random() * 300)); notifyItemInserted(position); } public void removeData(int position) { mDatas.remove(position); notifyItemRemoved(position); } class MyViewHolder extends RecyclerView.ViewHolder { TextView tv; public MyViewHolder(View view) { super(view); tv = (TextView) view.findViewById(R.id.id_num); } }}
点击事件回调接口
/**系统没有提供ClickListener和LongClickListener,我们自己通过接口回调处理 */public interface MyOnItemClickLitener { void onItemClick(View view, int position); void onItemLongClick(View view, int position);}
分割线示例
/**zhy写的分割线,Item如果为最后一列则右边无间隔线,如果为最后一行则底部无分割线*/public class MyGridItemDecoration extends RecyclerView.ItemDecoration { private Drawable mDivider; public MyGridItemDecoration(Context context) { //通过读取系统主题中的 Android.R.attr.listDivider属性,将其作为Item间的分割线, <item name="android:listDivider">@drawable/divider_bg</item> TypedArray typedArray = context.obtainStyledAttributes(new int[] { android.R.attr.listDivider }); mDivider = typedArray.getDrawable(0); typedArray.recycle();//回收TypedArray,以便后面重用。This TypedArray should be recycled after use with recycle() } @Override /**判断如果是最后一行,则不需要绘制底部;如果是最后一列,则不需要绘制右边,整个判断也考虑到了StaggeredGridLayoutManager的横向和纵向*/ public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); int itemCount = parent.getAdapter().getItemCount(); //使用outRect设置绘制的范围。一般如果仅仅是希望有空隙,还是去设置item的margin方便 if (isLastRaw(parent, itemPosition, spanCount, itemCount)) {// 如果是最后一行,则不需要绘制底部 outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else if (isLastColum(parent, itemPosition, spanCount, itemCount)) {// 如果是最后一列,则不需要绘制右边 outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } } @Override public void onDraw(Canvas c, RecyclerView parent, State state) { drawHorizontal(c, parent); drawVertical(c, parent); } //****************************************************************************************** public void drawHorizontal(Canvas c, RecyclerView parent) { 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.getLeft() - params.leftMargin; final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawVertical(Canvas c, RecyclerView parent) { 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 top = child.getTop() - params.topMargin; final int bottom = child.getBottom() + params.bottomMargin; final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } //****************************************************************************************** /**获取RecyclerView有多少列*/ private int getSpanCount(RecyclerView parent) { int spanCount = -1; // 列数 LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); else if (layoutManager instanceof StaggeredGridLayoutManager) spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount(); return spanCount; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; if (pos >= childCount) // 如果是最后一行,则不需要绘制底部 return true; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation(); // StaggeredGridLayoutManager 且纵向滚动 if (orientation == StaggeredGridLayoutManager.VERTICAL) { childCount = childCount - childCount % spanCount; if (pos >= childCount) return true;// 如果是最后一行,则不需要绘制底部 } else { // StaggeredGridLayoutManager 且横向滚动 if ((pos + 1) % spanCount == 0) return true;// 如果是最后一行,则不需要绘制底部 } } return false; } private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { if ((pos + 1) % spanCount == 0) {// 如果是最后一列,则不需要绘制右边 return true; } } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL) { if ((pos + 1) % spanCount == 0) {// 如果是最后一列,则不需要绘制右边 return true; } } else { childCount = childCount - childCount % spanCount; if (pos >= childCount) // 如果是最后一列,则不需要绘制右边 return true; } } return false; }}
来自为知笔记(Wiz)
附件列表
相关文章推荐
- 设计模式六大原则
- 虚函数和纯虚函数的区别
- Lucas求解组合数模板
- 链表解决银行储存系统---技术支持
- 【CF679E】Bear and Bad Powers of 42
- 数组做数据成员2(改)
- C语言中关于指针的学习
- hdu 4349(Lucas定理)
- leetcode笔记—Decode Ways
- 学生信息管理系统
- 权限树问题
- 实战Walker之侧滑菜单的实现分析
- VBA Study Notes
- VBA Study Notes
- VBA Study Notes
- VBA Study Notes
- VBA Study Notes
- VBA Study Notes
- VBA Study Notes
- div嵌套后margin出现失效(转移)的问题