从0系统学Android--3.6 RecyclerView

从0系统学Android--更强大的滚动控件---RecyclerView


本系列文章目录更多精品文章分类

本系列持续更新中.... 参考《第一行代码》

首先说明一点昨天发了一篇关于 ListView 的使用入门文章,得到了大家的一致调侃。我的想法是这样的,虽然现在 ListView 已经被 RecyclerView 替代了,但是本系列作为入门系列,力求内容完整!还是有必要提及一下这么重要的控件的,谁能保证老的项目没有 ListView 呢?

作为入门,一个 Android 开发者不会使用或者根本没有听说过 ListView 说不过去把!

3.6 更强大的滚动控件---RecyclerView

ListView 虽然很强大,但是缺点也不少,比如如果我们刚刚不给它优化的话,效率就会很低。而且 ListView 的扩展性不好,只能实现数据的纵向滚动效果,如果想要实现横向滚动的话就做不到了。

为此 Android 提供了更为强大的控件--RecyclerView。ListView 能够实现的功能它都可以实现,而且还优化了 ListView 的那些不足。还有许多功能是 ListView 所做不多的,就比如横向滑动。

Android 官方更加推荐使用 RecyclerView

3.6.1 RecyclerView 的基本用法

如果你没有使用 androidx 的话,使用 RecyclerView 也是需要引入支持库。

complie ‘com.android.support.recyclerview-v7:24.2.1‘

现在都推荐使用 androidx 库了,可以这样引入

compile ‘androidx.recyclerview:recyclerview:1.0.0‘

然后在 xml 中添加 RecyclerView

<?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:orientation="vertical">
    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/rlv"/>

</LinearLayout>

下面我就来实现和上面的例子一样的效果。

下面需要给 RecyclerView 准备一个适配器,这个适配器需要继承 RecyclerView.Adapter ,并且将泛型指定为 FruitAdapter.ViewHodler 其中 ViewHolder 是我们在 FruitAdapter 中定义的一个内部类。代码如下

public class FruitAdapter2 extends RecyclerView.Adapter<FruitAdapter2.ViewHolder> {
    private List<Fruit> listFruit;

    public FruitAdapter2(List<Fruit> listFruit){
        this.listFruit = listFruit;
    }
    static class ViewHolder extends RecyclerView.ViewHolder{
        ImageView iv;
        TextView tv;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            iv = itemView.findViewById(R.id.iv);
            tv = itemView.findViewById(R.id.tv_name);
        }
    }

    @NonNull
    @Override
    public FruitAdapter2.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fuit_item,parent,false);
        ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull FruitAdapter2.ViewHolder holder, int position) {
        holder.iv.setImageResource(listFruit.get(position).getImgId());
        holder.tv.setText(listFruit.get(position).getName());

    }

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

代码看上很长,其实很简单,容易理解。首先在内部定义了一个类 ViewHolder 这个类是继承自 RecyclerView.ViewHolder 的,在构造方法中需要传入一个 View 参数,这个参数就是我们 RecyclerView 的子项的最外层的布局,然后就可以通过 findViewById() 方法来获取内部的各个控件。

FruitAdapter2 也有一个构造方法,需要传入用于展示的数据源,后续在这个数据源的基础上进行。

FruitAdapter2 继承自 RecyclerView.Adaprer 就必须要实现三个方法onCreateViewHolder()、onBindViewHolder() 和 getItemCount()

  • onCreateViewHolder() 从方法名也很容易可以得出,是用来创建 ViewHolder 的,把此方法内创建的 ViewHolder 通过 return 返回。
  • onBindViewHolder() 就是用于对 RecyclerView 的子项数据绑定到 ViewHolder 上面,这个方法会在每个子项被滚动到屏幕内的时候执行,通过这里的 position 参数得到当前子项的数据,然后设置到 ViewHolder 中就可以了。
  • getItemCount() 方法很简单就告诉 RecyclerView 一共有多少子项,直接返回数据源的长度就可以了。

适配器创建好,就可以使用 RecyclerView 了。

public class RecyclerViewActivity extends AppCompatActivity {

    List<Fruit> list;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化数据源
        initData();
        setContentView(R.layout.activity_recyclerview);
        RecyclerView recyclerView = findViewById(R.id.rlv);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter2 fruitAdapter2 = new FruitAdapter2(list);
        recyclerView.setAdapter(fruitAdapter2);

    }
    public void initData(){
        list  = new ArrayList<>();
        for (int i=0;i<30;i++){
            Fruit fruit = new Fruit("水果"+i,R.mipmap.ic_launcher);
            list.add(fruit);
        }
    }

}

首先获取了 RecyclerView 的实例,然后创建了一个 LinearlayoutManager 的对象,并将它设置到了 RecyclerView 中。LinearLayoutManager 用于指定 RecyclerView 的布局方式,是线性布局的意思,可以实现和 ListView 同样的效果。然后创建了适配器,将数据传入到适配器中,调用 RecyclerView 的 setAdapter 来完成适配器设置,让 RecyclerView 和 数据产生联系。

可以看到 RecyclerView 实现了和 ListView一样的效果,虽然代码量没有明显减少,但是逻辑更加清晰了。这只是 RecyclerView 的最基本的用法而已,下面来一些 ListView 所实现不了的功能。

3.6.2 实现横向滚动和瀑布流布局

Listview 的可扩展性不好,只能实现纵向滚动,如果想要横向滚动的话 ListView 就做不到了。下面用 RecyclerView 来实现横向滚动。

首先对子项布局进行修改一下,目前的布局是水平排列的,不适合水平滚动。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="100dp"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <ImageView
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/iv"/>
    <TextView
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/tv_name"
        android:layout_marginLeft="10dp"/>
</LinearLayout>

然后修改 MainActivity

// 只需要插入这么一句就可以了
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

调用 LinearLayoutManagersetOrientation() 方法来设置布局的排列方向,默认是纵向排列的。

为什么 ListView 很难实现的效果在 RecyclerView 上这么轻松就实现了呢?

主要原因是RecyclerView 出色的设计,ListView 的布局排列是又自身去管理的,而 RecyclerView 的布局排列交给了 LayoutManager ,LayoutManager 有一套可扩展布局排列接口,子类只要按照接口的规范来实现,就可以制定各种不同方式的排列布局了。

除了 LinearLayoutManger ,RecyclerView 还提供了 GridLayoutManager 和 StaggeredGrildLayoutManager 这两种内置的布局排列方式。

GridLayoutManager 可以实现网格布局

StraggeredGridLayoutManager 可以实现瀑布流布局

这里就来实现一下瀑布流

        RecyclerView recyclerView = findViewById(R.id.rlv);
//        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
//        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
//        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setLayoutManager(staggeredGridLayoutManager);

        FruitAdapter2 fruitAdapter2 = new FruitAdapter2(list);
        recyclerView.setAdapter(fruitAdapter2);

别的地方不改变,只需要在代码中 new 一个瀑布流的布局管理器就可以了,里面穿的参数分别是 3 代表会把不会分成 3 列,第二个参数传入的是布局的排列方向,对于瀑布流来说一般就是传入 VERTICAL,水平方向没有什么意义。是不是很简单啊。来看一下效果。

你可以看到和网格布局没有什么区别啊,不要着急那是因为我们数据的原因,导致了所有的子项高度都一样看上去就和网络布局没有什么区别了。

下面我们来改变数据。

    public void initData(){
        list  = new ArrayList<>();
        Random random = new Random();
        for (int i=0;i<30;i++){
            int length =random.nextInt(20)+5;
            StringBuilder stringBuilder  = new StringBuilder();
            for (int j =0;j<length;j++){
                stringBuilder.append("水果").append(i).append("++").append(length);
            }
            Fruit fruit = new Fruit(stringBuilder.toString(),R.mipmap.ic_launcher);
            list.add(fruit);
        }
    }

这里我们巧妙的使用了 Random 让它随机产生数字,用来让 name 的数据变得不一样,从而出现高度不同。

需要注意的:

在使用瀑布布局管理器的时候,子项目的布局的宽度是由分的列数来决定的。也就是说如你的子项布局的宽度设置了 match_parent 的话,StraggeredGridLayoutManager 会自动给它按照比例缩小,而不是截取。比如你给它传入了 3 列,则会缩小成 1 行可以容纳 3 个子项View 的宽度。当然如果你的子项布局的宽度设置成很小,那么就不会缩小了,效果就是子View 和 子 View 之间有很大的空隙,导致不美观。

一般做法就是将子View 的宽度设置为 match_parent 然后设置 margin 来让子项之间互留一点间距。

3.6.3 RecyclerView 的点击事件

RecyclerView 并没有像 ListView 一样提供类似 setOnItemClickListener() 的注册监听的方法。需要我们自己给子项具体的 View 去注册点击事件,相比 ListView来说实现起来复杂一些。

那么你会说了,既然 RecyclerView 这个强大了,各个方面都优于 ListView,但是为什么点击事件没有处理好呢?

其实不是这样的,ListView 的点击事件上的处理并不是那么好,setOnItemClickListener() 方法注册的只是子项的点击事件,如果我想点击子线里面的某一个按钮,通过这种方式就没法直接实现了,虽然 ListView 也可以通过在适配器中做到,但是实现起来就比较麻烦了。为此 RecyclerView 干脆把子项点击事件的监听器给去除了,所有的点击事件都由具体的 View 去注册,更加灵活了。

static class ViewHolder extends RecyclerView.ViewHolder{
        View view;
        ImageView iv;
        TextView tv;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            iv = itemView.findViewById(R.id.iv);
            tv = itemView.findViewById(R.id.tv_name);
            view = itemView;
        }
    }

    @NonNull
    @Override
    public FruitAdapter2.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fuit_item,parent,false);
        final ViewHolder viewHolder = new ViewHolder(view);
        viewHolder.view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = viewHolder.getAdapterPosition();
                Fruit fruit = listFruit.get(position);
                Toast.makeText(v.getContext(),fruit.getName(),Toast.LENGTH_SHORT).show();
            }
        });
        viewHolder.iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(v.getContext(),"img",Toast.LENGTH_SHORT).show();
            }
        });

        return viewHolder;
    }

注意:

为了优化性能,注册点击事件的时候一定要在 onCreateViewHolder 方法中进行。通过 ViewHolder 的 getAdapaterPositon() 我们就清楚的指定我们点击的 View 在 Adapter 中的位置了。

原文地址:https://www.cnblogs.com/sydmobile/p/12036603.html

时间: 2024-10-12 09:48:46

从0系统学Android--3.6 RecyclerView的相关文章

从0系统学Android--4.1探究碎片

从0系统学Android--4.1探究碎片 本系列文章目录:更多精品文章分类 本系列持续更新中.... 初级阶段内容参考<第一行代码> 第四章:手机平板要兼顾--探究碎片 平板电脑和手机最大的区别就在于屏幕的大小,一般手机的屏幕大小会在 3 英寸到 6 英寸之间,而一般平板电脑屏幕大小会在 7 英寸到 10 英寸之间.屏幕大小差距过大会导致同样的界面视觉效果有很大的差异. 为了兼顾手机和平板开发,Android 3.0 引入了碎片的概念,可以让界面在平板上更好的展示. 4.1 碎片是什么 碎片

从0系统学Android--4.2 Fragment 生命周期

从0系统学Android-- 本系列文章目录:更多精品文章分类 本系列持续更新中.... 初级阶段内容参考<第一行代码> 4.3 碎片的生命周期 碎片也有自己的生命周期,并且和 Activity 的生命周期还很像. 4.3.1 碎片的状态和回调 运行状态 当一个碎片可见,并且它所关联的活动正处在运行状态时,这个碎片也处于运行状态. 暂停状态 当一个 Activity 进入暂停状态时,与它相关联的可见碎片就会进入到暂停状态. 停止状态 当一个活动进入了停止状态时,与他关联的碎片就会进入到停止状态

[Android Pro] Android7.0系统 关于Android获取流量计数TrafficStats.getUidRxBytes(uid)和TrafficStats.getUidTxBytes(uid)返回-1解决方案

reference : http://blog.csdn.net/zhangyong7112/article/details/54574214 最近一个关于流量的项目在Android7.0系统的手机上运行,一直获取不到流量的使用数据,查看源码然后发现TrafficStats.getUidRxBytes(uid)和TrafficStats.getUidTxBytes(uid)一直都是返回的-1, // 获取某个网络UID接收和发送字节的总和 long total = TrafficStats.ge

从0系统学Android--1.2 手把手带你搭建开发环境

要想进行程序开发,首先我们需要搭建开发环境,下面就开始搭建环境. 1.2.1 所需的工具 首先 Android 开发是基于 Java 的,因此你需要掌握简单的 Java 语法.会基础的 Java 语法就可以开始. JDK Java 语言的开发工具包,包含了 Java 的运行环境.工具集合.基础类库等 Android SDK Android 的开发工具包,我们进行 Android 开发离不开这个工具包 Android Studio Android Studio 就是我们的开发软件(写代码用的软件)

从0系统学Android-2.3使用 Intent 在 Activity 之间穿梭

2.3 使用 Intent 在 Activity 之间穿梭 在上一节中我们已经学会了如何创建一个 Activity 了.对于一个应用程序来说,肯定不可能只有一个 Activity.下面就来学习多个 Activity 是专门跳转的. 2.3.1 使用显式 Intent 对于创建 Activity 的过程我们已经很熟悉了,下面快速的创建第二个 Activity.取名 SecondActivity.好了第二个 Activity 已经创建好了,创建好了 Activity 后不要忘了需要在 Android

第8章 Android 4.0系统的下载与编译

第8章  Android 4.0系统的下载与编译 本章首先的准备Android下载与编译环境,内容主要分为:准备Android下载与编译环境.下载源码.编译源码及内核源码.下载Android 4.0及Goldfish源码中包括下载并初始化repo工具.下载Android源码.其他源码下载源.下载模拟器Goldfish内核源码.编译Android及Goldfish内核源码中切换到Android源码目录命令:$cd WORKING_DIRECTORY 执行如下命令,加载编译过程中用到的命令.环境变量

Android开发之深入理解Android 7.0系统权限更改相关文档

摘要: Android 6.0之后的版本增加了运行时权限,应用程序在执行每个需要系统权限的功能时,需要添加权限请求代码(默认权限禁止),否则应用程序无法响应:Android 7.0在Android 6.0的基础上,对系统权限进一步更改,这次的权限更改包括三个方面: APP应用程序的私有文件不再向使用者放宽 Intent组件传递file://URI的方式可能给接收器留下无法访问的路径,触发FileUriExposedException异常,推荐使用FileProvider DownloadMana

Android 5.0系统特性全解析

Android 5.0 Lollipop是今年最为期待的产品升级之一.它将带来全新的设计语言,更多人性化的功能,以及最纯正的Google味道. 最近Google陆续发布的Inbox.新版Gmail和今天公布的新版Google Calendar,都让人认识到Material Design的魅力.到底Android 5.0比之前的版本有多大的变化?来看看Engadget是怎么说的. Material Design 正如Engadget所说,Google计划将在所有的产品当中采用Material De

使用 VirtualBox 虚拟机在电脑上运行 Android 4.0 系统,让电脑瞬间变安卓平板

Ref: http://www.iplaysoft.com/android-v4-ics-for-virtualbox.html 随着?Android?手机的各种软件应用越来越多,很多没有购买的朋友都纷纷表示想要试一试.虽然官方的Android SDK开发包中附带有模拟器,但安装使用上较为复杂,不太适合咱们普通青年,于是我们介绍了一款更易用的BlueStacks 安卓模拟器,不过它也还有一些不爽的地方,譬如只能在 Windows 上运行,或是有时不能连接网络. 所以今天给大家介绍另外一款可以在