×

AndroidStudyDemo之Android5.x新控件介绍(三)

96
diygreen
2016.04.18 21:04* 字数 2018
Android 5.x 新控件

作者:李旺成

时间:2016年4月18日


接上篇 AndroidStudyDemo之Android5.x新控件介绍(二)

这是 Android 5.x 中的新控件介绍的最后一篇,在这一篇中主要介绍 RecyclerView,该系列文章以讲解新特性和简单使用为主。

简介

RecyclerView 是 android-support-v7-21版本中新增的一个 Widget,看看一句话介绍:

  • 能够在有限的窗口中展示大数据集合的灵活视图
  • ListView 的升级版本,更加先进和灵活
  • 通过方便的视图复用轻松实现自定义高效的视图集
  • 它就是一个容器,可以有效的重用和滚动

上面从不同角度指出了 RecyclerView 的一些特点,我觉得可以简单的从其命名上来理解:
Recycler:反复循环器,再循环器。

对,就是循环,这个概念有没有很熟悉的感觉。回想下 ListView、GridView等,这些 AdapterView,它们就是利用视图回收技术来展示大量数据的控件;RecyclerView 与它们有什么区别,或者说其优势在哪里?

先看看官方介绍:

RecyclerView 类

继承自 ViewGroup,这个情理之中意料之外,为什么这么说?

情理之中,是指 RecyclerView 它是个容器,那么肯定会直接或间接继承自 ViewGroup。
意料之外,上面提到的 ListView 都是继承自 AdapterView,而 RecyclerView 又何其有类似的功能 —— 视图复用。但是他竟然没有继承自 AdapterView,从这一点来看,RecyclerView 的改动应该不小,完全不按以前那一套来了。

好了,闲话不多说,看看 RecyclerView 的相关类:

RecyclerView 相关类

这里直接列了出来,不是说它们都很常用,不要紧张,常用到的没这么多。

大致说一下 RecyclerView 的实现思路:

RecyclerView原理

上图只是为了说明一个问题:RecyclerView 只负责 Receiver,其他的功能都交给其他专门的类去处理,比如:Adapter 专门负责数据绑定(PS:话说,ListView 也有Adapter,哈哈,举个例子而已)。有没有一点单一职责原则的感觉。

可以看出 RecyclerView 的设计更利于解耦,更灵活,可操作性更高。

下面来看看怎么用。

简单使用

先看看效果:

RecyclerView 示例

一个简单的 Demo,演示了 RecyclerView 的简单使用,下面看看所涉及到的几个类。

几个关键类

Adapter

这个与 ListView 的 Adapter 功能类似,可以托管数据集合,为每一项Item创建视图并且绑定数据。
先看官方文档:

RecyclerView 的 Adapter 类

继承自 Object,与 AdapterView 所使用的 BaseAdapter 没有多少关系。但是不用急,看看它提供的方法,和 BaseAdapter 提供了不少类似的。

关于具体用法,在下面介绍吧!

ViewHolder

Google 建议 在 ListView 的 Adapter 中使用 ViewHolder,但是没有严格的限制,使用与否(还有用对与否)都没有什么标准,完全看开发者的意愿。

ViewHolder ,主要用来将数据和布局 item 进行绑定。所有对视图的操作都需要通过 ViewHolder 来进行,这是强制性的。

看下官方的介绍:

RecyclerView 的 ViewHolder 类

LayoutManager

LayoutManager,这个是新概念,在 ListView 中从没听说过;其作用就是负责Item 视图的布局的显示管理。

先看下官方介绍:


LayoutManager 类

它有三个实现类,看下它们的继承结构:


LayoutManager的继承结构.png
  • LinearLayoutManager:水平或者垂直的 Item 视图
  • GridLayoutManager:网格 Item 视图
  • StaggeredGridLayoutManager:交错的网格 Item 视图(瀑布流,明白了吧)

这几个类负责 RecyclerView 中 Item 的排布,具体使用在下面介绍。

ItemDecoration

ItemDecoration 用于设置条目的分割线,在 ListView 中通过 android:divider 属性来为条目设置分割线。RecyclerView 可不管这些了,都交给“专人”(专门的类)负责。这样灵活了,效果也更丰富,也稍微麻烦了(呵呵,哪有两全其美的设计)。

看下官方介绍:

ItemDecoration 类

上面使用红色框圈出来的 onDrawXxx() 方法,可想而知,你想要什么样式/效果,自己画去。

ItemAnimator

还记得给 ListView 的 Item 添加动画吗?全部都得自己弄,现在好了,RecyclerView Item 的添加/删除动画都交给 ItemAnimator。

ItemAnimator 就是负责处理数据添加或者删除时候的动画效果的。

看下官方介绍:

ItemAnimator 类

提供了很多方法,这里就不全部贴出来了,幸好 ItemAnimator 提供了默认实现,如果你不打算自定义的话,那用默认的即可。

下面介绍具体使用。

使用示例

1、导入兼容包
RecyclerView 是兼容包中提供的,要使用,首先需要导入兼容包:

compile 'com.android.support:recyclerview-v7:23.2.0'

注意,不同版本可能稍有差别,例如:条目根布局设置为 much_parent,早期的版本和目前的版本是有区别的。

2、在 Layout 中使用
RecyclerView 在布局中的使用与 ListView 类似,就把它当作 ListView 即可。看代码:

<android.support.v7.widget.RecyclerView
    android:id="@+id/rv_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

这里只是简单使用,所以没有去尝试使用更多的属性了。

3、设置 LayoutManager

LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(mOrientation); // 可以设置LinearLayoutManager.VERTICAL/LinearLayoutManager.HORIZONTAL
// 设置布局管理器
mContentRV.setLayoutManager(layoutManager);

横向布局与纵向布局在示例 Demo 中可以通过开关来切换,自己 Down 下 Demo 去试试吧!

4、设置 ItemAnimator
这里直接用默认的来演示了:

mContentRV.setItemAnimator(new DefaultItemAnimator());

5、自定义 Adapter
这个与 ListView 的 Adapter 稍有不同,具体看注释吧:

public class RecyclerAdapter1 extends RecyclerView.Adapter<RecyclerAdapter1.ViewHolder> {

    private List<String> mDataList;
    private OnRVItemClickListener mListener;

    public RecyclerAdapter1(List<String> dataList) {
        mDataList = dataList;
    }

    // 设置 Item 点击监听
    public void setOnItemClickListener(OnRVItemClickListener listener) {
        mListener = listener;
    }

    // 自定义的 ViewHolder,持有每个 Item 的所有 View 控件
    // 必须继承自 RecyclerView.ViewHolder
    public static class ViewHolder extends RecyclerView.ViewHolder {

        public TextView mTextView;

        public ViewHolder(View itemView) {
            super(itemView);
            mTextView = (TextView) itemView;
        }
    }

    // 获取Item的数量
    @Override
    public int getItemCount() {
        return mDataList.size();
    }

    // 将数据与 View 控件进行绑定
    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        holder.mTextView.setText(mDataList.get(position));
        if (mListener != null) {
            holder.mTextView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mListener.onItemClick(v, position);
                }
            });
        }
    }

    // 创建新 View,被 LayoutManager 所调用
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = View.inflate(parent.getContext(),
                android.R.layout.simple_list_item_1, null);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }
}

6、设置分割线
首先,定义出分割线,直接看代码,都有注释:

public class DIYDecoration extends RecyclerView.ItemDecoration {

    // 采用系统内置的风格的分割线
    private static final int[] attrs = new int[]{android.R.attr.listDivider};
    private Drawable mDivider;
    private int orientation;

    public DIYDecoration(Context context, int orientation) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs);
        mDivider = typedArray.getDrawable(0);
        typedArray.recycle();
        this.orientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        drawHDeraction(c, parent);
        drawVDeraction(c, parent);
    }

    /**
     * 绘制水平方向的分割线
     */
    private void drawHDeraction(Canvas c, RecyclerView parent) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            int top = child.getBottom() + layoutParams.bottomMargin;
            int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    /**
     * 绘制垂直方向的分割线
     */
    private void drawVDeraction(Canvas c, RecyclerView parent) {
        int top = parent.getPaddingTop();
        int bottom = parent.getHeight() - parent.getPaddingBottom();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            int left = child.getRight() + layoutParams.rightMargin;
            int right = left + mDivider.getIntrinsicWidth();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (OrientationHelper.HORIZONTAL == orientation) {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        } else {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        }
    }
}  

(参考自:RecyclerView完全解析,让你从此爱上它(二十八)

然后,为 RecyclerView 设置分割线:

mDIYDecoration = new DIYDecoration(RecyclerViewActivity.this, OrientationHelper.HORIZONTAL);

mContentRV.addItemDecoration(mDIYDecoration);

7、更新数据
ListView 中使用 Adapter 更新数据提供了 notifyDataSetChanged(),而 RecyclerView 的 Adapter 提供了更多,更细粒度的更新数据的方法:

更新数据

基本上都可以见名知义,直接看看用法吧:

mDataList.add(0, "DIY-ITEM:NEW");
if (mAdapter1 != null) {
    mAdapter1.notifyItemInserted(0);
}

mDataList.remove(0);
if (mAdapter1 != null) {
    mAdapter1.notifyItemRemoved(0);
}

在 RecyclerView 中要做局部刷新那就很简单了,RecyclerView 还提供了一些很实用的方法(LayoutManager 中提供的):

  • findFirstVisibleItemPosition() :返回当前第一个可见Item的position
  • findFirstCompletelyVisibleItemPosition() :返回当前第一个完全可见Item的position
  • findLastVisibleItemPosition() :返回当前最后一个可见Item的position
  • findLastCompletelyVisibleItemPosition() :返回当前最后一个完全可见Item的position

这些东西就不再这里详述了,方法名已经给出了它的作用,有兴趣可以自己去试试。

进阶

所谓进阶,就是在 RecyclerView 的基本使用上进行扩展,主要有如下几个方面:

  1. 下拉刷新上拉加载更多
  2. 万能Adapter
  3. 多View Type
  4. AddHeader和AddFooter
  5. Drag
  6. 动画
  7. 嵌套 ViewPager/Gridview

这里只是说一下有这么几个扩展的方向,不在这里展开介绍了,计划以后专门出几篇来介绍。

小结

关于 Android 5.x 的新控件的介绍就到这里了,都只是简单的介绍了下使用方法。

对于其他的控件这样介绍过之后使用应该基本没问题了,但是对于 RecyclerView 的使用,这里只是个引子。关于 RecyclerView 的进阶内容打算用几篇(暂定三篇)来专门介绍,这里就不做展开了。

下一篇就是 Android 6.x 的相关介绍了,未完待续...

附录

Android 5.x 思维导图

项目地址

GitHub

Android学习之RecyclerView
使用StaggeredGridLayoutManager实现瀑布流效果
关于RecyclerView的卡顿问题,对谷歌非常失望
RecyclerView使用介绍
RecyclerView技术栈
RecyclerView完全解析,让你从此爱上它(二十八)

AndroidStudyDemo系列
Web note ad 1