RecyclerView知识点串讲
来源:互联网 发布:linux 编译打包war 编辑:程序博客网 时间:2024/05/18 00:18
RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好。
概述
RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。
据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView、GridView。
那么有了ListView、GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。
- 你想要控制其显示的方式,请通过布局管理器LayoutManager
- 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
- 你想要控制Item增删的动画,请通过ItemAnimator
- 你想要控制点击、长按事件,请自己写(擦,这点尼玛。)
基本使用
RecyclerView 与 ListView、GridView 类似,都是可以显示同一种类型 View 的集合的控件。
首先看看最简单的用法,四步走:
接入 build.gradle 文件中加入
compile 'com.android.support:recyclerview-v7:24.0.0'
创建对象
RecyclerView recyclerview = (RecyclerView) findViewById(R.id.recyclerview);
设置显示规则
recyclerview.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
RecyclerView 将所有的显示规则交给一个叫 LayoutManager 的类去完成了。
LayoutManager 是一个抽象类,系统已经为我们提供了三个默认的实现类,分别是:- LinearLayoutManager
- GridLayoutManager
- StaggeredGridLayoutManager
从名字我们就能看出来了,分别是,线性显示、网格显示、瀑布流显示。当然你也可以通过继承这些类来扩展实现自己的 LayougManager。
设置适配器
recyclerview.setAdapter(adapter);
适配器,同 ListView 一样,用来设置每个item显示内容的。
通常,我们写 ListView 适配器,都是首先继承 BaseAdapter,实现四个抽象方法,创建一个静态 ViewHolder , getView() 方法中判断 convertView 是否为空,创建还是获取 viewholder 对象。
而 RecyclerView 也是类似的步骤,首先继承RecyclerView.Adapter类,实现三个抽象方法,创建一个静态的 ViewHolder。不过 RecyclerView 的 ViewHolder 创建稍微有些限制,类名就是上面继承的时候泛型中声明的类名(好像反了,应该是上面泛型中的类名应该是这个holder的类名);并且 ViewHolder 必须继承自RecyclerView.ViewHolder类。
相比较于ListView的代码,ListView可能只需要去设置一个adapter就能正常使用了。而RecyclerView基本需要上面一系列的步骤,那么为什么会添加这么多的步骤呢?
那么就必须解释下RecyclerView的这个名字了,从它类名上看,RecyclerView代表的意义是,我只管Recycler View,也就是说RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)。
MainActivity.java
public class MainActivity extends Activity {private RecyclerView recyclerView;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取RecyclerView控件 recyclerView = (RecyclerView) findViewById(R.id.recyclerView); //设置显示规则 recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); //设置适配器 final MyAdapter myAdapter = new MyAdapter(); myAdapter.setOnItemClickListener(new MyAdapter.OnItemClickLitener() { @Override public void onItemClick_Add(View view, List<String> datas, int position) { datas.add(position-1, "hello"); myAdapter.notifyItemInserted(position); } @Override public void onItemClick_Delete(View view, List<String> datas, int position) { datas.remove(position); myAdapter.notifyItemRemoved(position); } }); recyclerView.setAdapter(myAdapter); //设置分割线 MyItemDecoration divider = new MyItemDecoration(new ColorDrawable(0xffff0000), OrientationHelper.VERTICAL); divider.setMargin(50,50,50,50); divider.setHeight(20); recyclerView.addItemDecoration(divider);}}
MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {/** * 定义RecyclerView的列表类型 */private static final int TYPE_HEADER = 1;private static final int TYPE_ITEM = 2;private static final int TYPE_FOOTER = 3;/** * OnItemClick点击事件 */public interface OnItemClickLitener { void onItemClick_Add(View view, List<String> datas, int position); void onItemClick_Delete(View view, List<String> datas, int position);}private OnItemClickLitener mOnItemClickLitener;public void setOnItemClickListener(OnItemClickLitener onItemClickLitener) { mOnItemClickLitener = onItemClickLitener;}private List<String> datas;public MyAdapter() { datas = new ArrayList(2); datas.add("01"); datas.add("02"); datas.add("03"); datas.add("04"); datas.add("05");}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); switch (viewType) { case TYPE_HEADER: return new HeadViewHolder(inflater.inflate(R.layout.layout_recycler_head, parent, false)); case TYPE_ITEM: return new ItemViewHolder(inflater.inflate(R.layout.layout_recycler_item, parent, false)); case TYPE_FOOTER: return new FooterViewHolder(inflater.inflate(R.layout.layout_recycler_footer, null)); } return null;}@Overridepublic void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { if (holder instanceof HeadViewHolder) { ((HeadViewHolder) holder).mTextView.setText("我是一个Header"); } else if (holder instanceof FooterViewHolder) { ((FooterViewHolder) holder).mTextView.setText("我是一个Footer"); } else { ((ItemViewHolder)holder).txtInfo.setText(datas.get(position-1)); ((ItemViewHolder) holder).btnAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemClick_Add(v, datas, pos); } }); ((ItemViewHolder) holder).btnDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemClick_Delete(v, datas, pos); } }); }}@Overridepublic int getItemCount() { return datas.size() + 2;}@Overridepublic int getItemViewType(int position) { if (position == 0) { return TYPE_HEADER; } else if (position == datas.size() + 1) { return TYPE_FOOTER; } return TYPE_ITEM;}//自定义的ViewHolder,持有每个Item的的所有界面元素public static class ItemViewHolder extends RecyclerView.ViewHolder { public TextView txtInfo; public Button btnAdd; public Button btnDelete; public ItemViewHolder(View view) { super(view); txtInfo = (TextView) view.findViewById(R.id.txtInfo); btnAdd = (Button) view.findViewById(R.id.btnAdd); btnDelete = (Button) view.findViewById(R.id.btnDelete); }}public static class HeadViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public HeadViewHolder(View view) { super(view); mTextView = (TextView) view.findViewById(R.id.txtHeader); }}public static class FooterViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public FooterViewHolder(View view) { super(view); mTextView = (TextView) view.findViewById(R.id.txtFooter); }}}
MyItemDecoration.java
public class MyItemDecoration extends RecyclerView.ItemDecoration {private Drawable mDivider;private int leftMargin, rightMargin, topMargin, bottomMargin;private int width, height;private int mOrientation;public MyItemDecoration(Drawable divider, int orientation) { setDivider(divider); setOrientation(orientation);}private void setDivider(Drawable divider) { this.mDivider = divider; if (mDivider == null) { mDivider = new ColorDrawable(0xffff0000); } width = mDivider.getIntrinsicWidth(); height = mDivider.getIntrinsicHeight();}private void setOrientation(int orientation) { if (orientation != LinearLayoutManager.HORIZONTAL && orientation != LinearLayoutManager.VERTICAL) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation;}public void setMargin(int left, int top, int right, int bottom) { this.leftMargin = left; this.topMargin = top; this.rightMargin = right; this.bottomMargin = bottom;}public void setHeight(int height) { this.height = height;}public void setWidth(int width) { this.width = width;}public int getHeight() { return height;}public int getWidth() { return width;}@Overridepublic void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); if (mOrientation == LinearLayoutManager.HORIZONTAL) { drawHorizontal(c, parent); } else { drawVertical(c, parent); }}public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop() + topMargin; final int bottom = parent.getHeight() - parent.getPaddingBottom() - bottomMargin; 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 + leftMargin; final int right = left + width; mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); }}public void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft() + leftMargin; final int right = parent.getWidth() - parent.getPaddingRight() - rightMargin; 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.getBottom() + params.bottomMargin + topMargin; final int bottom = top + height; mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); }}@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); if (mOrientation == LinearLayoutManager.HORIZONTAL) { outRect.set(0, 0, leftMargin + width + rightMargin, 0); } else { outRect.set(0, 0, 0, topMargin + height + bottomMargin); }}}
layout_recycler_head.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFFF00" android:orientation="horizontal" > <TextView android:id="@+id/txtHeader" android:text="Header" android:layout_width="match_parent" android:layout_height="175dp" android:textColor="#ff000000" android:gravity="center" android:textSize="20sp" /></LinearLayout>
- yout_recycler_item.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <TextView android:id="@+id/txtInfo" android:layout_width="0dp" android:layout_height="80dp" android:layout_weight="1" android:textColor="#ff000000" android:gravity="center" android:layout_gravity="center" android:text="Hello" android:textSize="20sp" /> <Button android:id="@+id/btnAdd" android:text="新增" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1"/> <Button android:id="@+id/btnDelete" android:text="删除" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1"/></LinearLayout>
- layout_recycler_footer.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFFF00" android:orientation="horizontal"> <TextView android:id="@+id/txtFooter" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="Footer" android:textColor="#ff000000" android:textSize="20sp"/></LinearLayout>
RecyclerView显示Item布局不一致
在自定义RecyclerAdapter的时候,在重写onCreateViewHolder方法是使用了
@Override public H onCreateViewHolder(ViewGroup parent, int viewType) { View view=View.inflate(context,layoutId,null); return view; }
进行生成布局,结果发现生成的布局没有LayoutParams。以前自定义View的时候发现,LayoutParams是由于ViewGroup生成的,因为这里添加的ViewGroup为null。所以并不会生成LayoutParams。结果在RecyclerView的getViewForPosition方法中检查了有没有LayoutParams如果没有的话就调用LayoutManager的generateDefaultLayoutParams生成默认的LayoutParames。代码段如下:
final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); final LayoutParams rvLayoutParams; if (lp == null) { rvLayoutParams = (LayoutParams) generateDefaultLayoutParams(); holder.itemView.setLayoutParams(rvLayoutParams); } else if (!checkLayoutParams(lp)) { rvLayoutParams = (LayoutParams) generateLayoutParams(lp); holder.itemView.setLayoutParams(rvLayoutParams); } else { rvLayoutParams = (LayoutParams) lp; }
而在LinearLayoutManager中generateDefaultLayoutParams方法实现如下。
/** * {@inheritDoc} */ @Override public LayoutParams generateDefaultLayoutParams() { return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); }
最终会造成RecycleView的显示效果与布局文件不一致。后来使用了LayoutInflater来填充布局。
@Override public H onCreateViewHolder(ViewGroup parent, int viewType) { View view = mInflater.inflate(layoutId, parent, false); return getInstanceOfH(view); }
查看LayoutInflater源码发现inflate最后的参数如果是false的话就不会将生成的View添加到parent。但是会根据parent产生相应的LayoutParams 。源码如下:
* @param attachToRoot Whether the inflated hierarchy should be attached to * the root parameter? If false, root is only used to create the * correct subclass of LayoutParams for the root view in the XML.
因为在onCreateViewHolder中产生的View不能由我们手动添加到RecycleView中所以最后的参数只能是false;
吐槽
OnItemTouchListener 什么鬼?
用习惯了 ListView 的 OnItemClickListener ,RecyclerView 你的 OnItemClickListener 呢?
Tell me where do I find, something like ListView listener ?
好吧,翻遍了 API 列表,就找到了个 OnItemTouchListener ,这特么什么鬼,我干嘛要对每个 item 监听触摸屏事件。
万万没想到,最终我还是在 Google IO 里面的介绍找到了原因。原来是 Google 的工程师分不清究竟是改给 listview 的 item 添加点击事件,还是应该给每个 item 的 view 添加点击事件,索性就不给 OnItemClickListener 了,然后在 support demo 里面,你就会发现,RecyclerView 的 item 点击事件都是写在了 adapter 的 ViewHolder 里面。
RecyclerView是一个高度自由的控件,它把控件位置的摆放交给LayoutManager去管理,RecyclerView的数据交给Adapter类去管理,RecyclerView的分割线交给RecyclerView.ItemDecoration的实现类去管理(需用户自己实现,系统未给出默认实现类),
- RecyclerView知识点串讲
- iOS-C语言知识点串讲
- JAVA SE重点知识点串讲总结_panfanglin
- 由三行代码串讲嵌入式知识点
- 【solr基础教程之一】Solr相关知识点串讲
- 【solr基础教程之一】Solr相关知识点串讲
- 【solr基础教程之一】Solr相关知识点串讲
- iOS-OC语言知识点串讲导图
- 由三行代码串讲嵌入式知识点
- RecyclerView的知识点
- RecyclerView知识点小解
- Android进阶知识点(RecyclerView)
- java中级-8-IO流知识点串讲(2)-字节流及字节字符转换流
- Oracle串讲
- CMMI串讲
- 业务串讲
- 串讲2
- 框架串讲
- LeetCode之通过二叉树的中序遍历和后序遍历还原二叉树
- 全景导航板子操作步骤
- GreenDao的简单使用说明(二)单表的增,删,改,查
- 查看mysql版本的四种方法
- Quartz 框架(一)
- RecyclerView知识点串讲
- 暑期dp46道(4)
- c++学习笔记(四):函数按指针调用和按引用调用
- 一篇通俗易懂的讲解OpenGL ES的文章,opengles
- [c#笔记]Winform TextBox中只能输入数字的几种常用方法(C#)
- linux中的/dev/fd详解(linux3.2.0)
- Rxjava解析
- Linux上几个可以影响到服务器并发处理性能的系统参数
- Android中的AsnycTask