1、RecyclerView是什么?
根据Googl给出的说明:“能够在有限的窗口中展示大数据集合的灵活视图”
RecyclerView看上去和我们一直用的ListVIew可以说很是一样,但是我们为什么还要用RecyclerView呢?
首先我们先来看一个场景,由于尺寸限制,用户的设备不能以此性展示所有条目,用户需要上下滚动查看会更多的的条目。滚出可见区域的条目将被回收,并在下一条目可见的时候被复用。
在这个场景可以知道,RecyclertView的缓存可以更大的减少内存的开销和CPU的计算,这意味着我们不用每次都创建新的条目,这样就大大的减少了内存开销和CPU的计算。,有效的降低屏幕的卡顿,保证滑动的顺滑。
说了半天你们会说还是没有说明白和LIstView的区别,毕竟我们用ListView很长一段时间了,其实在应用RecyclerView的时候,RecyclerView只管回收和复用View,其他的我们自己去设置。可以根据开发者的需要去设置任何样式的的动画和布局View,可以很轻松的实现LsitView和GirdView,瀑布流的效果。
2、引入RecyclerView
我们在应用RecyclerView的时候,需要添加依赖。
compile 'com.android.support:appcompat-v7:25.3.1' compile 'com.android.support:recyclerview-v7:25.3.1'
在添加依赖的时候这两个依赖包的版本号要统一,否则会报错。
3、在布局文件中应用RecyclerView
<android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" />
4、使用RecyclerView
我们先来了解一下RecyclerView中几个重要的类,都是内部类
Class | 功能 |
Adapter | 处理数据集合并负责绑定数据 |
ViewHolder | 持有所有的用于绑定数据或者需要操作的View |
LayoutManager | 负责摆放视图等相关操作 |
ItemDecoration | 负责绘制Item附近的分割线 |
ItemAnimator | 为Item的一般操作添加动画效果 |
4.1RecyclerView.ViewHolder
这里的ViewHolder和ListVIew里面的ViewHolder差不多是一样的,但是在ListView中,并没有强制我们使用ViewHolder,但是在RecyclerView中,Google强制我们必须使用ViewHolder这种模式。(不了解ViewHolder可以去查一下,网上有很多说明,面试重点)
在RecycleView中的ViewHolder子类可以通过访问公共成员itemView来访问ViewHolder的根视图,所以不需要再ViewHolder子类中存储。
public static class MyViewHolder extends RecyclerView.ViewHolder { TextView tv; public MyViewHolder(View itemView) { super(itemView); tv = (TextView) itemView.findViewById(R.id.tv); } }
4.2、Adapter
adapter有两个功能。一.根据不同Viewtype创建与之相应的Item-Layout,二.访问数据集合并将数据绑定到正确的View上。在实现这两个方面的时候,需要我们重写三个方法:
。public VH onCreateViewHolder(ViewGroup parent,int ViewType)创建Item视图,并返回相应的ViewHolder
。public void onBindViewHolder(VH holder,int position)绑定数据到正确的Item视图上
。public int getItemCount()返回该Adapter所持有的item数量
@Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { MyViewHolder holder = new MyViewHolder(LayoutInflater.from(context) .inflate(R.layout.item_recycler_view, parent, false)); return holder; } @Override public void onBindViewHolder(MyViewHolder holder, final int position) { holder.tv.setText(mDatas.get(position)); } @Override public int getItemCount() { return mDatas.size(); }
4.3、RecyclerView.LayoutManager
LayoutManager的职责是摆放Item的位置,并且负责决定何时回收和重用item。他有一个默认的实现:LinearLayoutManager,他可以用于垂直和水平列表。RecycleVIew.LayoutManager是一个抽象类,RecycleView提供了三个实现类:
1、LinearlayoutManager现行管理器,支持横向,纵向。
2、GridLayoutManager网格布局管理器
3、StaggeredGridLayoutManager瀑布流式布局管理器
4.3.1LinearlayoutManager
LinearlayoutManager是LayoutManager的默认实现。可以使用这个类来创建垂直或水平列表
设置纵向布局
LinearLayoutManager layoutManager= new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager);设置横向布局
LinearLayoutManager layoutManager= new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerView.setLayoutManager(layoutManager);
在LinearLayoutManager中还有一些很实用的API:
。findFirstVisibleItemPosition()返回当前第一个可见的Item的position
。findLastVisibleItemPosition()返回当前最后一个可见的Item的position
。findFirstCompletelyVisibleItemPosition()返回当前的第一个完全可见的Item的position
。findLastCompletelyVisibleItemPosition()返回当前最后一个完全可见Item的position
剩下的两个RecycleView的实现类就不多说了,大家可以查看官方文档,很简单。
4.4、RecyclerView.ItemDecoration
设置:
// 给纵向显示RecyclerView设置分割线 recyclerView.addItemDecoration(new DividerItemDecoration(activity, DividerItemDecoration.VERTICAL)); // 给横向显示RecyclerView设置分割线 recyclerView.addItemDecoration(new DividerItemDecoration(activity,DividerItemDecoration.HORIZONTAL));
当然,我们也可以对RecyclerView设置多个ItemDecoration,列表展示的时候会遍历所有的ItemDecoration并调用里面的绘制方法,对Item进行装饰。
RecyclerView.ItemDecoration也是一个抽象类,重写他的三个方法来实现Item之间的偏移量或者装饰效果:
。public void onDraw(Canvas c,RecyclerView parent)装饰的绘制在Item条目绘制之前调用,所以这有可能被Item的内容所遮挡
。public void onDrawOver(Canvas c,RecyclerView parent)装饰的绘制在Item条目绘制之后调用,因此装饰奖浮于Item之上
。public void getItemOffsets(Rect outRect,int itemPositon,RecyclerView parent)和padding或者margin类似,LayoutManager在测量阶段会调用这个方法,计算出每一个Item的正确尺寸并设置偏移量。
4.5、RecyclerView.ItemAnimator
ItemAnimator能够帮助Item实现独立的动画。我们可以通过以下代码为Item增加动画效果:
recyclerView.setItemAnimator(new DefaultItemAnimator());
在之前我们开发过程中,表中的集合发生改变的时候,我们通过调用.notifyDataSetChanged()来刷新列表,这样做会触发列表重绘,所以并不会出现任何动画效果,因此需要调用一些以notifyItem*()最为作为前缀的特殊方法。
。public final void notifyItemInserted(int position)向指定位置插入Item
。public final void notifyItemRemoved(int position)移除指定位置的Item
。public final void notifyItemChanged(int position)更新指定位置Item
例:
btnAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { adapter.addData(1); } }); btnRemove.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { adapter.removeData(2); } });
在适配器中添加两个方法:
public void addData(int position) { mDatas.add(position, "新添加的"); mHeights.add( (int) (100 + Math.random() * 300)); notifyItemInserted(position); } public void removeData(int position) { mDatas.remove(position); notifyItemRemoved(position); }下面给大家上一下全部代码:
public class MainActivity extends Activity { private Activity activity; private Button btnAdd,btnRemove; private RecyclerView recyclerView; private RecyclerAdapter adapter; private List<String> mDatas = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); activity = this; recyclerView = (RecyclerView) findViewById(R.id.recycler_view); btnAdd = (Button) findViewById(R.id.btn_add); btnRemove = (Button) findViewById(R.id.btn_remove); initDatas(); adapter = new RecyclerAdapter(activity,mDatas); // 设置RecyclerView布局方式为纵向布局 // LinearLayoutManager layoutManager= new LinearLayoutManager(this); // layoutManager.setOrientation(LinearLayoutManager.VERTICAL); // recyclerView.setLayoutManager(layoutManager); // 设置RecyclerView布局方式为横向布局 // LinearLayoutManager layoutManager= new LinearLayoutManager(this); // layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); // recyclerView.setLayoutManager(layoutManager); // 设置纵向的4列的表格布局 // GridLayoutManager layoutManager = new GridLayoutManager(this,4,GridLayoutManager.VERTICAL,false); // recyclerView.setLayoutManager(layoutManager); // recyclerView.addItemDecoration(new DividerGridItemDecoration(activity));l // 设置横向的3行的表格布局 // GridLayoutManager layoutManager = new GridLayoutManager(this,3,GridLayoutManager.HORIZONTAL,false); // recyclerView.setLayoutManager(layoutManager); // 给纵向显示RecyclerView设置分割线 // recyclerView.addItemDecoration(new DividerItemDecoration(activity, DividerItemDecoration.VERTICAL)); // 给横向显示RecyclerView设置分割线 // recyclerView.addItemDecoration(new DividerItemDecoration(activity,DividerItemDecoration.HORIZONTAL)); // 设置纵向的瀑布流布局 StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); // 给RecyclerView设置默认动画效果 recyclerView.setItemAnimator(new DefaultItemAnimator()); adapter.setLitener(new RecyclerAdapter.OnItemClickLitener() { @Override public void onItemClick(View view, int position) { Toast.makeText(activity,"第"+(position+1)+"项被点击了",Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(activity,"第"+(position+1)+"项被长按了",Toast.LENGTH_SHORT).show(); } }); recyclerView.setAdapter(adapter); btnAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { adapter.addData(1); } }); btnRemove.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { adapter.removeData(2); } }); } private void initDatas(){ for (int i = 0;i<50;i++){ mDatas.add(String.valueOf(i+1)); } } }
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> { private Context context; private List<String> mDatas; private List<Integer> mHeights; public RecyclerAdapter(Context context, List<String> mDatas) { this.context = context; this.mDatas = mDatas; mHeights = new ArrayList<Integer>(); for (int i = 0; i < mDatas.size(); i++) { mHeights.add((int) (100 + Math.random() * 300)); } } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { MyViewHolder holder = new MyViewHolder(LayoutInflater.from(context) .inflate(R.layout.item_recycler_view, parent, false)); return holder; } @Override public void onBindViewHolder(MyViewHolder holder, final int position) { ViewGroup.LayoutParams lp = holder.tv.getLayoutParams(); lp.height = mHeights.get(position); holder.tv.setLayoutParams(lp); holder.tv.setText(mDatas.get(position)); // 如果设置了回调,则响应点击事件 holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (litener != null) { litener.onItemClick(v, position); } } }); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { if (litener != null) { litener.onItemLongClick(v, position); } return false; } }); } @Override public int getItemCount() { return mDatas == null ? 0 : mDatas.size(); } public static class MyViewHolder extends RecyclerView.ViewHolder { TextView tv; public MyViewHolder(View itemView) { super(itemView); tv = (TextView) itemView.findViewById(R.id.tv); } } public void addData(int position) { mDatas.add(position, "新添加的"); mHeights.add( (int) (100 + Math.random() * 300)); notifyItemInserted(position); } public void removeData(int position) { mDatas.remove(position); notifyItemRemoved(position); } public interface OnItemClickLitener { void onItemClick(View view, int position); void onItemLongClick(View view, int position); } private OnItemClickLitener litener; public void setLitener(OnItemClickLitener litener) { this.litener = litener; } }
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; private Drawable mDivider; public DividerGridItemDecoration(Context context) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent, State state) { drawHorizontal(c, parent); drawVertical(c, parent); } 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; } 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); } } 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; } 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; } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); int childCount = parent.getAdapter().getItemCount(); if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部 { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边 { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } } }