众所周知,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. Be Better:遇见更好的自己-2016年记

    其实并不能找到好的词语来形容过去的一年,感觉就如此平淡的过了!没有了毕业的稚气,看事情淡了,少了一丝浮躁,多了一分认真.2016也许就是那句话-多读书,多看报,少吃零食多睡觉,而我更愿意说--Be B ...

  2. Fabio 安装和简单使用

    Fabio(Go 语言):https://github.com/eBay/fabio Fabio 是一个快速.现代.zero-conf 负载均衡 HTTP(S) 路由器,用于部署 Consul 管理的 ...

  3. ASP.NET Core HTTP 管道中的那些事儿

    前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...

  4. CSS Position 定位属性

    本篇文章主要介绍元素的Position属性,此属性可以设置元素在页面的定位方式. 目录 1. 介绍 position:介绍position的值以及辅助属性. 2. position 定位方式:介绍po ...

  5. 微软新神器-Power BI横空出世,一个简单易用,还用得起的BI产品,你还在等什么???

    在当前互联网,由于大数据研究热潮,以及数据挖掘,机器学习等技术的改进,各种数据可视化图表层出不穷,如何让大数据生动呈现,也成了一个具有挑战性的可能,随之也出现了大量的商业化软件.今天就给大家介绍一款逆 ...

  6. 原生javascript 固定表头原理与源码

    我在工作中需要固定表头这个功能,我不想去找,没意思.于是就写了一个,我写的是angularjs 自定义指令 起了个 "fix-header" ,有人叫  "freeze- ...

  7. Android—简单的仿QQ聊天界面

    最近仿照QQ聊天做了一个类似界面,先看下界面组成(画面不太美凑合凑合呗,,,,):

  8. iOS之App Store上架被拒Legal - 5.1.5问题

    今天在看到App Store 上架过程中,苹果公司反馈的拒绝原因发现了这么一个问题: Legal - 5.1.5 Your app uses background location services ...

  9. 编译器开发系列--Ocelot语言6.静态类型检查

    关于"静态类型检查",想必使用C 或Java 的各位应该非常熟悉了.在此过程中将检查表达式的类型,发现类型不正确的操作时就会报错.例如结构体之间无法用+ 进行加法运算,指针和数值之 ...

  10. 开源一个WEB版本GEF,基于SVG的网页流程图框架

    8月开始断断续续的制作这个web gef,没有任何依赖,完全原生js开发,目前已经完成了雏形,基本上可以在项目里应用了. 下图展示的是demo1的效果,包括拖拽,生成连线,点击生成\取消墙体,整个de ...