Android5.0 RecyclerView的使用

RecylerView简介

RecylerView是一个高级的ListView。可以很好的维护大数据集的滚动和显示。

RecylerView位置

包名:android.support.v7.widget.RecyclerView
文件地址有两个
1: android-sdk/extras/android/m2repository/com/android/support/recyclerview-v7
2:android-sdk/extras/android/support/v7/recyclerview

RecylerView引用

Android Studio

dependencies {
  compile 'com.android.support:recyclerview-v7:21.0.0'
}

Eclipse

1.在android-sdk/extras/android/support/v7/recyclerview目录下面有libs,里面有jar包,引用此jar包。
2.在android-sdk/extras/android/m2repository/com/android/support/recyclerview-v7目录下根据版本号21.0.0目录可以找到一个名为recyclerview-v7-21.0.0.aar的文件。解压此文件里面有classes.jar,引用此jar包。

针对找不到目录,可以打开Android SDK Manager把最新的资源更新下来即可。

在此推荐使用Android Studio开发Android项目

为什么要使用RecyclerView

RecyclerView适用于无法在一个屏幕范围内展现格式一样的数据时,需要用多行或多列来展示。

比如,当用户滑动使当前一个可视的Item滑出屏幕,这个Item的视图将会被回收并在一个新Item进入可视范围后重新被使用。

可回收利用View是个很实用的功能,它不仅可以减少CPU不断Inflate View的开销,而且可以节省缓存View的内存开销。

ListView具备这样的机制,但是当使用ListView时,显示,回收等功能是紧密耦合在一起的。

RecyclerView不负责显示工作。和ListView不一样的是,RecyclerView不再负责Item的摆放等显示方面的功能。所有和布局、绘制等方面的工作Google都其拆分成不同的类进行管理。所以我们可以自定义各种各样满足定制需求的的功能类。

RecylerView类的说明

涉及到的类

Adapter(android.support.v7.widget.RecyclerView.Adapter)
ViewHolder(android.support.v7.widget.RecyclerView.ViewHolder)
LayoutManager(android.support.v7.widget.RecyclerView.LayoutManager)

Adapter
适配器,跟ListView有关的Adapter是不一样,此Adapter为RecylerView特有。作为一个抽象类,有以下几个抽象方法。

public static abstract class Adapter<VH extends ViewHolder> {
    public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
    public abstract void onBindViewHolder(VH holder, int position);
    public abstract int getItemCount();
    ...
}

1. onCreateViewHolder

直接创建一种可复用的VH或者根据ViewType创建多种VH。

2. onBindViewHolder

数据和VH通过位置position绑定

3. getItemCount

返回有多少条数据

4. ViewHolder

同样是一个抽象类,我们通过继承此类实现view的封装。

5. LayoutManager

布局管理器,RecylerView中数据显示布局方式。目前v7包种提供了三种模式,分别是LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager。

LinearLayoutManager
线性布局,通过方向VERTICAL和HORIZONTAL可以实现垂直和水平的效果。默认为VERTICAL垂直方向。
通过此布局可以实现ListView的效果。垂直方向为普通的ListView显示效果,水平方向即是水平滑动的ListView。

GridLayoutManager
网格布局,继承于LinearLayoutManager,可以指定有几行和方向。
通过此布局可以实现GridView的效果,同样有垂直方向和水平方向。

StaggeredGridLayoutManager
交错网格布局,类似于网格布局,但每个格子的高度或者长度可以不一样。俗称的瀑布流效果,同样有垂直方向和水平方向。

实例代码

引入的包

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.0'
    compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
    compile 'com.android.support:recyclerview-v7:21.0.0'
    compile 'org.roboguice:roboguice:2.0'
    compile 'com.android.support:palette-v7:21.0.0'
}

MainActivity.java

package com.cienet.android;

import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;

import com.cienet.android.adapter.MyAdapter;

import roboguice.activity.RoboFragmentActivity;
import roboguice.inject.InjectView;

public class MainActivity extends RoboFragmentActivity {

    @InjectView(R.id.recyclerView)
    private RecyclerView recyclerView;

    @InjectView(R.id.swipeLayout)
    private SwipeRefreshLayout swipeLayout;

    private MyAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        adapter = new MyAdapter(this, getPicUrls());

        useLinearLayoutManager();
//      useGridLayoutManager();
//      useStaggeredGridLayoutManager();

        recyclerView.setAdapter(adapter);

        // 模拟下拉刷新
        swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        swipeLayout.setRefreshing(false);
                        adapter.notifyDataSetChanged();
                    }
                }, 2000);
            }
        });
    }

    /**
     * 使用线性布局管理器
     * @return
     */
    private void useLinearLayoutManager() {
        // 创建线性布局管理器
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        // 设置显示布局的方向,默认方向是垂直
//        linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        // 设置布局管理器
        recyclerView.setLayoutManager(linearLayoutManager);
    }

    /**
     * 使用网格布局管理器
     * @return
     */
    private void useGridLayoutManager() {
        // 创建网格布局管理器
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
        // 设置显示布局的方向,默认方向是垂直
//        gridLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        // 设置布局管理器
        recyclerView.setLayoutManager(gridLayoutManager);
    }

    /**
     * 使用交错网格布局管理器
     * @return
     */
    private void useStaggeredGridLayoutManager() {
        // 创建交错网格布局管理器
        StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, LinearLayoutManager.VERTICAL);
        // 设置布局管理器
        recyclerView.setLayoutManager(staggeredGridLayoutManager);
    }

    private String[] getPicUrls() {
        String[] picUrls = new String[]{
                "http://img0.hao123.com/data/1_2d6066fea769896573b01478c2312832_510",
                "http://img4.hao123.com/data/1_dd84959fa7910d741d0f4cc9dec79bdd_510",
                "http://img6.hao123.com/data/1_72b86760cbcf0a9251e4c5d28127d3f6_510",
                "http://img3.hao123.com/data/1_ae18d3a1b65a0194a8f5fa2c76f3f8a7_0",
                "http://img5.hao123.com/data/1_bc6ef28f063aa1c0d72daed48a18554a_0",
                "http://img.hao123.com/data/1_62333db73d9fa2fe2a1db6f26edab9f3_0",
                "http://img.hao123.com/data/1_0c4f1dc3daab007063fac855c9825ca5_0",
                "http://img6.hao123.com/data/1_22699180ce1bfef7db27c205a3b9cda2_0",
                "http://img4.hao123.com/data/1_758d06615bb089bcc979aa974442720a_0",
                "http://img.hao123.com/data/1_bf80a0aa0901e1a52ba2cd03c164511e_0"
        };

        return picUrls;
    }
}

MyAdapter.java

package com.cienet.android.adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.cienet.android.R;
import com.nostra13.universalimageloader.core.ImageLoader;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private String[] picUrls;

    public MyAdapter(Context context, String[] picUrls) {
        this.picUrls = picUrls;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        // 加载数据item的布局,生成VH返回
        if (null == viewGroup.getTag()) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_vertical, viewGroup, false);
//        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_horizontal, viewGroup, false);
//        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_gridlayout, viewGroup, false);
//        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_staggered_gridlayout, viewGroup, false);
            viewGroup.setTag(view);
            return new MyViewHolder(view);
        } else {
            return new MyViewHolder((View) viewGroup.getTag());
        }
    }

    @Override
    public void onBindViewHolder(MyViewHolder viewHolder, int i) {
        // 数据绑定
        ImageLoader.getInstance().displayImage(picUrls[i], viewHolder.picImageView);
        viewHolder.picUrl.setText(picUrls[i]);
    }

    @Override
    public int getItemCount() {
        // 返回数据有多少条
        if (null == picUrls) {
            return 0;
        }
        return picUrls.length;
    }

    // 可复用的VH
    public class MyViewHolder extends RecyclerView.ViewHolder {
        public ImageView picImageView;
        public TextView picUrl;

        public MyViewHolder(View itemView) {
            super(itemView);
            picImageView = (ImageView) itemView.findViewById(R.id.imavPic);
            picUrl = (TextView) itemView.findViewById(R.id.tvUrl);
        }
    }
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_red_dark"
    tools:context=".MainActivity">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />
    </android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>

item_vertical.xml

<?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="match_parent"
    android:layout_margin="5dp"
    android:orientation="vertical">

    <!-- 垂直时候使用的ImageView控件-->
    <ImageView
        android:id="@+id/imavPic"
        android:layout_width="wrap_content"
        android:layout_height="150dp"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/tvUrl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:lines="2" />
</LinearLayout>

item_horizontal.xml

<?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="match_parent"
    android:layout_margin="5dp"
    android:orientation="vertical">

    <!-- 水平时候使用的ImageView控件-->
    <ImageView
        android:id="@+id/imavPic"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/tvUrl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:lines="2" />
</LinearLayout>

item_gridlayout.xml

<?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="match_parent"
    android:layout_margin="5dp"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/imavPic"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/tvUrl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:lines="2" />
</LinearLayout>

item_staggered_gridlayout.xml

<?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="match_parent"
    android:layout_margin="5dp"
    android:orientation="vertical">


    <ImageView
        android:id="@+id/imavPic"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/tvUrl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

自己运行验证结果,这里就不上图了。

与ListView的相同与不同

其实官方文档说,RecyclerView是ListView的豪华增强版。它主要包含以下几处新的特性,
如ViewHolder,ItemDecorator,LayoutManager,SmothScroller以及增加或删除item时item动画等。
并且官方推荐我们采用RecyclerView来取代ListView。

1. ViewHolder

ViewHolder是用来保存视图引用的类,无论是ListView亦或是RecyclerView。只不过在ListView中,
ViewHolder需要自己来定义,且这只是一种推荐的使用方式,不使用当然也可以,这不是必须的。
只不过不使用ViewHolder的话,ListView每次getView的时候都会调用findViewById(int),
这将导致ListView性能展示迟缓。而在RecyclerView中使用RecyclerView.ViewHolder则变成了必须,
尽管实现起来稍显复杂,但它却解决了ListView面临的上述不使用自定义ViewHolder时所面临的问题。
RecyclerView.ViewHolder被BaseAdapter使用,以将posiiton绑定到上面
(可以通过API查看 RecyclerView.ViewHolder#getPosition() 方法)。

2. LayoutManager

ListView只能在垂直方向上滚动,Android API没有提供ListView在水平方向上面滚动的支持。
或许有多种方式实现水平滑动,但是ListView并不是设计来做这件事情的。而RecyclerView相较于
ListView,在滚动上面的功能扩展了许多。它可以支持多种类型列表的展示要求。

3. ItemAnimator

Android API中,删除或添加item时,item是无法产生动画效果的。相比较于ListView,
RecyclerView.ItemAnimator则被提供用于在RecyclerView添加、删除或移动item时处理动画效果。
同时,不想自定义ItemAnimator,可以使用已有的 DefaultItemAnimator 。

4. Adapter

ListView的Adapter中,getView是最重要的方法,它将视图跟position绑定起来。
同时我们也能够通过registerDataObserver在Adapter中注册一个观察者。RecyclerView也有这个特性,
RecyclerView.AdapterDataObserver就是这个观察者。ListView有三个Adapter的默认实现,
分别是ArrayAdapter、CursorAdapter和SimpleCursorAdapter。然而,RecyclerView的Adapter则
拥有除了内置的内DB游标和ArrayList的支持之外的所有功能。 RecyclerView.Adapter 的实现的,
我们必须采取措施将数据提供给Adapter,正如BaseAdapter对ListView所做的那样。

5. ItemDecoration

在ListView中如果我们想要在item之间添加间隔符,我们只需要在布局文件中对ListView添加如下属性即可:

1 android:divider="@android:color/transparent"
2 android:dividerHeight="5dp"

RecyclerView在默认情况下并不在item之间展示间隔符。这个问题增加了我们的负担。如果你想要添加间隔符,
你必须使用RecyclerView.ItemDecoration类来实现。或者,可以参考应用官方示例中的 DividerItemDecoration.java文件。

6. OnItemTouchListener

ListView通过AdapterView.OnItemClickListener接口来探测点击事件。而RecyclerView则通过
RecyclerView.OnItemTouchListener接口来探测触摸事件。它虽然增加了实现的难度,但是却给予我们拦截触摸事件
更多的控制权限。

Others

ListView可以设置选择模式,并添加MultiChoiceModeListener,如下所示:

listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        ...
    }
        
    public void onItemCheckedStateChanged(ActionMode mode, int position,
                                                  long id, boolean checked) {
        ...
    }
            
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_item_delete_crime:
                CrimeAdapter adapter = (CrimeAdapter)getListAdapter();
                CrimeLab crimeLab = CrimeLab.get(getActivity());
                for (int i = adapter.getCount() - 1; i >= 0; i--) {
                    if (getListView().isItemChecked(i)) {
                        crimeLab.deleteCrime(adapter.getItem(i));
                    }
                }
                mode.finish();
                adapter.notifyDataSetChanged();
                return true;
            default:
                return false;
        }
            
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) 
        ... 
    }
    
    public void onDestroyActionMode(ActionMode mode) {
        ...
    }
});

而RecyclerView则没有此功能。

总之,通过比较我们可以发现,RecyclerView充满了大量的自定义功能,它可以用于实现复杂的列表或网格,但实现起来稍显得复杂。

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

推荐阅读更多精彩内容