Android常见布局控件(四)

目录

前言

RecyclerView控件

数据适配器

布局管理器

使用RecyclerView

创建XML布局文件

创建ReActivity类

创建数据适配器

onCreateViewHolder

onBindViewHolder

 完整代码

补充知识

ItemDecoration


前言

在前一篇中,我们讲解了ListView控件,那么本篇我们就来讲解RecyclerView控件

RecyclerView控件

与ListView类似,RecyclerView控件同样也是以列表的形式将数据进行展示,都是为了维护少量的View来进行显示大量的数据。是在android5.0之后新添的控件,用来替代传统的ListView和GridView控件的。

与传统的ListView不同,RecyclerView能通过LayoutManager类来实现横向或者竖向的列表效果、瀑布流效果和GridView效果,而ListView只能实现竖直的列表效果。

格式如下:

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

我们 可以通过在Patale中找到RecylcerView,将其拉到XML文件中

当然,我们也可以手动添加:

数据适配器

RecyclerView控件使用的是RecyclerView.Adapter,该数据适配器将BaseAdapter中的getView()方法拆解为onCreateViewHolder()方法和onBindViewHolder()方法,强制使用ViewHolder类,使代码更加规范化。

布局管理器

LayoutManager布局管理器用来设置每一项view在RecyclerView中的位置布局,以及控件item view的显示或隐藏。当View重用或者回收的时候,LayoutManager都会向Adapter来请求新的数据进行替换原来的数据的内容。这种回收复用机制可以提供性能,避免创建很多的view或者是频繁的调用findViewById()方法。

RcyclerView提供了三种内置的LayoutManager:

  • LinearLayoutManager:线性布局,横向或者纵向滑动列表
  • GridLayoutManager:网格布局
  • StaggerdGridLayoutManager:流式布局,如瀑布流效果。

当然,我们也可以通过继承RecyclerView.LayoutManager来实现一个自定义的LayoutManager。

Animations(动画)效果:

  • RecyclerView对于Item的添加和删除是默认开启动画的,我们也可以通过RecyclerView.ItemAnimator类来定制动画,再通过RecyclerView.setItemAnimator()方法来使用

以下是RecyclerView的一些相关类:

类名说明
RecyclerView.Adapter可以托管数据集合,为每一项Item创建视图并且绑定数据
RecyclerView.ViewHolder承载Item视图的子布局
RecyclerView.LayoutManager负责Item视图的布局的显示管理
RecyclerView.ItemDecoration给每一项Item视图添加子View,例如进行画分割线等
RecyclerView.ItemAnimator负责处理数据添加或者删除时候的动画效果

使用RecyclerView

创建XML布局文件

这里我选择创建一个名为recycle_list.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="wrap_content"
    android:layout_margin="16dp">

    <ImageView
        android:id="@+id/tv"
        android:src="@mipmap/ic_launcher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <LinearLayout
        android:layout_width="200dp"
        android:layout_height="match_parent"
        android:layout_marginLeft="10dp"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_name"
            android:text="李四"
            android:textStyle="bold"
            android:textSize="20sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <TextView
            android:id="@+id/news"
            android:text="李四,你今天在干嘛呢?"
            android:textSize="15sp"
            android:layout_marginTop="15sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
    <TextView
        android:id="@+id/tv_time"
        android:gravity="end"
        android:text="24/8/25\n12:00"
        android:textColor="#999999"
        android:textSize="15sp"
        android:layout_width="wrap_content"
        android:layout_weight="1"
        android:layout_height="match_parent"/>
</LinearLayout>

同时,我们可以创建一个recycle_main.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">
    
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

创建ReActivity类

我们既然已经把布局文件做好,那么现在就是有关java的代码了,我们需要创建一个ReActivity类,在里面来进行绑定布局文件等操作。


import android.os.Bundle;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;

public class ReActivity extends AppCompatActivity {

    private RecyclerView recyclerView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recycle_main);
        Init();
    }

    private void Init() {
        recyclerView = findViewById(R.id.recyclerView);//初始化recyclerView
    }
}

创建数据适配器

在上面,我们已经把聊天的界面以及RecyclerView控件创建出来了,那么接下来我们就来自定义一个适配器继承RecyclerView.Adapter来创建item view以及绑定数据.

onCreateViewHolder

步骤:

  • 创建适配器类继承RecyclerView.Adapter,传入泛型
  • 创建内部类即RecyclerView.ViewHolder的子类,并初始化item控件。
  • 重写RecyclerView.Adapter类的相关方法。

这里我们使用View.inflate来加载v,并将v传给内部类MyAdapter(自己实现)继续实例化。

        private View v;
        private MyAdapter myAdapter;

        @NonNull
        @Override
        public MyAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            v = View.inflate(ReActivity.this,R.layout.recycle_list,null);
            myAdapter = new MyAdapter(v);
            return myAdapter;
        }

这里创建一个内部类MyAdapter继承ViewHolder,绑定传进来的v的控件。

 class MyAdapter extends RecyclerView.ViewHolder {
            TextView name, news, time;
            ImageView image;

            public MyAdapter(@NonNull View itemView) {
                super(itemView);
                name = itemView.findViewById(R.id.tv_name);
                news = itemView.findViewById(R.id.news);
                time = itemView.findViewById(R.id.tv_time);
            }
        }

onBindViewHolder

  • 将获取到的数据设置到对应的控件上。
  • 传入类的holder和position

position相当于下标,既然需要获取类的数据,那么我们需要一个构造方法来初始化类的数据

        private final Context context;
        private final ArrayList nameList,timeList,newsList;
        public Adapter(Context context, ArrayList nameList, ArrayList newsList, ArrayList timeList){
            this.context = context;
            this.nameList = nameList;
            this.newsList = newsList;
            this.timeList = timeList;
        }

到这,我们已经把自定义的适配器Adapter实现完成,完整代码:

/**
     * RecyclerView的适配器类
     */
    class Adapter extends RecyclerView.Adapter<Adapter.MyAdapter> {
        private Context context;
        private ArrayList nameList, newsList, timeList;

        /**
         * Adapter构造方法
         * @param context 上下文环境,此处为ReActivity实例
         * @param nameList 名称数据列表
         * @param newsList 消息数据列表
         * @param timeList 时间数据列表
         */
        public Adapter(Context context, ArrayList nameList, ArrayList newsList, ArrayList timeList){
            this.context = context;
            this.nameList = nameList;
            this.newsList = newsList;
            this.timeList = timeList;
        }

        /**
         * 创建ViewHolder
         * @param parent 父容器,用于容纳RecyclerView项视图
         * @param viewType 视图类型,此处未使用
         * @return 创建的ViewHolder实例
         */
        @NonNull
        @Override
        public MyAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View v = View.inflate(ReActivity.this,R.layout.recycle_list,null); // 加载RecyclerView项布局文件
            MyAdapter myAdapter = new MyAdapter(v); // 创建ViewHolder
            return myAdapter;
        }

        /**
         * 绑定ViewHolder数据
         * @param holder ViewHolder实例,用于展示数据
         * @param position 数据源中的位置
         */
        @Override
        public void onBindViewHolder(@NonNull MyAdapter holder, int position) {
            holder.name.setText(nameList.get(position).toString()); // 设置名称文本
            holder.news.setText(newsList.get(position).toString()); // 设置消息文本
            holder.time.setText(timeList.get(position).toString()); // 设置时间文本
        }

        /**
         * 返回数据源中项的数量
         * @return 数据源大小
         */
        @Override
        public int getItemCount() {
            return nameList.size();
        }

        /**
         * RecyclerView ViewHolder子类
         */
        class MyAdapter extends RecyclerView.ViewHolder {
            TextView name, news, time;
            ImageView image;

            /**
             * MyAdapter构造方法,用于初始化ViewHolder中的视图组件
             * @param itemView RecyclerView项的根视图
             */
            public MyAdapter(@NonNull View itemView) {
                super(itemView);
                name = itemView.findViewById(R.id.tv_name); // 初始化名称文本视图
                news = itemView.findViewById(R.id.news); // 初始化消息文本视图
                time = itemView.findViewById(R.id.tv_time); // 初始化时间文本视图
            }
        }
    }

接下来,我们就在我们定义的Init()方法中来实例化这些类以及调用相关的方法。

  • 首先实例化存储数据的三个ArrayList,分别存储用户名,信息,时间。并模拟生成一些数据。
  • 创建Adapter对象并将上面三个ArratList传入
  • 创建线性布局管理器,并给前面的rercyclerView设置布局为线性布局。
  • 给rercyclerView设置每一项的动画,并且设置分割线和适配器
/**
     * 初始化方法,用于初始化RecyclerView组件及适配器
     */
    private void Init() {
        recyclerView = findViewById(R.id.recyclerView); // 初始化RecyclerView组件
        // 初始化存储数据的ArrayList
        ArrayList names = new ArrayList();
        ArrayList newsList = new ArrayList();
        ArrayList timeList = new ArrayList();
        // 循环生成模拟数据
        for(int i=0;i<50;i++){
            names.add("00"+i);
            newsList.add("你今天在干嘛呢?");
            timeList.add("2020-01-01 \n15:"+i);
        }
        // 创建Adapter实例并传入数据源
        Adapter adapter = new Adapter(this ,names,newsList,timeList);
        // 创建LinearLayoutManager实例,用于管理RecyclerView的布局
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager); // 设置RecyclerView的布局管理器
        recyclerView.setItemAnimator(new DefaultItemAnimator()); // 设置RecyclerView的项动画
        recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); // 添加分割线装饰
        recyclerView.setAdapter(adapter); // 设置RecyclerView的适配器
    }

 完整代码

ReActivity.java

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

// ReActivity类继承自AppCompatActivity,用于展示RecyclerView实现的列表界面
public class ReActivity extends AppCompatActivity {

    // 声明RecyclerView对象
    private RecyclerView recyclerView;

    /**
     * onCreate方法,在活动创建时调用
     * @param savedInstanceState 可能存在的保存实例状态Bundle,此处为可空
     */
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recycle_main); // 设置活动的布局文件
        Init(); // 初始化组件和数据
    }

    /**
     * 初始化方法,用于初始化RecyclerView组件及适配器
     */
    private void Init() {
        recyclerView = findViewById(R.id.recyclerView); // 初始化RecyclerView组件
        // 初始化存储数据的ArrayList
        ArrayList names = new ArrayList();
        ArrayList newsList = new ArrayList();
        ArrayList timeList = new ArrayList();
        // 循环生成模拟数据
        for(int i=0;i<50;i++){
            names.add("00"+i);
            newsList.add("你今天在干嘛呢?");
            timeList.add("2020-01-01 \n15:"+i);
        }
        // 创建Adapter实例并传入数据源
        Adapter adapter = new Adapter(this ,names,newsList,timeList);
        // 创建LinearLayoutManager实例,用于管理RecyclerView的布局
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager); // 设置RecyclerView的布局管理器
        recyclerView.setItemAnimator(new DefaultItemAnimator()); // 设置RecyclerView的项动画
        recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); // 添加分割线装饰
        recyclerView.setAdapter(adapter); // 设置RecyclerView的适配器
    }

    /**
     * RecyclerView的适配器类
     */
    class Adapter extends RecyclerView.Adapter<Adapter.MyAdapter> {
        private Context context;
        private ArrayList nameList, newsList, timeList;

        /**
         * Adapter构造方法
         * @param context 上下文环境,此处为ReActivity实例
         * @param nameList 名称数据列表
         * @param newsList 消息数据列表
         * @param timeList 时间数据列表
         */
        public Adapter(Context context, ArrayList nameList, ArrayList newsList, ArrayList timeList){
            this.context = context;
            this.nameList = nameList;
            this.newsList = newsList;
            this.timeList = timeList;
        }

        /**
         * 创建ViewHolder
         * @param parent 父容器,用于容纳RecyclerView项视图
         * @param viewType 视图类型,此处未使用
         * @return 创建的ViewHolder实例
         */
        @NonNull
        @Override
        public MyAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View v = View.inflate(ReActivity.this,R.layout.recycle_list,null); // 加载RecyclerView项布局文件
            MyAdapter myAdapter = new MyAdapter(v); // 创建ViewHolder
            return myAdapter;
        }

        /**
         * 绑定ViewHolder数据
         * @param holder ViewHolder实例,用于展示数据
         * @param position 数据源中的位置
         */
        @Override
        public void onBindViewHolder(@NonNull MyAdapter holder, int position) {
            holder.name.setText(nameList.get(position).toString()); // 设置名称文本
            holder.news.setText(newsList.get(position).toString()); // 设置消息文本
            holder.time.setText(timeList.get(position).toString()); // 设置时间文本
        }

        /**
         * 返回数据源中项的数量
         * @return 数据源大小
         */
        @Override
        public int getItemCount() {
            return nameList.size();
        }

        /**
         * RecyclerView ViewHolder子类
         */
        class MyAdapter extends RecyclerView.ViewHolder {
            TextView name, news, time;
            ImageView image;

            /**
             * MyAdapter构造方法,用于初始化ViewHolder中的视图组件
             * @param itemView RecyclerView项的根视图
             */
            public MyAdapter(@NonNull View itemView) {
                super(itemView);
                name = itemView.findViewById(R.id.tv_name); // 初始化名称文本视图
                news = itemView.findViewById(R.id.news); // 初始化消息文本视图
                time = itemView.findViewById(R.id.tv_time); // 初始化时间文本视图
            }
        }
    }
}

结合前面的两个XML布局,运行即可得到:

补充知识

ItemDecoration

在使用RecyclerView的时候,如果我们想要设置分割线,那么我们可以通过ItemDecoration。例如:

        DividerItemDecoration decoration = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);分割线
        recyclerView.addItemDecoration(decoration); // 添加分割线装饰

 

Item动画

在RecyclerView中提供了默认的IntemAnimator实现类: DefaulttemAnimator。我们可以借助该类帮我们实现一些动画。

        DefaultItemAnimator animator = new DefaultItemAnimator();
        animator.setAddDuration(1000);
        animator.setRemoveDuration(1000);
        recyclerView.setItemAnimator(animator); // 设置RecyclerView的项动画

在前面的基础上,在recycle_main.xml聊天界面的上部添加两个按钮(添加和删除)

 <LinearLayout
        android:layout_marginLeft="80dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/add"
            android:text="添加Item"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/del"
            android:text="删除Item"
            android:layout_marginLeft="60dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>

同时在ReActivity类中添加对应点击事件(由于代码有改动,所以这里直接放全部代码):

package com.example.newapptext1;

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

// ReActivity类继承自AppCompatActivity,用于展示RecyclerView实现的列表界面
public class ReActivity extends AppCompatActivity implements View.OnClickListener {

    // 声明RecyclerView对象
    private RecyclerView recyclerView;
    private Adapter adapter;
    private ArrayList names;
    private ArrayList newsList;
    private ArrayList timeList;

    /**
     * onCreate方法,在活动创建时调用
     * @param savedInstanceState 可能存在的保存实例状态Bundle,此处为可空
     */
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recycle_main); // 设置活动的布局文件
        Init(); // 初始化组件和数据
    }

    /**
     * 初始化方法,用于初始化RecyclerView组件及适配器
     */
    private void Init() {
        recyclerView = findViewById(R.id.recyclerView); // 初始化RecyclerView组件
        // 初始化存储数据的ArrayList
        names = new ArrayList();
        newsList = new ArrayList();
        timeList = new ArrayList();
        // 循环生成模拟数据
        for(int i=0;i<5;i++){
            names.add("00"+i);
            newsList.add("你今天在干嘛呢?");
            timeList.add("2020-01-01 \n15:"+i);
        }
        // 创建Adapter实例并传入数据源
        adapter = new Adapter(this , names, newsList, timeList);
        // 创建LinearLayoutManager实例,用于管理RecyclerView的布局
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); // 设置垂直方向
        recyclerView.setLayoutManager(linearLayoutManager); // 设置RecyclerView的布局管理器
        DefaultItemAnimator animator = new DefaultItemAnimator();
        animator.setAddDuration(1000);
        animator.setRemoveDuration(1000);
        recyclerView.setItemAnimator(animator); // 设置RecyclerView的项动画
        DividerItemDecoration decoration = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);分割线
        recyclerView.addItemDecoration(decoration); // 添加分割线装饰
        recyclerView.setAdapter(adapter); // 设置RecyclerView的适配器

        //添加点击事件
        findViewById(R.id.add).setOnClickListener(this);
        findViewById(R.id.del).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        // 判断点击的视图是否为添加按钮
        if(view.getId()==R.id.add){
            // 向列表中添加新的名字和消息,用于模拟新消息的到来
            names.add(1,"新添");
            newsList.add(1,"你在干嘛呢?");
            timeList.add(1,"2020-01-01 \n15:00");
            // 通知适配器插入新项,以便更新UI
            adapter.notifyItemInserted(1);
        } else if(view.getId()==R.id.del){
            // 判断点击的视图是否为删除按钮
            // 从名字、消息和时间列表中移除指定位置的元素,以模拟数据删除操作
            names.remove(1);
            newsList.remove(1);
            timeList.remove(1);
            // 通知适配器移除项,以便更新UI
            adapter.notifyItemRemoved(1);
        }
    }



    /**
     * RecyclerView的适配器类
     */
    class Adapter extends RecyclerView.Adapter<Adapter.MyAdapter> {
        private Context context;
        private ArrayList nameList, newsList, timeList;

        /**
         * Adapter构造方法
         * @param context 上下文环境,此处为ReActivity实例
         * @param nameList 名称数据列表
         * @param newsList 消息数据列表
         * @param timeList 时间数据列表
         */
        public Adapter(Context context, ArrayList nameList, ArrayList newsList, ArrayList timeList){
            this.context = context;
            this.nameList = nameList;
            this.newsList = newsList;
            this.timeList = timeList;
        }

        /**
         * 创建ViewHolder
         * @param parent 父容器,用于容纳RecyclerView项视图
         * @param viewType 视图类型,此处未使用
         * @return 创建的ViewHolder实例
         */
        @NonNull
        @Override
        public MyAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View v = View.inflate(ReActivity.this,R.layout.recycle_list,null); // 加载RecyclerView项布局文件
            MyAdapter myAdapter = new MyAdapter(v); // 创建ViewHolder
            return myAdapter;
        }

        /**
         * 绑定ViewHolder数据
         * @param holder ViewHolder实例,用于展示数据
         * @param position 数据源中的位置
         */
        @Override
        public void onBindViewHolder(@NonNull MyAdapter holder, int position) {
            holder.name.setText(nameList.get(position).toString()); // 设置名称文本
            holder.news.setText(newsList.get(position).toString()); // 设置消息文本
            holder.time.setText(timeList.get(position).toString()); // 设置时间文本
        }

        /**
         * 返回数据源中项的数量
         * @return 数据源大小
         */
        @Override
        public int getItemCount() {
            return nameList.size();
        }

        /**
         * RecyclerView ViewHolder子类
         */
        class MyAdapter extends RecyclerView.ViewHolder {
            TextView name, news, time;
            ImageView image;

            /**
             * MyAdapter构造方法,用于初始化ViewHolder中的视图组件
             * @param itemView RecyclerView项的根视图
             */
            public MyAdapter(@NonNull View itemView) {
                super(itemView);
                name = itemView.findViewById(R.id.tv_name); // 初始化名称文本视图
                news = itemView.findViewById(R.id.news); // 初始化消息文本视图
                time = itemView.findViewById(R.id.tv_time); // 初始化时间文本视图
            }
        }
    }
}

运行之后可以得到:

当然,还有个刷新功能,后面再跟大家讲。


以上就是本篇所有内容,若有不足,欢迎指正~ 

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小猪同学hy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值