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

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完全解析,让你从此爱上它(二十八)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容