android v7兼容包RecyclerView的使用(二)

上篇文章 android v7兼容包RecyclerView的使用(一)讲了RecyclerView的最基本用法,现在开始挖掘更详细的内容。
在RecyclerView的API中,有这样一句话

A flexible view for providing a limited window into a large data set.

大致意思就是:当有大量的数据显示在一个有限大小的窗口上时,RecyclerView就是解决这种情况的一个灵活的View。

从以上描述可以看出RecyclerView的使用场景。如果我们有大量的同一类型的数据要显示在屏幕上,而此时很有可能整个屏幕无法完全显示所有的数据,这时候RecyclerView就是一个合适的选择。当用户上下滚动屏幕的时候,item的循环重复利用也在同时进行,当一个新的item进入可视范围,必然有一个旧的item移出可视范围,而这个被移出的item也就被循环重用。那么循环重用item有什么作用呢,其实他是很有用的一种方法,因为它节省了CPU资源和内存。

或许你会说我们用ListView用了很长的时间了,RecyclerView与我们以前的方式有什么特别的地方呢,从前,我们使用listview的时候,显示,循环重用以及其它的一些东西都有一定程度上的耦合性,现在RecyclerView则提供了一种更加灵活的方式。

谷歌现在使用的这种方法,它不在乎RecyclerView所看到的东西,也不在乎元素是否显示在正确的地方,更不在乎每个元素是如何分隔的。RecyclerView做的事仅仅是是回收。因此得名RecyclerView。

下面是几个和RecyclerView有关的最重要的类

Adapter:适配器,绑定数据集
ViewHolder:根据当前的数据保存视图
LayoutManager:布局管理器。决定item如何摆放
ItemDecoration:勉强理解为item装饰器,可以美化item
ItemAnimator:动画,当一个item被增加,删除或者重新摆放时,会有动画效果。

ViewHolder

安卓官方已经推荐使用ViewHolder模式有很长一段时间了,因为它能在一定程度上大大提高效率,界面流畅性增加。RecyclerView的适配器是一个内部类,我们通过继承它来扩展我们的子类,就像这样。

public final static class ListItemViewHolder extends RecyclerView.ViewHolder {
   TextView title;//item界面上的一个元素
   TextView description;//item界面上的一个元素
   //当然可以继续增加这些元素
   public ListItemViewHolder(View itemView) {
      super(itemView);
      //关联引用
      title= (TextView) itemView.findViewById(R.id.title);
      description= (TextView) itemView.findViewById(R.id.description);
   }
}

RecyclerView.Adapter

适配器完成两个主要功能:负责建立基础数据集和单个item布局之间的联系。适配器是Android的一个重要部分之一。它被用在许多地方,比如ListView, AutoCompleteTextView, Spinner都用到了适配器。

谷歌使用了RecyclerView的内部类Adapter代替了传统的Adapter,所以在RecyclerView中,你不会见到类似SimpleCursorAdapter, ArrayAdapter这样的适配器。

然而不幸的是谷歌并没有提供RecyclerView.Adapter的默认实现类,它是一个抽象类,所以我们必须去实现三个方法。

public VH onCreateViewHolder(ViewGroup parent, int viewType)
public void onBindViewHolder(VH holder, int position)
public int getItemCount()

VH是一个继承自ViewHolder的泛型,在子类中必须提供具体的类型。最基本的适配器写法如下

public class RecyclerViewAdapter extends 
        RecyclerView.Adapter
        <RecyclerViewAdapter.ItemViewHolder> {

    private List<DemoModel> items;

    RecyclerViewAdapter(List<DemoModel> modelData) {
        if (modelData == null) {
            throw new IllegalArgumentException(
                  "modelData must not be null");
        }
        this.items = modelData;
    }

    @Override
    public ItemViewHolder onCreateViewHolder(
            ViewGroup viewGroup, int viewType) {
        View itemView = LayoutInflater.
                from(viewGroup.getContext()).
                inflate(R.layout.item,viewGroup,false);
        return new ItemViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(
            ItemViewHolder viewHolder, int position) {
        DemoModel model = items.get(position);
        viewHolder.title.setText(model.getTitle());
        viewHolder.description.setText(model.getDescription());
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public final static class ItemViewHolder 
           extends RecyclerView.ViewHolder {
        // ... shown above in the ViewHolder section
    }
}

RecyclerView.LayoutManager

布局管理器是RecyclerView最有意思的一个地方,它负责对所有子item进行布局,在最新的v7兼容库中,它有三个实现类,分别是LinearLayoutManager(线性布局),GridLayoutManager(a网格布局),StaggeredGridLayoutManager(流式布局),默认情况下,如果我们不设置布局管理器,将使用线性布局。我们可以通过继承该类实现自己的布局管理器,由我们自己决定如何摆放item的内容,然而,当你去看上面的三个实现类的代码时,你会发现这是多么不容易的一件事。。。代码好长。。。真的!布局管理器的具体使用方法将再开一篇博客,这里先介绍一下线性布局管理器的简单使用。代码如下

//实例化对象
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
//设置布局方向为竖直
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
//滚动到那一项
layoutManager.scrollToPosition(currPos);
//设置布局管理器
recyclerView.setLayoutManager(layoutManager);

RecyclerView.ItemDecoration

姑且我叫它为item装饰器,使用ItemDecoration我们可以给item增加偏移量,增加item分割线,高亮等等。我们可以能增加很多个ItemDecoration,RecyclerView 会依次遍历所有的ItemDecoration并调用绘图方法去完成装饰。它有三个抽象方法

public void onDraw(Canvas c, RecyclerView parent)
public void onDrawOver(Canvas c, RecyclerView parent)
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)

在onDraw中绘制的内容可能会被item的内容遮挡,但是你在onDrawOver中绘制的内容会被绘制在item上方。我们可以使用偏移量对item进行简单分割,但是如果你想显示一条分割线,你就不得不去使用onDrawOver方法去显式的绘制一条分隔线。

布局管理器会在测量阶段调用getItemOffset方法,它与一个Rect类型的参数,outRect 参数看上去有点怪怪的,那么为什么不用一个返回值代替它呢,既然谷歌这样做了,必然是有意义的。因为这样可以让所有item去复用一个Rect对象因此节约了资源,这样做不一定是最好的,但是的确可以有效节约资源。

有一点要值得注意的是,onDraw()/onDrawOver()方法并不会为每个item调用,它只会被调用一次去绘制所有item的装饰内容。因此我们必须在该方法中遍历所有item进行绘制。

上篇文章简单提供了一个DividerItemDecoration的源码,当然我们可以通过继承ItemDecoration 去实现更加丰富的内容。

RecyclerView.ItemAnimator

ItemAnimator类主要是处理动画效果,以下三种情况,会触发该动画。

  • 一个item被添加到数据集中
  • 一个item从数据集中被移除
  • 一项在界面中位置被移动了

幸运的是,在v7兼容包中提供了一个默认的实现类叫做DefaultItemAnimator,如果我们不设置,默认就会适应该类。

如果想让动画生效,很明显android必须知道数据改变了。在早期的适配器中,我们需要调用notifyDataSetChanged方法通知android数据集改变了。但是现在不再适用了,这个方法会重绘所有可见item但是不会触发任何动画,我们必须调用类似以下的方法才能触发动画。

public final void notifyItemInserted(int position)
public final void notifyItemRemoved(int position)

Listeners

监听器,RecyclerView中不再有OnItemClickListener和OnItemLongClickListener监听器,但是我们可以使用OnItemTouchListener再配合手势去识别这些事件,这意味着我们需要编写更多的代码去实现同样的效果。

综上所述

在布局文件中使用,代码如下

     <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity"
        tools:listitem="@layout/item_demo_01"
        />

tools属性会渲染当前布局,当我们切换到可视化编辑器的时候可以看到效果,当然也可以不写,只是看不到效果罢了。

我们可以看到,并没有使用一些特别的属性,实际上RecyclerView的属性都是来自其父类ViewGroup。在RecyclerView中有一个方法用到了AttributeSet,这个方法就是generateLayoutParams,但是在内部它也并没有怎么对属性进行操作。

@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
   if (mLayout == null) {
      throw new IllegalStateException("RecyclerView has no LayoutManager");
   }
   return mLayout.generateLayoutParams(getContext(), attrs);
}

而在Activity中使用它也是很简单的

//获得控件引用
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

//实例化布局管理器并设置布局管理器,滚动到顶部
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.scrollToPosition(0);
recyclerView.setLayoutManager(layoutManager);

// 当item大小一样时有利于优化,具体怎么个优化法我也不知道。。
recyclerView.setHasFixedSize(true);

// 绑定适配器并提供数据集
List<DemoModel> items = new ArratList<DemoModel>();
//......这里进行数据的初始化工作
adapter = new RecyclerViewDemoAdapter(items);
recyclerView.setAdapter(adapter);

//使用装饰器,该类的源码在上一篇文章中有提供
RecyclerView.ItemDecoration itemDecoration =
        new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST);
//注意这里是add而不是set,说明可以添加多个
recyclerView.addItemDecoration(itemDecoration);

// 下面这句话可以不写,因为默认就是使用该类; 
// 当我们自定义了动画,才有必要去设置
recyclerView.setItemAnimator(new DefaultItemAnimator());

// 设置监听器,需要配合手势
//recyclerView.addOnItemTouchListener(this);

总结为以下几步

  1. 获得RecyclerView引用
  2. 创建布局管理器并添加到RecyclerView中
  3. 创建适配器并添加到RecyclerView中
  4. 创建0到多个item装饰器ItemDecoration,前提是有必要的话
  5. 创建ItemAnimator,如果有必要的话
  6. 创建0到多个监听器并增加到RecyclerView中

基本源码同上篇文章,源码下载地址http://download.csdn.net/detail/sbsujjbcy/8489667

本篇文章参考了http://www.grokkingandroid.com/first-glance-androids-recyclerview/

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。