众所周知,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的更多相关文章

  1. Android RecyclerView 实现支付宝首页效果

    Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...

  2. Android RecyclerView的基本使用

    Android RecyclerView 在去年的Google I/O大会上就推出来了,以前经常使用的ListView 继承的是AbsListView,而RecyclerView则直接继承 ViewG ...

  3. Android RecyclerView单击、长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类

     Android RecyclerView单击.长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类 我写的附录文章2,介绍了 ...

  4. Android RecyclerView单击、长按事件标准实现:基于OnItemTouchListener + GestureDetector

     Android RecyclerView单击.长按事件:基于OnItemTouchListener + GestureDetector标准实现 Android RecyclerView虽然拥有L ...

  5. Android RecyclerView添加Header头部

     Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...

  6. Android RecyclerView(瀑布流)水平/垂直方向分割线

     Android RecyclerView(瀑布流)水平/垂直方向分割线 Android RecyclerView不像过去的ListView那样随意的设置水平方向的分割线,如果要实现Recycle ...

  7. 极简的Android RecyclerView Adapter(使用DataBinding)

    阅读本篇文章需要读者对Android Databinding和RecyclerView有一定的了解. 简介 我们知道,DataBinding的核心理念是数据驱动.数据驱动驱动的目标就是View,使用D ...

  8. 浅谈Android RecyclerView

    Android RecyclerView 是Android5.0推出来的,导入support-v7包即可使用. 个人体验来说,RecyclerView绝对是一款功能强大的控件. 首先总结下Recycl ...

  9. [Android]RecyclerView的简单演示样例

    去年google的IO上就展示了一个新的ListView.它就是RecyclerView. 下面是官方的说明,我英语能力有限,只是我大概这么理解:RecyclerView会比ListView更具有拓展 ...

  10. (转载) Android RecyclerView 使用完全解析 体验艺术般的控件

    Android RecyclerView 使用完全解析 体验艺术般的控件 标签: Recyclerviewpager瀑布流 2015-04-16 09:07 721474人阅读 评论(458) 收藏  ...

随机推荐

  1. 基于spring注解AOP的异常处理

    一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...

  2. 札记:Fragment基础

    Fragment概述 在Fragment出现之前,Activity是app中界面的基本组成单位,值得一提的是,作为四大组件之一,它是需要"注册"的.组件的特性使得一个Activit ...

  3. DynamicObject - 代理对象的种类

    开箱即用,DynamicProxy提供了多种代理对象,主要分成两个大类: 基于继承(Inheritance-based) 基于继承的代理是通过继承一个代理类来实现,代理拦截对类的虚(virtual)成 ...

  4. web全栈开发之网站开发二(弹出式登录注册框前端实现-类腾讯)

    这次给大家分享的是目前很多网站中流行的弹出式登录框,如下面的腾讯网登录界面,采用弹出式登录的好处是大大提升了网站的用户体验和交互性,用户不用重新跳转到指定的页面就能登录,非常方便 先来个演示地址 要实 ...

  5. xamarin DependencyService源码阅读

    xamarin在面对PCL无法实现的各平台特有功能时使用了一种叫[DependencyService]的方式来实现.它使得xamarin能像原生平台一样做平台能做到的事情!主要分四个部分 接口:定义功 ...

  6. 如何定位Oracle数据库被锁阻塞会话的根源

    首先再次明确下,数据库因为要同时保证数据的并发性和一致性,所以操作有锁等待是正常的. 只有那些长时间没有提交或回滚的事物,阻塞了其他业务正常操作,才是需要去定位处理的. 1.单实例环境 2.RAC环境 ...

  7. 如何在Elasticsearch中安装中文分词器(IK+pinyin)

    如果直接使用Elasticsearch的朋友在处理中文内容的搜索时,肯定会遇到很尴尬的问题--中文词语被分成了一个一个的汉字,当用Kibana作图的时候,按照term来分组,结果一个汉字被分成了一组. ...

  8. APEX:对object中数据进行简单处理?

    在Salesforce中,常常要对各种数据进行处理,已满足业务逻辑.本篇文章会介绍如何实现从object获取数据,然后将取得的数据进行一系列简单处理. 第一步:SongName__c 是一个新建的ob ...

  9. Node.js使用PM2的集群将变得更加容易

    介绍 众所周知,Node.js运行在Chrome的JavaScript运行时平台上,我们把该平台优雅地称之为V8引擎.不论是V8引擎,还是之后的Node.js,都是以单线程的方式运行的,因此,在多核心 ...

  10. SpringMVC入门

    Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模 ...