Android RecyclerView 使用
来源:互联网 发布:鲶鱼墨水 知乎 编辑:程序博客网 时间:2024/06/06 05:46
RecyclerView已经出来很久了,工作当中用android原生控件比较少,而且由于项目基于android的版本的也是比较旧的,所以android新出的各种技术在工作中都用不到,平时也只有工作之余写demo的时候去学习学习,这里就对RecyclerView的使用做一下笔记。
在RecyclerView之前,我们用ListView或者GridView来显示庞大数据集,屏幕之外的内容可以滚动该视图变得可见。其中ListView是垂直显示,GridView是网格显示,而且我们还知道虽然ListView和GridView虽然显示很多数据,但是不会出现内存溢出,这是因为它们里面只会存在有限个子视图。关于ListView和GridView这里就不细说了,这两个控件很重要,用的也比较多。
既然官方推出了RecyclerView ,相信RecyclerView一定有特别之处。
下面就跟着一个小demo,一边理解RecyclerView的使用,一边感受RecyclerView的特别之处。
首先记得添加依赖库
compile 'com.android.support:recyclerview-v7:24.2.1'
布局文件:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="cj.com.recyclerviewdemo.RecyclerViewActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/my_recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content"/></RelativeLayout>
看主要代码:
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); // use this setting to improve performance if you know that changes // in content do not change the layout size of the RecyclerView mRecyclerView.setHasFixedSize(true); // use a linear layout manager mLayoutManager = new LinearLayoutManager(this); mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); mLayoutManager.setReverseLayout(false);//true 从底部开始填充子视图 mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.addItemDecoration(new DividerItemDecoration(this,LinearLayoutManager.VERTICAL)); mAdapter = new MyAdapter(datas); mRecyclerView.setAdapter(mAdapter);
跟ListView它们相比,RecyclerView使用稍微复杂一些,因为多了一些新对象比如上面的LinearLayoutManager, DividerItemDecoration,那么首先来了解一下。
RecyclerView有一个函数:
setLayoutManager(RecyclerView.LayoutManager layout)
来看一下RecyclerView.LayoutManager的类结构:
有三个子类,这里先说这一下这类的作用,效果后面看
LayoutManager就理解为布局管理器吧,像ListView或者GridView它们的子视图在它们当中布局已经定死了,ListView就垂直显示,GridView就是网格显示,这样如果要需要显示两种不同的效果,则就需要用到这两个控件,而RecyclerView就灵活点了,子视图在它里面怎么布局都由LayoutManager去控制,只需要通过setLayoutManager()函数给
RecyclerView设置一个布局管理器即可,比如上面android已有布局管理器:
LinearLayoutManager(线性布局管理器),添加这个布局管理器的话,可以设置方向,通过setOrientation()函数设置,参数可以是水平或者垂直,如果是垂直的话,那就跟ListView一模一样了。等下看demo演示效果。
GridLayoutManager(网格布局管理器)它是LinearLayoutManager的子类,也可以设置方向,水平或垂直,垂直方向的话就跟GridView一模一样了。等下看demo演示效果。
StaggeredGridLayoutManager(瀑布流布局管理器),这个之前用ListView或者GridView没法实现,需要自定义,现在通过RecyclerView可以很简单实现,等下看效果。
怎么样,看到上面这些介绍,是不是觉得RecyclerView功能很强大了。
接着往下看DividerItemDecoration:
RecyclerView下面这个函数
addItemDecoration (RecyclerView.ItemDecoration decor)是来装饰它的子视图的。这里我是用来给子视图添加一条分割线,因为RecyclerView默认没有分割线了,我们在使用使用ListView的给ListView添加分割线很简单,例如:
<ListView android:dividerHeight="2dp" android:divider="@color/colorPrimaryDark" android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView>
现在是不是觉得RecyclerView添加分割线好复杂的,其实不然,RecyclerView这样更灵活了,因为RecyclerView.ItemDecoration给子视图装饰,不仅仅可以添加分割线,还可以随意添加其他装饰,后面看ItemDecoration的内部函数就知道。所以说RecyclerView功能更丰富了
跟ListView它们一样,RecyclerView也需要关联一个Adapter,这个Adapter负责提供数据集。
只不过RecyclerView关联的Adapter需要继承RecyclerView.Adapter而且该Adapter还需要加一个泛型继承android.support.v7.widget.RecyclerView.ViewHolder
先看代码:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private ArrayList<Contact> mDataset; // Provide a reference to the views for each data item // Complex data items may need more than one view per item, and // you provide access to all the views for a data item in a view holder public class ViewHolder extends RecyclerView.ViewHolder { // each data item is just a string in this case public TextView name; public TextView number; public View View; public ViewHolder(View v) { super(v); View = v; name = (TextView) v.findViewById(R.id.name); number = (TextView) v.findViewById(R.id.number); } } // Provide a suitable constructor (depends on the kind of dataset) public MyAdapter(ArrayList<Contact> myDataset) { mDataset = myDataset; } // Create new views (invoked by the layout manager) @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { Log.d("test","onCreateViewHolder viewType="+viewType); // create a new view View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_item_layout, parent, false); // set the view's size, margins, paddings and layout parameters // ... ViewHolder vh = new ViewHolder(v); Log.d("test","onCreateViewHolder ViewHolder="+vh); return vh; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder holder, int position) { // - get element from your dataset at this position // - replace the contents of the view with that element Log.d("test","onBindViewHolder holder="+holder+ " position="+position); holder.name.setText(mDataset.get(position).getName()); holder.number.setText(mDataset.get(position).getNumber()); } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { Log.d("test","getItemCount"); return mDataset.size(); } @Override public int getItemViewType(int position) { Log.d("test","getItemViewType position="+position); return super.getItemViewType(position); } @Override public long getItemId(int position) { Log.d("test","getItemId position="+position); return super.getItemId(position); } }
RecyclerView.Adapter跟我们使用ListView用的BaseAdapter区挺大的
BaseAdapter主要重写下面两个函数:
public int getCount()public View getView(int position, View convertView, ViewGroup parent)
RecyclerView.Adapter主要重写下面三个函数:
public int getItemCount() public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) public void onBindViewHolder(ViewHolder holder, int position)
上面提到了RecyclerView.ViewHolder,关于ViewHolder,我们也在ListView的中用到过,它的作用是来优化ListView的性能,减少去查找控件次数,使用一般如下:
class MyAdapter extends BaseAdapter{ private Context context; public MyAdapter(Context context){ this.context = context; } @Override public int getCount() { return datas.size(); } @Override public Object getItem(int position) { return datas.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; Log.d("test"," position="+position+"convertView="+convertView); if(convertView == null){ convertView = LayoutInflater.from(context).inflate(R.layout.list_item_layout, null); Log.d("test","==null position="+position+"convertView="+convertView); TextView name = (TextView) convertView.findViewById(R.id.name); TextView number = (TextView) convertView.findViewById(R.id.number); viewHolder = new ViewHolder(); viewHolder.name = name; viewHolder.number = number; convertView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.name.setText(datas.get(position).getName()); viewHolder.number.setText(datas.get(position).getNumber()); return convertView; } class ViewHolder{ private TextView name; private TextView number; } }
可以看到ViewHolder是我们自己定义的。
而在RecyclerView中已经提供RecyclerView.ViewHolder,我们只需继承该类即可。
然后我们说ListView的它们内部只会有有限个子View,因为新出现的View会复用移出屏幕的View,主要通过下面这个函数来体现的:
public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; Log.d("test"," position="+position+"convertView="+convertView); if(convertView == null){ convertView = LayoutInflater.from(context).inflate(R.layout.list_item_layout, null); Log.d("test","==null position="+position+"convertView="+convertView); TextView name = (TextView) convertView.findViewById(R.id.name); TextView number = (TextView) convertView.findViewById(R.id.number); viewHolder = new ViewHolder(); viewHolder.name = name; viewHolder.number = number; convertView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.name.setText(datas.get(position).getName()); viewHolder.number.setText(datas.get(position).getNumber()); return convertView; }
如果convertView不为null,就表示有废弃的View可以复用,这个时候只需要修改相应的数据即可,无需创建新的视图。
在RecyclerView.Adapter中看一下下面两个函数:
// Create new views (invoked by the layout manager) @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { Log.d("test","onCreateViewHolder viewType="+viewType); // create a new view View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_item_layout, parent, false); // set the view's size, margins, paddings and layout parameters // ... ViewHolder vh = new ViewHolder(v); Log.d("test","onCreateViewHolder ViewHolder="+vh); return vh; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder holder, int position) { // - get element from your dataset at this position // - replace the contents of the view with that element Log.d("test","onBindViewHolder holder="+holder+ " position="+position); holder.name.setText(mDataset.get(position).getName()); holder.number.setText(mDataset.get(position).getNumber()); }
这里可以理解RecyclerView内部复用机制是复用ViewHolder。
onCreateViewHolder()这个函数在滚动RecyclerView时候不一定会调用,只要有子View被移出屏幕,滚进来的View显示,就不会调用onCreateViewHolder()该函数,只会调用onBindViewHolder来重新设置数据。后面通过log可以发现。当然这里讲ListView和RecyclerView都只考虑它们的子View都属于一个Type,也就是他们布局都一样。即使布局一样,有多个类型,它们也会复用,这里就没再讨论了。
好了,主要代码就讲这么多,看一下效果:
这里用的RecyclerView.LayoutManager是LinearLayoutManager,方向设置是垂直的,所以效果跟ListView一样。
看一下log
D/test: getItemCountD/test: getItemCountD/test: getItemCountD/test: getItemViewType position=0D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{584e7bc position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{584e7bc position=0 id=-1, oldPos=-1, pLpos:-1 no parent} position=0D/test: getItemCountD/test: getItemViewType position=1D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{f9a2345 position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{f9a2345 position=1 id=-1, oldPos=-1, pLpos:-1 no parent} position=1D/test: getItemCountD/test: getItemViewType position=2D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{27be3a9a position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{27be3a9a position=2 id=-1, oldPos=-1, pLpos:-1 no parent} position=2D/test: getItemCountD/test: getItemViewType position=3D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{ef60fcb position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{ef60fcb position=3 id=-1, oldPos=-1, pLpos:-1 no parent} position=3D/test: getItemCountD/test: getItemViewType position=4D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{1996c3a8 position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{1996c3a8 position=4 id=-1, oldPos=-1, pLpos:-1 no parent} position=4D/test: getItemCountD/test: getItemViewType position=5D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{10beaec1 position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{10beaec1 position=5 id=-1, oldPos=-1, pLpos:-1 no parent} position=5D/test: getItemCountD/test: getItemViewType position=6D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{24b56666 position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{24b56666 position=6 id=-1, oldPos=-1, pLpos:-1 no parent} position=6D/test: getItemCountD/test: getItemViewType position=7D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{3f53dda7 position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{3f53dda7 position=7 id=-1, oldPos=-1, pLpos:-1 no parent} position=7D/test: getItemCountD/test: getItemViewType position=8D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{1a687254 position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{1a687254 position=8 id=-1, oldPos=-1, pLpos:-1 no parent} position=8D/test: getItemCountD/test: getItemCountD/test: getItemCountD/test: getItemViewType position=0D/test: getItemCountD/test: getItemViewType position=1D/test: getItemCountD/test: getItemViewType position=2D/test: getItemCountD/test: getItemViewType position=3D/test: getItemCountD/test: getItemViewType position=4D/test: getItemCountD/test: getItemViewType position=5D/test: getItemCountD/test: getItemViewType position=6D/test: getItemCountD/test: getItemViewType position=7D/test: getItemCountD/test: getItemViewType position=8D/OpenGLRenderer: endAllStagingAnimators on 0x7f7a642c2800 (RippleDrawable) with handle 0x7f7a6550ff40D/test: getItemCountD/test: getItemViewType position=9D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{124675fd position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{124675fd position=9 id=-1, oldPos=-1, pLpos:-1 no parent} position=9D/test: getItemCountD/test: getItemViewType position=10D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{3ea182f2 position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{3ea182f2 position=10 id=-1, oldPos=-1, pLpos:-1 no parent} position=10D/test: getItemCountD/test: getItemViewType position=11D/test: onCreateViewHolder viewType=0D/test: onCreateViewHolder ViewHolder=ViewHolder{19d6ad43 position=-1 id=-1, oldPos=-1, pLpos:-1 unbound no parent}D/test: onBindViewHolder holder=ViewHolder{19d6ad43 position=11 id=-1, oldPos=-1, pLpos:-1 no parent} position=11D/test: getItemCountD/test: getItemViewType position=12D/test: onBindViewHolder holder=ViewHolder{584e7bc position=12 id=-1, oldPos=-1, pLpos:-1 no parent} position=12D/test: getItemCountD/test: getItemViewType position=13D/test: onBindViewHolder holder=ViewHolder{f9a2345 position=13 id=-1, oldPos=-1, pLpos:-1 no parent} position=13
可以发现滚动RecyclerView时从下标为12开始也就是第13个item开始就没有调用onCreateViewHolder()了,因为这时已经有itme已经完全移出屏幕了,可以复用了。
这里设置了DividerItemDecoration,我们来看一下它的实现:
首先看RecyclerView的这个函数:
addItemDecoration(RecyclerView.ItemDecoration decor)看一下RecyclerView.ItemDecoration的类结构:
可以发现DividerItemDecoration是它的一个子类,但是android SDK中并没有包含该类的API
下载DividerItemDecoration源码点击这里
看一下它的实现:
package cj.com.recyclerviewdemo;/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */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.util.Log;import android.view.View;/** * This class is from the v7 samples of the Android SDK. It's not by me! * <p/> * See the license above for details. */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);//mDivider = context.getResources().getDrawable(R.drawable.divider);a.recycle();setOrientation(orientation);}public void setOrientation(int orientation){if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){throw new IllegalArgumentException("invalid orientation");}mOrientation = orientation;}@Overridepublic void onDraw(Canvas c, RecyclerView parent){//Log.v("test", "onDraw"); 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);//RecyclerView v = new 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);}}@Overridepublic void getItemOffsets(Rect outRect, int itemPosition,RecyclerView parent){//Log.v("test", "getItemOffsets");if (mOrientation == VERTICAL_LIST){outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());} else{outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);}}}
注意该DividerItemDecoration只能用于布局管理器是LinearLayoutManager时
mDivider = a.getDrawable(0);
divider是一个drawable,这里drawable就直接使用android默认的listView的分割线,也就属性listDivider对应的drawable。
当然这里可以改成我们自己想要的drawable:
mDivider = context.getResources().getDrawable(R.drawable.divider);
这里可以在修改主题中listDivider属性的值,引用该drawable。
看一下divider.xml文件<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@color/colorPrimaryDark"/> <size android:height="3dp" android:width="3dp"/></shape>
看一下效果:
继续看上面的DividerItemDecoration
重写两个重要函数:
public void getItemOffsets(Rect outRect, int itemPosition,RecyclerView parent)
public void onDraw(Canvas c, RecyclerView parent)
getItemOffsets这个函数用于获取item的偏移量
onDraw函数就开始绘制item的装饰,上面函数偏移量的位置就用来绘制装饰
if (mOrientation == VERTICAL_LIST){outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());} else{outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);}
比如这里是垂直布局,所以只需要在竖直方法有一定的偏移量来绘制divider,这里设置距离也就是divider(drawable)的内部高度,也就是drawable的高度了。
关于上面绘制divider没讲了,等下会根据这个给StaggeredGridLayoutManager,GridLayoutManager绘制分割线,再讲ondraw函数
将LinearLayoutManager设置为水平方向看看,改一下代码
mLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,LinearLayoutManager.HORIZONTAL));
既然上面都提到了有三种布局管理器,咱们都来看一下效果:
GridLayoutManager ,使用GridLayoutManager,那么就不能使用上面的DividerItemDecoration来绘制分割线了,咱先看没有分割线的效果
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,4); gridLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(gridLayoutManager);4表示有4列,方向为垂直方法
效果跟GridView一样,不过没有分割线,有点不好分辨,所以咱们就跟根据DividerItemDecoration重新来为GridLayoutManager 垂直方向写一个ItemDecoration,来绘制分割线:
可以发现GridLayoutManager下方和右边都应该绘制分割线
看代码:
package cj.com.recyclerviewdemo;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.util.Log;import android.view.View;/** * */public class DividerItemDecoration1 extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[] { android.R.attr.listDivider }; private Drawable mDivider; public DividerItemDecoration1(Context context){ final TypedArray a = context.obtainStyledAttributes(ATTRS); // mDivider = a.getDrawable(0); mDivider = context.getResources().getDrawable(R.drawable.divider); a.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent){ Log.v("test", "onDraw"); draw(c, parent); } public void draw(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 left = child.getLeft(); final int right = child.getRight(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom);//水平方向 mDivider.draw(c); final int left1 = child.getRight()+params.rightMargin; final int right1 = left1+mDivider.getIntrinsicWidth(); final int top1 = child.getTop()+params.topMargin; final int bottom1 = child.getBottom()+params.bottomMargin; mDivider.setBounds(left1, top1, right1, bottom1);//垂直方向 mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { Log.v("test", "getItemOffsets"); outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDrawOver(c, parent, state); }}
先看这简单粗暴的:
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { Log.v("test", "getItemOffsets"); outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); }
现在item的下方和右边都应该要有偏移量来绘制分割线,就以分割线的宽度高度了
然后看draw()函数
public void draw(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 left = child.getLeft(); final int right = child.getRight(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom);//水平方向 mDivider.draw(c); final int left1 = child.getRight()+params.rightMargin; final int right1 = left1+mDivider.getIntrinsicWidth(); final int top1 = child.getTop()+params.topMargin; final int bottom1 = child.getBottom()+params.bottomMargin; mDivider.setBounds(left1, top1, right1, bottom1);//垂直方向 mDivider.draw(c); } }
这里代码应该不难,首先绘制水平的分割线,然后绘制竖直分割线,暂时这样,先看效果图:
mRecyclerView.addItemDecoration(new DividerItemDecoration1(this));
看效果貌似还可以,但是发现还有些许问题,每一行最后一个位置没必要竖直方向的分割线了而且四个item的交界处有空隙,最后一行貌似也没必要绘制水平分割线
咱们再改改
package cj.com.recyclerviewdemo;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.util.Log;import android.view.View;/** * */public class DividerItemDecoration1 extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[] { android.R.attr.listDivider }; private Drawable mDivider; private int spanCount; public DividerItemDecoration1(Context context,int spanCount){ final TypedArray a = context.obtainStyledAttributes(ATTRS); // mDivider = a.getDrawable(0); mDivider = context.getResources().getDrawable(R.drawable.divider); this.spanCount = spanCount; a.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent){ Log.v("test", "onDraw"); draw(c, parent); } public void draw(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 left = child.getLeft(); final int right = child.getRight()+mDivider.getIntrinsicWidth(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom);//水平方向 mDivider.draw(c); final int left1 = child.getRight()+params.rightMargin; final int right1 = left1+mDivider.getIntrinsicWidth(); final int top1 = child.getTop()+params.topMargin; final int bottom1 = child.getBottom()+params.bottomMargin; mDivider.setBounds(left1, top1, right1, bottom1);//垂直方向 mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { Log.v("test", "getItemOffsets"); final int itemCount = parent.getAdapter().getItemCount(); if(isLastOnRow(itemPosition,itemCount)){//是不是每一行的最后一个 if(isLastOnLastRow(itemPosition,itemCount)){//刚好整数行整数列 最后一个 水平垂直都不要绘制 outRect.set(0, 0, 0, 0);//竖直方向需要绘制 }else{ outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());//水平方向需要绘制 } }else if(isLastOnLastRow(itemPosition,itemCount)){//最后一个 不够spanCount个数 outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);//竖直方向需要绘制 }else if(isLastRow(itemPosition,itemCount)){//最后一行 outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);//竖直方向需要绘制 }else{ outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());//水平竖直方向需要绘制 } } private boolean isLastRow(int itemPosition,int itemCount){ int row = itemCount/spanCount; int r = itemCount%spanCount; if(r == 0){ if(itemPosition/spanCount == (row-1)){ return true; }else { return false; } }else{ if((itemPosition+1) > row*spanCount){ return true; }else{ return false; } } } private boolean isLastOnLastRow(int itemPosition,int itemCount){ return (itemPosition+1) == itemCount; } private boolean isLastOnRow(int itemPosition,int itemCount){ return (itemPosition+1)%spanCount == 0; } @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDrawOver(c, parent, state); }}
多了些判断,看效果:
好看多了。不过这里我只是针对GridLayoutManager 垂直方向做了修改,应用水平方向可能又不一样了,所以可以写一个通用的,根据管理器不同使用不同的显示效果,这里没有细说了,只要知道如何去修改就可以了
GridLayoutManager也有水平和竖直方向之分,这里就没演示了。
接着看另外一种管理器StaggeredGridLayoutManager
同样可以设置方向和相应的列数或者函数
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
瀑布流,就是每个item的宽高可能不一样
关于RecyclerView.LayoutManager就讲这些了。
相比ListView,RecyclerView没有类似
setOnItemClickListener
这种函数来监听item的点击事件。需要自己去设置监听例如:
@Override public void onBindViewHolder(final ViewHolder holder, int position) { // - get element from your dataset at this position // - replace the contents of the view with that element Log.d("test","onBindViewHolder holder="+holder+ " position="+position); holder.View.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //.....处理 } }); holder.name.setText(mDataset.get(position).getName()); holder.number.setText(mDataset.get(position).getNumber()); }这里的处理大家有不同处理,就不再说了
最后提几个RecyclerView.Adapter的API:
notifyDataSetChanged() 通知数据变化了 notifyItemChanged(int position) 某一个item发生变化了notifyItemInserted(int position) 在某个位置添加一个itemnotifyItemRemoved(int position)删除某个位置上的item这里没演示了,添加数据和删除数据都有动画效果,通过下面这个函数可以添加自定义动画
setItemAnimator(RecyclerView.ItemAnimator animator)Sets the RecyclerView.ItemAnimator that will handle animations involving changes to the items in this RecyclerView.
- Android 5.0 RecyclerView 使用
- Android RecyclerView使用
- Android RecyclerView 使用详解
- Android -- RecyclerView的使用
- Android RecyclerView 使用解析
- Android之RecyclerView使用
- Android RecyclerView使用介绍
- Android RecyclerView的使用
- Android开发--RecyclerView使用
- Android RecyclerView的使用
- android中使用RecyclerView
- Android RecyclerView的使用
- Android RecyclerView的使用
- Android RecyclerView 使用解析
- Android RecyclerView使用Demo
- [Android]使用RecyclerView
- Android RecyclerView基本使用
- Android RecyclerView使用详解
- Linux系统下怎么安装.deb文件?
- 高性能网站建设方法集锦
- 使用silix做序列聚类
- 如何安排上班时间之外的时间
- Xcode8创建CoreData托管对象文件,编译报错
- Android RecyclerView 使用
- 微信公众平台开发 数据库操作
- Mybatis 示例之 SelectKey
- OpenCV 初步使用
- 通达OA中 关于msql使用存储过程控制执行的总体成功与否
- 单点登录cas常见问题系列汇总
- Android Duplicate files copied in APK
- ionic 实现html的两种方式
- spring的controller默认是单例还是多例