Android-RecyclerView
众所周知,RecyclerView是Google公司推出的V7包中的一个重要的控件,非常方便,可以替代现有的ListView和Gridview等控件,它功能很强大,灵活性好,扩展性强,还自带VIewHolder,不再需要自己去写,这点非常方便,缺点也是有的,那就是比较原始,因为灵活性好,所以封装的比较浅,原汁原味,所以它不具备点击事件和长按事件等。
下面先来看看Recycler的简单的使用:
直接上代码:
public class MainActivity extends AppCompatActivity { private RecyclerView rv;
private List<ItemEntity> rvList; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvList = initRvList();
rv = (RecyclerView) findViewById(R.id.rv);
//默认的设置一个纵向的LinearLayoutManage,这一步,不能忘,很重要
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(new RecyclerViewAdapter(this, rvList));
} public class ItemEntity {
private int id;
private String name; public ItemEntity(int ids, String names) {
this.id = ids;
this.name = names;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
} private List<ItemEntity> initRvList() {
List<ItemEntity> ll = new ArrayList<ItemEntity>();
ll.add(new ItemEntity(1, "张三"));
ll.add(new ItemEntity(2, "李四"));
ll.add(new ItemEntity(3, "王五"));
ll.add(new ItemEntity(4, "赵六"));
ll.add(new ItemEntity(5, "钱七"));
ll.add(new ItemEntity(6, "孙八"));
ll.add(new ItemEntity(7, "吴九"));
ll.add(new ItemEntity(8, "周十"));
return ll;
} public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewViewHolder> { private Context mContext;
private List<ItemEntity> mList; //构造方法,提供context上下文和RexyclerView的数据源
public RecyclerViewAdapter(Context context, List<ItemEntity> list) {
this.mContext = context;
this.mList = list;
} @Override
public RecyclerViewAdapter.RecyclerViewViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//通过一个itemView创建一个ViewHolder
View itemViews = getLayoutInflater().inflate(R.layout.item_recyclerview,parent, false);
return new RecyclerViewViewHolder(itemViews);
} @Override
public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {
//这里为item项的每个一个控件赋值
holder.id.setText(String.valueOf(mList.get(position).getId()));
holder.name.setText(mList.get(position).getName());
} @Override
public int getItemCount() {
//返回值item的总数
return mList == null ? 0 : mList.size();
} public class RecyclerViewViewHolder extends RecyclerView.ViewHolder { public TextView id;
public TextView name; public RecyclerViewViewHolder(View itemView) {
super(itemView);
id = (TextView) itemView.findViewById(R.id.id);
name = (TextView) itemView.findViewById(R.id.name);
}
}
}
}
这里附上itemView的布局文件的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> <TextView
android:id="@+id/id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#f80"
android:textSize="13sp" /> <TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:textColor="#08f"
android:textSize="16sp" /> </LinearLayout>
整个示例就这样了,写的比较简单,仅供各位参考。
温馨提醒一句,因为之前习惯使用ListView,所以,刚开始使用RecyclerView的时候总是忘了设置LayoutManage,这样出来的结果是一片空白,一脸懵逼,自此,每次都要去检查一下有没有LayoutManage。哈哈。
上面的示例是一个纵向的线性的LayoutManage,如果要搞成横向的或者Grid的直接设置另外一个LayoutManage就可以了,还可以很方便的实现瀑布流布局。
//设置一个LinearLayoutManager
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
//设置一个GridLayoutManager
rv.setLayoutManager(new GridLayoutManager(this, 3));
//设置一个纵向的,有3列的瀑布流的布局
rv.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));
最简单的示例先写到这里,后续更新。哈哈。2016-12-28 14:56
示例看完,仔细的看一下代码,其实也很繁琐的,而且实际使用中还有诸多的不方便,例如没有getItem()等方法。所以,我们对这进行做一些封装。使得我们使用的时候更加方便,也让代码变得更加简洁。
public abstract class BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder<T>> { private List<T> list;
private Context mContext; public BaseAdapter(List<T> list, Context mContext) {
this.list = list;
this.mContext = mContext;
} @Override
public BaseViewHolder<T> onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = (LayoutInflater)
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return onViewHolderCreate(layoutInflater, parent, viewType);
} @Override
public void onBindViewHolder(BaseViewHolder<T> holder, int position) {
holder.onBind(list.get(position), position);
} @Override
public int getItemCount() {
return list == null ? 0 : list.size();
} public void setList(List<T> list) {
this.list = list;
} public T getItem(int pos) {
return list.get(pos);
} abstract BaseViewHolder<T> onViewHolderCreate(LayoutInflater layoutInflater, ViewGroup parent, int viewType);
}
上面就是Adapter的封装。下面来看看ViewHolder的封装。
public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder { public BaseViewHolder(View itemView) {
super(itemView);
} public abstract void onBind(T t,int position);
}
最后来看看是怎么使用我们已经封装好的Adapter和ViewHolder。
public class Adapters extends BaseAdapter<ItemEntity> { public Adapters(List<ItemEntity> list, Context mContext) {
super(list, mContext);
} @Override
BaseViewHolder<ItemEntity> onViewHolderCreate(LayoutInflater layoutInflater, ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.item_recyclerview, parent, false);
return new ViewHolders(view);
}
} public class ViewHolders extends BaseViewHolder<ItemEntity> { private TextView id;
private TextView name; public ViewHolders(View itemView) {
super(itemView);
id = (TextView) itemView.findViewById(R.id.id);
name = (TextView) itemView.findViewById(R.id.name);
} @Override
public void onBind(ItemEntity itemEntity, int position) {
id.setText(String.valueOf(itemEntity.getId()));
name.setText(itemEntity.getName());
}
}
现在来看看我们封装的Adapter和VIewHolder,先来看简单的ViewHolder,ViewHolder中很简单,只有一个构造方法和一个抽象的onBind() 这有啥作用呢?其实很简单,看最后使用的时候,使用的时候,onBind() 可以直接写itemView的控件的赋值就可以了,简单明了。再来看看Adapter,Adapter也很简单,第一个是构造方法,中间三个是复写父类的,接下来的两个是自己加的,方便以后Adapter里面操作一些和业务相关的一些东西,别问为什么,只是这两个方法在以后的日子里,用上的概率会比较大。现在来看看onCreateViewHolder() 先不要看封装的,先看看最前面的那个小示例,示例里面的onCreateViewHolder里面做的是直接返回一个ViewHolder对象,而new一个ViewHolder需要一个itemView,而itenView需要inflater一个Layout来得到一个View,这,明显不对,为啥呢?因为我们这是封装,而不是实际的使用,实际使用的时候才涉及到这一块,所以,对于未知的东西,我们可以写一个抽象的方法来返回,而inflater一个view需要的一些东西我们可以事先做好,例如获取一个inflater就可以在这里做,然后直接在使用的时候调用就可以了,所以这里直接调用一个抽象的方法来获取ViewHolder.再来看看OnBindViewHolder(),这里我们直接调用ViewHolder的那个抽象的onBind(),看看最前面的那个小示例,示例里面,我们做的是给itemView的每一个控件赋值,现在这样封装,可以将Adapter里面做的操作转到ViewHolder,这样分工更加明确,看看最后的使用就知道了,Adapter里面只有几行代码,只需要在onViewHolder()方法里面操作即可,最后看看ViewHolder,所有涉及到的控件的findView,赋值都在ViewHolder里面进行。这样分工就明确了,代码也一眼看懂。里面要给itemView的每一个控件添加点击事件也很方便,如果要给整个的ItemView添加点击事件可以直接使用RecyclerView的ViewHolder里面的itemView既可(itemView.setOnClickListener)。
有关于Adapter和ViewHolder的封装写到这里。后续更新,2016-12-28 18:01
RecyclerView.ItemDecoration
RecyclerView 是不带分割线的,所以分割线要自己写,而分割线实现起来也是比较简单的,最傻瓜式的就是在布局文件中预留出来。然后再最后一个的时候隐藏掉,就这个简单,但是,如果遇到比较复杂的就难搞了,像gridview一样,要考虑横竖的情况就比较麻烦了。但不用怕,官方有一个现成的抽象类,你只需要继承这个类,实现相应的方法即可。哈哈,那就是ItemDecoration。ItemDecoration中有三个重要的方法,onDraw,onDrawOver,getItemOffsets,其中,onDraw和onDrawOver只需要实现一个即可,看名字也知道啥意思了,一个是在draw item 之前调用,一个是在之后调用,所以只需要实现一个即可,一般实现onDraw比较多。getItemOffsets,这个方法其实就是计算分割线的偏移量。说白了就是执行分割线的大小。
public class LinearManageItemDecoracition extends RecyclerView.ItemDecoration { public int mOrientation = LinearLayoutManager.VERTICAL;
public Drawable dividerLineHorizontal = null;
public Drawable dividerLineVertical = null;
public ColorDrawable cd;
private Paint paint; public LinearManageItemDecoracition(Context context, int orientation) {
if (orientation == LinearLayoutManager.VERTICAL || orientation == LinearLayoutManager.HORIZONTAL)
mOrientation = orientation;
else
throw new RuntimeException("orientation is Incorrect");
dividerLineHorizontal = context.getResources().getDrawable(R.drawable.divider_drawable_horizontal);
dividerLineVertical = context.getResources().getDrawable(R.drawable.divider_drawable_vertical);
//纯色可以不用Drawable,直接用颜色
cd = new ColorDrawable(Color.parseColor("#FF8800"));
paint = new Paint();
paint.setColor(Color.BLUE);
} @Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// drawMethod1(c, parent, state);
// drawMethod2(c, parent, state);
drawMethod3(c, parent, state);
} @Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
} @Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.left = 0;
outRect.top = 0;
if (mOrientation == LinearLayoutManager.VERTICAL) {
outRect.right = 0;
if (dividerLineHorizontal != null) {
outRect.bottom = dividerLineHorizontal.getIntrinsicHeight();
} else {
outRect.bottom = 5;
}
} else if (mOrientation == LinearLayoutManager.HORIZONTAL) {
outRect.bottom = 0;
if (dividerLineVertical != null) {
outRect.right = dividerLineVertical.getIntrinsicWidth();
} else {
outRect.right = 5;
}
}
} public void drawMethod1(Canvas c, RecyclerView parent, RecyclerView.State state) {
int childCount = parent.getChildCount();
if (mOrientation == LinearLayoutManager.VERTICAL) {
for (int pos = 0; pos < childCount - 1; pos++) {
View view = parent.getChildAt(pos);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
int left = view.getLeft() - layoutParams.leftMargin;
int right = view.getRight() + layoutParams.rightMargin;
int top = view.getBottom() + layoutParams.bottomMargin;
int bottom = top + dividerLineHorizontal.getIntrinsicHeight();
dividerLineHorizontal.setBounds(left, top, right, bottom);
dividerLineHorizontal.draw(c);
} } else if (mOrientation == LinearLayoutManager.HORIZONTAL) {
int top = parent.getPaddingTop();
int bottom = parent.getHeight() - parent.getPaddingBottom();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + params.rightMargin;
int right = left + dividerLineVertical.getIntrinsicWidth();
dividerLineVertical.setBounds(left, top, right, bottom);
dividerLineVertical.draw(c);
}
}
} public void drawMethod2(Canvas c, RecyclerView parent, RecyclerView.State state) {
int childCount = parent.getChildCount();
int dp1 = dpToPx(1);
if (mOrientation == LinearLayoutManager.VERTICAL) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
for (int index = 0; index < childCount - 1; index++) {
View childView = parent.getChildAt(index);
RecyclerView.LayoutParams childViewParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
//childViewParams.bottomMargin->画在childview下面,自然要加上margin
//ViewCompat.getTranslationY(childView) ,纵向的平移,在动画的时候用得上,如果item没有动画则可有可无
int top = childView.getBottom() + childViewParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
int bottom = top + dp1;
c.drawRect(left, top, right, bottom, paint);
}
} else if (mOrientation == LinearLayoutManager.HORIZONTAL) {
int top = parent.getPaddingTop();
int bottom = top + parent.getHeight();
for (int index = 0; index < childCount - 1; index++) {
View childView = parent.getChildAt(index);
RecyclerView.LayoutParams childViewParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int left = childView.getRight() + childViewParams.rightMargin;
int right = left + dp1;
c.drawRect(left, top, right, bottom, paint);
}
}
} public void drawMethod3(Canvas c, RecyclerView parent, RecyclerView.State state) {
int childCount = parent.getChildCount();
if (mOrientation == LinearLayoutManager.VERTICAL) {
for (int pos = 0; pos < childCount - 1; pos++) {
View view = parent.getChildAt(pos);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
int left = view.getLeft() - layoutParams.leftMargin;
int right = view.getRight() + layoutParams.rightMargin;
int top = view.getBottom() + layoutParams.bottomMargin;
//纯色的情况下,高度要写死了
int bottom = top + dpToPx(1);
cd.setBounds(left, top, right, bottom);
cd.draw(c);
}
} else if (mOrientation == LinearLayoutManager.HORIZONTAL) {
int top = parent.getPaddingTop();
int bottom = parent.getHeight() - parent.getPaddingBottom();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + params.rightMargin;
//同理。纯色的情况下,宽度是写死的
int right = left + dpToPx(1);
cd.setBounds(left, top, right, bottom);
cd.draw(c);
}
}
} public int dpToPx(int dp) {
return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}
}
这个Decoration中,实现了几种方式。onDraw提供了3种绘制分割线的方式,但getItemOffsets只实现drawable的时候
第一种方式,是在分割线处画一个Drawable,这里的drawable可以是自己写的一个shape的drawable文件,也可以是图片,等等。如果是自己写的drawable,那么要写上宽或高。
第二种方式,是直接在分割线处画一个矩形的纯色区域,也很好理解。
第三种方式,和第一种。换汤不换药。也就是在第一种的情况下,如果drawable是纯色的话,就可以直接用colorDrawable,就不用那么麻烦自己去写一个shape的drawable了。这时候。高度(或宽度)就要自己自定义了,而不是在drawable里面写死。其实也很好理解。
2017-1-4 10:34 更新,后续再更新gridview的divider吧
RecyclerView还有一个和ListView比较常用不同是:当没数据的时候,也就是emptyView,ListView 可以直接设就可以了,而RecyclerView 没有提供相应的api,这就要我们自己去实现了。
实现的原理也很简单。那就是控件的显示和隐藏嘛。
虽然没有提供设置emptyView的api,但是提供了一个数据的观察相应的api,也就是给Adapter注册一个观察者,这个观察者可以观察整个Recycler的数据的变化。这就很简单啦,只要数据是空的时候,那就显示EmptyView,否则显示RecyclerView。都弄清楚了,就看代码。
rvAdapter.registerAdapterDataObserver(obs = new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
setEmptyView();
}
});
rv.setAdapter(rvAdapter);
setEmptyView();
private void setEmptyView() {
if (rvAdapter.getItemCount() > 0) {
emptyView.setVisibility(View.GONE);
rv.setVisibility(View.VISIBLE);
} else {
emptyView.setVisibility(View.VISIBLE);
rv.setVisibility(View.GONE);
}
}
这里,注册完了。最后一步,在RecyclerView的数据加载完毕的时候再调一下SemptyView,因为很多时候,加载出来的数据就是空的。所以,这里要设一下。设置EmptyView是否显示也就根据Adapter的返回的getItemCount来判断。
都好了,最后别忘了反注册。注册的观察者要在Activity 销毁的时候反注册。
@Override
protected void onDestroy() {
super.onDestroy();
if (obs != null)
rvAdapter.unregisterAdapterDataObserver(obs);
}
OK ,一切完毕。
更新于2017-1-4 11:46
Android-RecyclerView的更多相关文章
- Android RecyclerView 实现支付宝首页效果
Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...
- Android RecyclerView的基本使用
Android RecyclerView 在去年的Google I/O大会上就推出来了,以前经常使用的ListView 继承的是AbsListView,而RecyclerView则直接继承 ViewG ...
- Android RecyclerView单击、长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类
Android RecyclerView单击.长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类 我写的附录文章2,介绍了 ...
- Android RecyclerView单击、长按事件标准实现:基于OnItemTouchListener + GestureDetector
Android RecyclerView单击.长按事件:基于OnItemTouchListener + GestureDetector标准实现 Android RecyclerView虽然拥有L ...
- Android RecyclerView添加Header头部
Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...
- Android RecyclerView(瀑布流)水平/垂直方向分割线
Android RecyclerView(瀑布流)水平/垂直方向分割线 Android RecyclerView不像过去的ListView那样随意的设置水平方向的分割线,如果要实现Recycle ...
- 极简的Android RecyclerView Adapter(使用DataBinding)
阅读本篇文章需要读者对Android Databinding和RecyclerView有一定的了解. 简介 我们知道,DataBinding的核心理念是数据驱动.数据驱动驱动的目标就是View,使用D ...
- 浅谈Android RecyclerView
Android RecyclerView 是Android5.0推出来的,导入support-v7包即可使用. 个人体验来说,RecyclerView绝对是一款功能强大的控件. 首先总结下Recycl ...
- [Android]RecyclerView的简单演示样例
去年google的IO上就展示了一个新的ListView.它就是RecyclerView. 下面是官方的说明,我英语能力有限,只是我大概这么理解:RecyclerView会比ListView更具有拓展 ...
- (转载) Android RecyclerView 使用完全解析 体验艺术般的控件
Android RecyclerView 使用完全解析 体验艺术般的控件 标签: Recyclerviewpager瀑布流 2015-04-16 09:07 721474人阅读 评论(458) 收藏 ...
随机推荐
- 在PowerShell中使用curl(Invoke-WebRequest)
前言 习惯了windows的界面模式就很难转去命令行,甚至以命令行发家的git也涌现出各种界面tool.然而命令行真的会比界面快的多,如果你是一个码农. situation:接到需求分析bug,需要访 ...
- 【腾讯bugly干货分享】HTML 5 视频直播一站式扫盲
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1277 视频直 ...
- 关于微软HttpClient使用,避免踩坑
最近公司对于WebApi的场景使用也越来越加大了,随之而来就是Api的客户端工具我们使用哪个?我们最常用的估计就是HttpClient,在微软类库中命名空间地址:System.Net.Http,是一个 ...
- git 命令
切换仓库地址: git remote set-url origin xxx.git切换分支:git checkout name撤销修改:git checkout -- file删除文件:git rm ...
- CSS 特殊属性介绍之 pointer-events
首先看一下 MDN 上关于 pointer-events 的介绍: CSS属性 pointer-events 允许作者控制特定的图形元素在何时成为鼠标事件的 target.当未指定该属性时,SVG 内 ...
- windows+nginx+iis+redis+Task.MainForm构建分布式架构 之 (nginx+iis构建服务集群)
本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,由标题就能看出此内容不是一篇分享文章能说完的,所以我打算分几篇分享文章来讲解,一步一步实现分 ...
- 编写高质量代码:改善Java程序的151个建议(第6章:枚举和注解___建议88~92)
建议88:用枚举实现工厂方法模式更简洁 工厂方法模式(Factory Method Pattern)是" 创建对象的接口,让子类决定实例化哪一个类,并使一个类的实例化延迟到其它子类" ...
- ASP.NET MVC原理
仅此一文让你明白ASP.NET MVC原理 ASP.NET MVC由以下两个核心组成部分构成: 一个名为UrlRoutingModule的自定义HttpModule,用来解析Controller与 ...
- 使用HTML5的cavas实现的一个画板
<!DOCTYPE html><html><head> <meta charset="utf-8"> <meta http-e ...
- 海鑫智圣:物联网漫谈之MQTT协议
什么是MQTT协议 MQTT(消息队列遥测传输协议)是IBM在1999年专门针对物联网等应用场景来制订的轻量级双向消息传输协议,它主要是为了解决物联网上使用到的设备的互相通信的问题,以及这些设备与后端 ...