在去年Google I/0大会,Google开放了一个全新的视图类RecyclerView,它被用来代替ListView以及GridView,提供更为高效的回收复用机制,同时实现管理与视图的解耦合,今天对这个新的控件来进行一次总结。

概述

首先,让我们来看一下RecyclerView类之下都有哪些重要的类,以及他们的作用:

  • RecyclerView.Adapter:托管数据集合,为每个Item创建视图;
  • RecyclerView.ViewHolder:承载Item视图的子视图;
  • RecyclerView.LayoutManager:负责Item视图的布局;
  • RecyclerView.ItemDecoration:为每个Item视图添加子视图,在Demo中被用来绘制Divider;
  • RecyclerView.ItemAnimator:负责添加、删除数据时的动画效果;

基本用法

首先让我来看一下RecyclerView的基本用法:

1.创建一个线性布局管理器LayoutManager

// 创建一个线性布局管理器
mLayoutManager = new LinearLayoutManager(this);
// 默认是Vertical,可以不写
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);

2.重新一个adapter继承RecyclerView.Adapter《VH》,VH就表示我们平时在ListView中用的ViewHolder类(该类必须继承RecyclerView.ViewHolder);

在adapter中有几个重要的方法需要我们自己补充:

  • public int getItemCount():返回显示Item总数;
  • public void onBindViewHolder(ViewHolder vh, int position):绑定View到Item上vh就是我们在继承RecyclerView.Adapter传入的VH类型,在这个方法中处理数据显示到Item上;
  • public ViewHolder onCreateViewHolder(ViewGroup view, int position):在该方法中我们创建一个ViewHolder并返回,ViewHolder必须有一个带有View的构造函数,这个View就是我们Item的根布局,在这里我们可以自定义Item的布局;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private List<String> dataList;
    public MyAdapter(List<String> list) {
        this.dataList = list;
    }
    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        return dataList.size();
    }
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {
        // TODO Auto-generated method stub
        viewHolder.textView.setText(dataList.get(position));
    }
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
        // TODO Auto-generated method stub
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(
                R.layout.item, null);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }
    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView textView;
        public ViewHolder(View view) {
            super(view);
            // TODO Auto-generated constructor stub
            textView = (TextView) view.findViewById(R.id.item_text);
        }
    }
}

3.设置数据集

List<String> list = new ArrayList<String>();
for (int i = 0; i < 100; i++) {
    list.add("item "+ i);
}
MyAdapter adapter = new MyAdapter(list);
mRecyclerView.setAdapter(adapter);

改变LinearLayoutManager的方向可以设置为横向的ListView显示,这也是RecyclerView的强大之处,可以实现回收管理和视图的解耦。

mLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

如何为Item添加分割线

简单的使用之后我们看到的RecyclerView连基本的分割线都没有,接下来就来看看如何为它添加分割线吧:使用addItemDecoration方法可以为RecyclerView添加一个ItemDecoration,利用ItemDecoration为我们绘制分割线。

ItemDecoration下有三个方法,ItemDecoration并没有对其实现,需要我们自己完成:

  • onDraw方法:其绘制将会在每个Item被绘制之前进行;
  • onDrawOver:在绘制完Item后进行绘制;
  • getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量;

让我们来看看代码:

public class ItemDivider extends ItemDecoration {
    private Drawable mDrawable;
    public ItemDivider(Context context, int resId) {
        //在这里我们传入作为Divider的Drawable对象
        mDrawable = context.getResources().getDrawable(resId);
    }
    @Override
    public void onDrawOver(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);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            //以下计算主要用来确定绘制的位置
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDrawable.getIntrinsicHeight();
            mDrawable.setBounds(left, top, right, bottom);
            mDrawable.draw(c);
        }
    }
    @Override
    public void getItemOffsets(Rect outRect, int position, RecyclerView parent) {
        outRect.set(0, 0, 0, mDrawable.getIntrinsicWidth());
    }
}

在这里我写了一个shape来代替分割线:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
    <solid android:color="#000" />
    <size android:height="2dp" />
</shape>

添加分割线的效果:

如何添加点击事件 ##

在Adapter中设置自己OnItemClickListener以及OnItemLongClickListener 在ViewHolder中公开Item的根布局View,之后在onBindViewHolder方法获得根布局View并设置点击的listener,调用自己设置的listener

1.首先在MyAdapter中创建自己的接口:

public interface OnItemClickListener {
    public void onClick(View parent, int position);
}
public interface OnItemLongClickListener {
    public boolean onLongClick(View parent, int position);
}

2.接着在ViewHolder中将根布局View公开:

public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView textView;
        public View itemView;
        public ViewHolder(View view) {
            super(view);
            // TODO Auto-generated constructor stub
            itemView = view;
            textView = (TextView) view.findViewById(R.id.item_text);
        }
    }

3.最后在onBindViewHolder对itemView设置点击事件:

viewHolder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                if (onItemClickListener != null) {
                    onItemClickListener.onClick(v, position);
                }
            }
        });
viewHolder.itemView.setOnLongClickListener(new OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                // TODO Auto-generated method stub
                if (onItemLongClickListener != null) {
                    return onItemLongClickListener.onLongClick(v, position);
                }
                return false;
            }
        });

如何判断是否滑动到达尾部或顶部

LinearLayoutManager提供了如下几个方法来帮助开发者获取屏幕上的顶部item和底部item: findFirstVisibleItemPosition() findFirstCompletelyVisibleItemPosition() findLastVisibleItemPosition() findLastCompletelyVisibleItemPosition()

这样我们就得到了思路:对RecyclerView设置滑动监听事件,在其中进行判断:

mRecyclerView.setOnScrollListener(new OnScrollListener() {
    boolean isShowTop = false;
    boolean isShowBottom = false;
    @Override
    public void onScrolled(int arg0, int arg1) {
        // TODO Auto-generated method stub
        if (mLayoutManager.findLastCompletelyVisibleItemPosition() == 99) {
            if (!isShowTop) {
                Toast.makeText(MainActivity.this, "滑动到底部",
                        Toast.LENGTH_SHORT).show();
            }
            isShowTop = true;
        } else {
            isShowTop = false;
        }
        if (mLayoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
            if (!isShowBottom) {
                Toast.makeText(MainActivity.this, "滑动到顶部",
                        Toast.LENGTH_SHORT).show();
            }
            isShowBottom = true;
        } else {
            isShowBottom = false;
        }
    }
    @Override
    public void onScrollStateChanged(int arg0) {
        // TODO Auto-generated method stub
    }
});

来看一下效果:

添加或移除数据

RecyclerView.Adapter中提供了两个方法来做出添加数据或删除数据的调整: public final void notifyItemInserted(int position) public final void notifyItemRemoved(int position)

这样我们只需在自己的Adapter中提供添加或删除的方法,并在方法之中调用上述方法即可:

    public void insert(String data, int position){
        dataList.add(position, data);
        notifyItemInserted(position);
    }
    public void remove(int position){
        dataList.remove(position);
        notifyItemRemoved(position);
    }

接下来我在Activity中对RecyclerView设置点击添加数据,长按删除数据;

        adapter.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onClick(View parent, int position) {
                // TODO Auto-generated method stub
                adapter.insert("Insert", position);
            }
        });
        adapter.setOnItemLongClickListener(new OnItemLongClickListener() {
            @Override
            public boolean onLongClick(View parent, int position) {
                // TODO Auto-generated method stub
                adapter.remove(position);
                return true;
            }
        });

来看一下效果吧:

ItemAnimator可以设置加载和移除时的动画,我们可以通过setItemAnimator方法设置,但目前只提供了DefaultItemAnimator。

源码下载

点击下载源码

你可能感兴趣的内容
0条评论

dexcoder

这家伙太懒了 <( ̄ ﹌  ̄)>
Owner