IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android RecyclerView -> 正文阅读

[移动开发]Android RecyclerView

基础使用 - 纵向滚动

添加依赖库

mytest1\build.gradle :

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

修改xml布局

mytest1\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

mytest1\src\main\res\layout\fruit_item.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="wrap_content">

    <TextView
        android:id="@+id/fruit_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        />
    <TextView
        android:id="@+id/fruit_price_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="20dp"
        android:textSize="30sp"
        />

</LinearLayout>

创建Fruit实体类

mytest1\src\main\java\com\example\mytest1\MyFruit.java

public class MyFruit {
    private String name;
    private float price;

    public MyFruit(String name, float price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public float getPrice() {
        return price;
    }
}

自定义适配器Adapter

mytest1\src\main\java\com\example\mytest1\MyFruitAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

// 适配器,继承自 RecyclerView.Adapter,泛型指定为MyFruitAdapter.MyViewHolder
public class MyFruitAdapter extends RecyclerView.Adapter<MyFruitAdapter.MyViewHolder> {
    private List<MyFruit> mFruitList;

    // 构造函数,list为需要展示的数据源
    public MyFruitAdapter(List<MyFruit> list) {
        this.mFruitList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.fruit_item, parent, false);

        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyFruit mFruit = mFruitList.get(position);
        holder.mFruitName.setText(mFruit.getName());
        holder.mFruitPrice.setText(mFruit.getPrice()+"元");
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }

    // 内部类 继承自RecyclerView.ViewHolder
    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView mFruitName;
        TextView mFruitPrice;

        // 构造函数 通过传入的view,可以获取到 RecyclerView子项 的最外成布局
        public MyViewHolder(View itemView) {
            super(itemView);
            mFruitName = itemView.findViewById(R.id.fruit_name_tv);
            mFruitPrice = itemView.findViewById(R.id.fruit_price_tv);
        }
    }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.Window;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        List<MyFruit> mFruitList = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("苹果" + i, (float) (1 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("香蕉" + i, (float) (2 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("橘子" + i, (float) (3 + 0.01 * i)));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 LinearLayoutManager 对象 ,即线性布局
        LinearLayoutManager mManager = new LinearLayoutManager(this);

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyFruitAdapter mAdapter = new MyFruitAdapter(mFruitList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

横向滚动

添加依赖库

mytest2\build.gradle :

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

修改xml布局

mytest2\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

mytest2\src\main\res\layout\fruit_item.xml :
这个是 RecyclerView 的子项布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_marginLeft="20dp"
    android:background="#ff0"
    >

    <TextView
        android:id="@+id/fruit_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        />
    <TextView
        android:id="@+id/fruit_price_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        />

</LinearLayout>

创建Fruit实体类

mytest2\src\main\java\com\example\mytest2\MyFruit.java

public class MyFruit {
    private String name;
    private float price;

    public MyFruit(String name, float price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public float getPrice() {
        return price;
    }
}

自定义适配器Adapter

mytest2\src\main\java\com\example\mytest2\MyFruitAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

// 适配器,继承自 RecyclerView.Adapter,泛型指定为MyFruitAdapter.MyViewHolder
public class MyFruitAdapter extends RecyclerView.Adapter<MyFruitAdapter.MyViewHolder> {
    private List<MyFruit> mFruitList;

    // 构造函数,list为需要展示的数据源
    public MyFruitAdapter(List<MyFruit> list) {
        this.mFruitList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.fruit_item, parent, false);

        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyFruit mFruit = mFruitList.get(position);
        holder.mFruitName.setText(mFruit.getName());
        holder.mFruitPrice.setText(mFruit.getPrice()+"元");
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }

    // 内部类 继承自RecyclerView.ViewHolder
    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView mFruitName;
        TextView mFruitPrice;

        // 构造函数 通过传入的view,可以获取到 RecyclerView子项 的最外成布局
        public MyViewHolder(View itemView) {
            super(itemView);
            mFruitName = itemView.findViewById(R.id.fruit_name_tv);
            mFruitPrice = itemView.findViewById(R.id.fruit_price_tv);
        }
    }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.Window;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        List<MyFruit> mFruitList = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("苹果" + i, (float) (1 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("香蕉" + i, (float) (2 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("橘子" + i, (float) (3 + 0.01 * i)));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 LinearLayoutManager 对象 ,即线性布局
        LinearLayoutManager mManager = new LinearLayoutManager(this);

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyFruitAdapter mAdapter = new MyFruitAdapter(mFruitList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

瀑布流布局

添加依赖库

mytest2\build.gradle :

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

修改xml布局

mytest3\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

mytest3\src\main\res\layout\fruit_item.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="wrap_content"
    android:orientation="vertical"
    android:layout_margin="5dp"
    android:background="#FF44FF"
    >

    <TextView
        android:id="@+id/fruit_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        />
    <TextView
        android:id="@+id/fruit_content_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_gravity="left"
        android:layout_marginTop="10dp"
        />

</LinearLayout>

创建Fruit实体类

mytest3\src\main\java\com\example\mytest3\MyFruit.java

public class MyFruit {
    private String name;
    private String content;

    public MyFruit(String name, String content) {
        this.name = name;
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public String getContent() {
        return content;
    }
}

自定义适配器Adapter

mytest3\src\main\java\com\example\mytest3\MyFruitAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

// 适配器,继承自 RecyclerView.Adapter,泛型指定为MyFruitAdapter.MyViewHolder
public class MyFruitAdapter extends RecyclerView.Adapter<MyFruitAdapter.MyViewHolder> {
    private List<MyFruit> mFruitList;

    // 构造函数,list为需要展示的数据源
    public MyFruitAdapter(List<MyFruit> list) {
        this.mFruitList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.fruit_item, parent, false);

        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyFruit mFruit = mFruitList.get(position);
        holder.mFruitName.setText(mFruit.getName());
        holder.mFruitContent.setText(mFruit.getContent());
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }

    // 内部类 继承自RecyclerView.ViewHolder
    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView mFruitName;
        TextView mFruitContent;

        // 构造函数 通过传入的view,可以获取到 RecyclerView子项 的最外成布局
        public MyViewHolder(View itemView) {
            super(itemView);
            mFruitName = itemView.findViewById(R.id.fruit_name_tv);
            mFruitContent = itemView.findViewById(R.id.fruit_content_tv);
        }
    }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import android.view.Window;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        List<MyFruit> mFruitList = new ArrayList<>();
        for (int i = 1; i <= 15; i++) {
            StringBuilder builder = new StringBuilder();
            for (int j = 0; j < new Random().nextInt(10) + 1; j++){
                builder.append("苹果");
            }
            mFruitList.add(new MyFruit("苹果" + i, builder.toString()));
        }
        for (int i = 1; i <= 15; i++) {
            StringBuilder builder = new StringBuilder();
            for (int j = 0; j < new Random().nextInt(10) + 1; j++){
                builder.append("香蕉");
            }
            mFruitList.add(new MyFruit("香蕉" + i, builder.toString()));
        }
        for (int i = 1; i <= 15; i++) {
            StringBuilder builder = new StringBuilder();
            for (int j = 0; j < new Random().nextInt(10) + 1; j++){
                builder.append("橘子");
            }
            mFruitList.add(new MyFruit("橘子" + i, builder.toString()));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 StaggeredGridLayoutManager 实例
        // int spanCount : 指定布局的列数
        // int orientation : 指定布局排列的方向
        StaggeredGridLayoutManager mManager = new
                StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyFruitAdapter mAdapter = new MyFruitAdapter(mFruitList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

点击事件

添加依赖库

mytest4\build.gradle :

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

修改xml布局

mytest4\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

mytest4\src\main\res\layout\fruit_item.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="wrap_content">

    <TextView
        android:id="@+id/fruit_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        />
    <TextView
        android:id="@+id/fruit_price_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="20dp"
        android:textSize="30sp"
        />

</LinearLayout>

创建Fruit实体类

mytest4\src\main\java\com\example\mytest4\MyFruit.java

public class MyFruit {
    private String name;
    private float price;

    public MyFruit(String name, float price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public float getPrice() {
        return price;
    }
}

自定义适配器Adapter

mytest4\src\main\java\com\example\mytest4\MyFruitAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

// 适配器,继承自 RecyclerView.Adapter,泛型指定为MyFruitAdapter.MyViewHolder
public class MyFruitAdapter extends RecyclerView.Adapter<MyFruitAdapter.MyViewHolder> {
    private List<MyFruit> mFruitList;

    // 构造函数,list为需要展示的数据源
    public MyFruitAdapter(List<MyFruit> list) {
        this.mFruitList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.fruit_item, parent, false);

        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyFruit mFruit = mFruitList.get(position);
        holder.mFruitName.setText(mFruit.getName());
        holder.mFruitPrice.setText(mFruit.getPrice()+"元");
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }

    // 内部类 继承自RecyclerView.ViewHolder
    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView mFruitName;
        TextView mFruitPrice;

        // 构造函数 通过传入的view,可以获取到 RecyclerView子项 的最外成布局
        public MyViewHolder(View itemView) {
            super(itemView);
            mFruitName = itemView.findViewById(R.id.fruit_name_tv);
            mFruitPrice = itemView.findViewById(R.id.fruit_price_tv);
        }
    }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.Window;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        List<MyFruit> mFruitList = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("苹果" + i, (float) (1 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("香蕉" + i, (float) (2 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("橘子" + i, (float) (3 + 0.01 * i)));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 LinearLayoutManager 对象 ,即线性布局
        LinearLayoutManager mManager = new LinearLayoutManager(this);

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyFruitAdapter mAdapter = new MyFruitAdapter(mFruitList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

分割线1 - 在适配器 onCreateViewHolder 中设置

在 Adapter 的 onCreateViewHolder 方法中进行设置 margin,以达到分割线的效果。

添加依赖库

mytest5\build.gradle :

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

修改xml布局

mytest5\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/black"
        />

</LinearLayout>

mytest5\src\main\res\layout\fruit_item.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="wrap_content"
    android:background="#9D9D9D"
    >

    <TextView
        android:id="@+id/fruit_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="30sp"
        android:gravity="center"
        />

    <TextView
        android:id="@+id/fruit_price_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:paddingLeft="20dp"
        android:textSize="30sp"
        android:gravity="center"
        />

</LinearLayout>

创建Fruit实体类

mytest5\src\main\java\com\example\mytest5\MyFruit.java

public class MyFruit {
    private String name;
    private float price;

    public MyFruit(String name, float price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public float getPrice() {
        return price;
    }
}

自定义适配器Adapter

mytest5\src\main\java\com\example\mytest5\MyFruitAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

// 继承自 RecyclerView.Adapter
public class MyFruitAdapter extends RecyclerView.Adapter<MyFruitAdapter.MyViewHolder> {
    private List<MyFruit> mFruitList;

    // 构造函数,list为需要展示的数据源
    public MyFruitAdapter(List<MyFruit> list) {
        this.mFruitList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.fruit_item, parent, false);

        //  ************ 设置分割线 使用margin的方式 ***************
        RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
        layoutParams.topMargin = 1;
        view.setLayoutParams(layoutParams);
        // *****************************************************

        // 创建自定义 viewHolder 实例
        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyFruit mFruit = mFruitList.get(position);
        holder.mFruitName.setText(mFruit.getName());
        holder.mFruitPrice.setText(mFruit.getPrice()+"元");
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }

    // 内部类 继承自RecyclerView.ViewHolder
    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView mFruitName;
        TextView mFruitPrice;

        // 构造函数 通过传入的view,可以获取到 RecyclerView子项 的最外成布局
        public MyViewHolder(View itemView) {
            super(itemView);
            mFruitName = itemView.findViewById(R.id.fruit_name_tv);
            mFruitPrice = itemView.findViewById(R.id.fruit_price_tv);
        }
    }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.Window;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

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

        List<MyFruit> mFruitList = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("苹果" + i, (float) (1 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("香蕉" + i, (float) (2 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("橘子" + i, (float) (3 + 0.01 * i)));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 LinearLayoutManager 对象 ,即线性布局
        LinearLayoutManager mManager = new LinearLayoutManager(this);

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyFruitAdapter mAdapter = new MyFruitAdapter(mFruitList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

分割线2 - 在 ItemDecoration 中 getItemOffsets 设置 rect

???? 在自定义 ItemDecoration 中,在 getItemOffsets 方法中 设置 rect,设置后,会露出底色,就是背景色,颜色也就取决于背景色了。

添加依赖库

mytest6\build.gradle :

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

修改xml布局

mytest6\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/black"
        />

</LinearLayout>

mytest6\src\main\res\layout\fruit_item.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="wrap_content"
    android:background="#9D9D9D"
    >

    <TextView
        android:id="@+id/fruit_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="30sp"
        android:gravity="center"
        />

    <TextView
        android:id="@+id/fruit_price_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:paddingLeft="20dp"
        android:textSize="30sp"
        android:gravity="center"
        />

</LinearLayout>

创建Fruit实体类

mytest6\src\main\java\com\example\mytest6\MyFruit.java

public class MyFruit {
    private String name;
    private float price;

    public MyFruit(String name, float price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public float getPrice() {
        return price;
    }
}

自定义适配器Adapter

mytest6\src\main\java\com\example\mytest6\MyFruitAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

// 继承自 RecyclerView.Adapter
public class MyFruitAdapter extends RecyclerView.Adapter<MyFruitAdapter.MyViewHolder> {
    private List<MyFruit> mFruitList;

    // 构造函数,list为需要展示的数据源
    public MyFruitAdapter(List<MyFruit> list) {
        this.mFruitList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.fruit_item, parent, false);

        // 创建自定义 viewHolder 实例
        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyFruit mFruit = mFruitList.get(position);
        holder.mFruitName.setText(mFruit.getName());
        holder.mFruitPrice.setText(mFruit.getPrice()+"元");
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }

    // 内部类 继承自RecyclerView.ViewHolder
    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView mFruitName;
        TextView mFruitPrice;

        // 构造函数 通过传入的view,可以获取到 RecyclerView子项 的最外成布局
        public MyViewHolder(View itemView) {
            super(itemView);
            mFruitName = itemView.findViewById(R.id.fruit_name_tv);
            mFruitPrice = itemView.findViewById(R.id.fruit_price_tv);
        }
    }
}

自定义ItemDecoration

import android.graphics.Rect;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;

public class MyItemDecoration extends RecyclerView.ItemDecoration {
    /**
     * 获取Item的偏移量,结果保存在第一个参数outRect中
     * @param outRect 是一个矩形,可以设置上下左右四个值。对应view在四个方向上的偏移量。默认是全为0的Rect
     *                设置rect,会透漏出下面的背景色,看起来像是分割线,颜色取决于背景色
     * @param view RecyclerView 中的 item
     * @param parent RecyclerView本身
     * @param state 状态
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        // 方式1
//        outRect.top = 1;
        // 方式2
        outRect.set(0, 1, 0, 0);
    }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.Window;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        List<MyFruit> mFruitList = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("苹果" + i, (float) (1 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("香蕉" + i, (float) (2 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("橘子" + i, (float) (3 + 0.01 * i)));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 LinearLayoutManager 对象 ,即线性布局
        LinearLayoutManager mManager = new LinearLayoutManager(this);

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyFruitAdapter mAdapter = new MyFruitAdapter(mFruitList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);

        // 设置分割线
        mRecyclerView.addItemDecoration(new MyItemDecoration());
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

分割线3 - getItemOffsets、onDraw、onDrawOver

???? 在自定义 ItemDecoration 中,在 getItemOffsets 方法中 设置 rect,设置后,会露出底色,就是背景色,颜色也就取决于背景色了;
???? 然后在 onDraw 方法 或 onDrawOver 方法中,再在 rect 区域再次绘制一个 rect。

添加依赖库

mytest7\build.gradle :

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

修改xml布局

mytest7\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/black"
        android:padding="10dp"
        />

</LinearLayout>

mytest7\src\main\res\layout\fruit_item.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="wrap_content"
    android:background="#9D9D9D"
    >

    <TextView
        android:id="@+id/fruit_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="30sp"
        android:gravity="center"
        />

    <TextView
        android:id="@+id/fruit_price_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:paddingLeft="20dp"
        android:textSize="30sp"
        android:gravity="center"
        />

</LinearLayout>

创建Fruit实体类

mytest7\src\main\java\com\example\mytest7\MyFruit.java

public class MyFruit {
    private String name;
    private float price;

    public MyFruit(String name, float price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public float getPrice() {
        return price;
    }
}

自定义适配器Adapter

mytest7\src\main\java\com\example\mytest7\MyFruitAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

// 继承自 RecyclerView.Adapter
public class MyFruitAdapter extends RecyclerView.Adapter<MyFruitAdapter.MyViewHolder> {
    private List<MyFruit> mFruitList;

    // 构造函数,list为需要展示的数据源
    public MyFruitAdapter(List<MyFruit> list) {
        this.mFruitList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.fruit_item, parent, false);

        // 创建自定义 viewHolder 实例
        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyFruit mFruit = mFruitList.get(position);
        holder.mFruitName.setText(mFruit.getName());
        holder.mFruitPrice.setText(mFruit.getPrice()+"元");
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }

    // 内部类 继承自RecyclerView.ViewHolder
    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView mFruitName;
        TextView mFruitPrice;

        // 构造函数 通过传入的view,可以获取到 RecyclerView子项 的最外成布局
        public MyViewHolder(View itemView) {
            super(itemView);
            mFruitName = itemView.findViewById(R.id.fruit_name_tv);
            mFruitPrice = itemView.findViewById(R.id.fruit_price_tv);
        }
    }
}

自定义ItemDecoration

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

public class MyItemDecoration extends RecyclerView.ItemDecoration {

    private int mDividerHeight;

    /**
     * 获取Item的偏移量,结果保存在第一个参数outRect中
     * @param outRect 是一个矩形,可以设置上下左右四个值。对应view在四个方向上的偏移量。默认是全为0的Rect
     *                设置rect,会透漏出下面的背景色,看起来像是分割线,颜色取决于背景色
     * @param view RecyclerView 中的 item
     * @param parent RecyclerView本身
     * @param state 状态
     */
    @Override
    public void getItemOffsets(Rect outRect, View view,
                               RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        mDividerHeight = 2;

        // 方式1
//        outRect.top = mDividerHeight;
        // 方式2
        outRect.set(0, mDividerHeight, 0, 0);
    }

    /**
     * 会在 绘制item之前调用,绘制的内容,会显示在 item 的底部,如果有重叠,会被 item 挡住
     * @param c 为 Canvas
     * @param parent RecyclerView本身
     * @param state 状态
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

//        Paint mPaint = new Paint();
//        mPaint.setColor(Color.BLUE);
//
//        for (int i = 0; i < parent.getChildCount(); i++) {
//            View view = parent.getChildAt(i);
//            int index = parent.getChildAdapterPosition(view);
//
//            // 第一个 item 子项不绘制分割线
//            if (index == 0) {
//                continue;
//            }
//
//            float mLeft = parent.getPaddingLeft();
//            float mTop = view.getTop() - mDividerHeight;
//            float mRight = parent.getWidth() - parent.getPaddingRight();
//            float mBottom = view.getTop();
//
//            c.drawRect(mLeft, mTop, mRight, mBottom, mPaint);
//        }
    }

    /**
     * 会在 绘制item之后调用,绘制的内容,会显示在 item 的上部,如果有重叠,会把 item 挡住
     * @param c 为 Canvas
     * @param parent RecyclerView本身
     * @param state 状态
     */
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);

        Paint mPaint = new Paint();
        mPaint.setColor(Color.RED);

        for (int i = 0; i < parent.getChildCount(); i++) {
            View view = parent.getChildAt(i);
            int index = parent.getChildAdapterPosition(view);

            // 第一个 item 子项不绘制分割线
            if (index == 0) {
                continue;
            }

            float mLeft = parent.getPaddingLeft();
            float mTop = view.getTop() - mDividerHeight;
            float mRight = parent.getWidth() - parent.getPaddingRight();
            float mBottom = view.getTop();

            c.drawRect(mLeft, mTop, mRight, mBottom, mPaint);
        }
    }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.Window;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        List<MyFruit> mFruitList = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("苹果" + i, (float) (1 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("香蕉" + i, (float) (2 + 0.01 * i)));
        }
        for (int i = 1; i <= 10; i++) {
            mFruitList.add(new MyFruit("橘子" + i, (float) (3 + 0.01 * i)));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 LinearLayoutManager 对象 ,即线性布局
        LinearLayoutManager mManager = new LinearLayoutManager(this);

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyFruitAdapter mAdapter = new MyFruitAdapter(mFruitList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);

        // 设置分割线
        mRecyclerView.addItemDecoration(new MyItemDecoration());
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

分割线4 - 实现每个分组都有一个头部分割线

添加依赖库

mytest8\build.gradle :

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

修改xml布局

mytest8\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/black"
        android:paddingTop="100dp"
        />

</LinearLayout>

mytest8\src\main\res\layout\fruit_item.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="wrap_content"
    android:background="#9D9D9D"
    >

    <TextView
        android:id="@+id/fruit_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_weight="1"
        android:textSize="20sp"
        android:textColor="#90EE90"
        android:gravity="center"
        />

    <TextView
        android:id="@+id/fruit_price_tv"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_weight="1"
        android:paddingLeft="20dp"
        android:textSize="20sp"
        android:textColor="#90EE90"
        android:gravity="center"
        />

</LinearLayout>

创建Fruit实体类

mytest8\src\main\java\com\example\mytest8\MyFruit.java

public class MyFruit {
    private String group;   // 分组
    private String name;    // 名称
    private float price;    // 价格

    public MyFruit(String group, String name, float price) {
        this.group = group;
        this.name = name;
        this.price = price;
    }

    public String getGroup() {
        return group;
    }

    public String getName() {
        return name;
    }

    public float getPrice() {
        return price;
    }
}

自定义适配器Adapter

mytest8\src\main\java\com\example\mytest8\MyFruitAdapter.java :

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

// 继承自 RecyclerView.Adapter
public class MyFruitAdapter extends RecyclerView.Adapter<MyFruitAdapter.MyViewHolder> {
    private List<MyFruit> mFruitList;

    // 构造函数,list为需要展示的数据源
    public MyFruitAdapter(List<MyFruit> list) {
        this.mFruitList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.fruit_item, parent, false);

        // 创建自定义 viewHolder 实例
        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyFruit mFruit = mFruitList.get(position);
        holder.mFruitName.setText(mFruit.getName());
        holder.mFruitPrice.setText(mFruit.getPrice()+"元");
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }

    // 内部类 继承自RecyclerView.ViewHolder
    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView mFruitName;
        TextView mFruitPrice;

        // 构造函数 通过传入的view,可以获取到 RecyclerView子项 的最外成布局
        public MyViewHolder(View itemView) {
            super(itemView);
            mFruitName = itemView.findViewById(R.id.fruit_name_tv);
            mFruitPrice = itemView.findViewById(R.id.fruit_price_tv);
        }
    }

    /**
     * 是否分组group的第一个
     * @param position
     */
    public boolean isGroupFirst(int position) {
        // 如果 position 那说明就是第一个了
        if (position == 0) {
            return true;
        } else {
            // 当前 item 所在的分组名称
            String currentGroupName = getGroupName(position);
            // 前一个 item 所在的分组名称
            String preGroupName = mFruitList.get(position - 1).getGroup();
            // 如果当前item的分组 === 前一个item的分组,说明就不是第一个了
            if (currentGroupName.equals(preGroupName)) {
                return false;
            } else {
                return true;
            }
        }
    }

    /**
     * 获取group名称
     * @param position
     * @return
     */
    public String getGroupName(int position) {
        return mFruitList.get(position).getGroup();
    }
}

自定义ItemDecoration

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

public class MyItemDecoration extends RecyclerView.ItemDecoration {
    private static final String TAG = "AAAAAAAAAA";

    private int mHeadHeight;
    Paint textPaint;
    Rect textRect;
    Paint headPaint;

    public MyItemDecoration(Context context) {
        mHeadHeight = dp2px(context, 100);

        textPaint = new Paint();
        textPaint.setTextSize(50);
        textPaint.setColor(Color.WHITE);
        textRect = new Rect();

        headPaint = new Paint();
        headPaint.setColor(Color.RED);
    }

    /**
     * 获取Item的偏移量,结果保存在第一个参数outRect中
     * @param outRect 是一个矩形,可以设置上下左右四个值。对应view在四个方向上的偏移量。默认是全为0的Rect
     *                设置rect,会透漏出下面的背景色,看起来像是分割线,颜色取决于背景色
     * @param view RecyclerView 中的 item
     * @param parent RecyclerView本身
     * @param state 状态
     */
    @Override
    public void getItemOffsets(Rect outRect, View view,
                               RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        if (parent.getAdapter() instanceof MyFruitAdapter) {
            MyFruitAdapter adapter = (MyFruitAdapter) parent.getAdapter();

            int position = parent.getChildAdapterPosition(view);

            Log.d(TAG, "getItemOffsets position: " + position);

            // 判断该 view 是否分组的第一个
            if (adapter.isGroupFirst(position)) {
                outRect.set(0, mHeadHeight, 0, 0);
            } else {
                outRect.set(0, 2, 0, 0);
            }
        }
    }

    /**
     * 会在 绘制item之前调用,绘制的内容,会显示在 item 的底部,如果有重叠,会被 item 挡住
     * @param c 为 Canvas
     * @param parent RecyclerView本身
     * @param state 状态
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        if (parent.getAdapter() instanceof MyFruitAdapter) {
            MyFruitAdapter adapter = (MyFruitAdapter) parent.getAdapter();

            // 当前屏幕中可见的 item 数量
            int childCount = parent.getChildCount();

            int mLeft = parent.getPaddingLeft();
            int mRight = parent.getWidth() - parent.getPaddingRight();

            for (int i = 0; i < childCount; i++) {
                View view = parent.getChildAt(i);
                int position = parent.getChildAdapterPosition(view);

                Log.d(TAG, "onDraw position: " + position);

                String groupName = adapter.getGroupName(position);

                // itemView 的 top,减去 paddingTop,即上边界距离边缘的高度,如果大于头部区域的高度,才绘制分割线,否则不绘制
                if (adapter.isGroupFirst(position) && view.getTop() - parent.getPaddingTop() >= mHeadHeight) {
                    c.drawRect( mLeft, view.getTop() - mHeadHeight, mRight, view.getTop(), headPaint);

                    textPaint.getTextBounds(groupName, 0, groupName.length(), textRect);

                    c.drawText(groupName, mLeft, view.getTop() - mHeadHeight/2 + textRect.height()/2, textPaint);
                // itemView 的 top,减去 paddingTop,即上边界距离边缘的高度,如果大于头部区域的高度,才绘制分割线,否则不绘制
                } else if (view.getTop() - parent.getPaddingTop() >= mHeadHeight) {
                    c.drawRect( mLeft, view.getTop() - 2, mRight, view.getTop(), headPaint);
                }
            }
        }
    }

    /**
     * 会在 绘制item之后调用,绘制的内容,会显示在 item 的上部,如果有重叠,会把 item 挡住
     * @param c 为 Canvas
     * @param parent RecyclerView本身
     * @param state 状态
     */
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);

        if (parent.getAdapter() instanceof MyFruitAdapter) {
            MyFruitAdapter adapter = (MyFruitAdapter) parent.getAdapter();

            // 返回可见区域的第一个 item 的 position
            int position = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();

            // 获取对应 position 的 itemView
            View itemView = parent.findViewHolderForAdapterPosition(position).itemView;

            int mLeft = parent.getPaddingLeft();
            int mTop = parent.getPaddingTop();
            int mRight = parent.getWidth() - parent.getPaddingRight();

            // 当可见的第二个 itemView,是另外一个分组的头部,也就是把之前的头部给挤走
            if (adapter.isGroupFirst(position+1)) {
                // 当一个分组的最后一个 itemView,往上滑动的时候,如果它 小于 头部的高度,头部也要跟着滑动了,也就是挤走
                // 头部分组的高度,会变化
                int mTempHeight = Math.min(mHeadHeight, itemView.getBottom() - parent.getPaddingTop());

                c.drawRect(mLeft, mTop, mRight, mTop + mTempHeight, headPaint);

                String groupName = adapter.getGroupName(position);
                textPaint.getTextBounds(groupName, 0, groupName.length(), textRect);
                // 禁止文字在 padding 区域显示
                c.clipRect(mLeft, mTop, mRight, mTop + mTempHeight);
                // 绘制文字,有上移的效果
                c.drawText(groupName, mLeft, mTop + mTempHeight - mHeadHeight/2 + textRect.height()/2, textPaint);
            } else {
                c.drawRect(mLeft, mTop, mRight, mTop + mHeadHeight, headPaint);

                String groupName = adapter.getGroupName(position);

                textPaint.getTextBounds(groupName, 0, groupName.length(), textRect);
                c.drawText(groupName, mLeft, mTop + mHeadHeight/2 + textRect.height()/2, textPaint);
            }
        }
    }

    // dp 转成 px
    private int dp2px(Context context, float dpValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale * 0.5f);
    }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.Window;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        List<MyFruit> mFruitList = new ArrayList<>();
        for (int i = 1; i <= 20; i++) {
            mFruitList.add(new MyFruit("苹果", "苹果" + i, (float) (1 + 0.01 * i)));
        }
        for (int i = 1; i <= 20; i++) {
            mFruitList.add(new MyFruit("香蕉", "香蕉" + i, (float) (2 + 0.01 * i)));
        }
        for (int i = 1; i <= 20; i++) {
            mFruitList.add(new MyFruit("橘子", "橘子" + i, (float) (3 + 0.01 * i)));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 LinearLayoutManager 对象 ,即线性布局
        LinearLayoutManager mManager = new LinearLayoutManager(this);

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyFruitAdapter mAdapter = new MyFruitAdapter(mFruitList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);

        // 设置分割线
        mRecyclerView.addItemDecoration(new MyItemDecoration(this));
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

自定义LayoutManager

添加依赖库

mytest9\build.gradle :

dependencies {
    implementation 'androidx.cardview:cardview:1.0.0'

    implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}

修改xml布局

mytest9\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

mytest9\src\main\res\layout\pic_item.xml :
这个是 RecyclerView 的子项布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="260dp"
    android:layout_height="360dp"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:foreground="?android:attr/selectableItemBackground"
    app:cardBackgroundColor="@color/teal_200">

    <ImageView
        android:id="@+id/pic_id_iv"
        android:layout_width="200dp"
        android:layout_height="300dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:src="@mipmap/ic_launcher"/>

    <TextView
        android:id="@+id/pic_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="310dp"
        android:paddingTop="10dp"
        android:text="name"/>

    <TextView
        android:id="@+id/pic_position_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right|bottom"
        android:layout_margin="3dp"
        android:textColor="@android:color/white"
        android:text="1/10"
        />

</androidx.cardview.widget.CardView>

创建MyPicBean实体类

mytest9\src\main\java\com\example\mytest9\MyPicBean.java

public class MyPicBean {
    // 图片资源
    private int pic_id;
    // 图片名称描述
    private String pic_name;
    // 图片位置索引
    private int pic_position;

    public MyPicBean(int pic_id, String pic_name, int pic_position) {
        this.pic_id = pic_id;
        this.pic_name = pic_name;
        this.pic_position = pic_position;
    }

    public int getPic_id() {
        return pic_id;
    }

    public void setPic_id(int pic_id) {
        this.pic_id = pic_id;
    }

    public String getPic_name() {
        return pic_name;
    }

    public void setPic_name(String pic_name) {
        this.pic_name = pic_name;
    }

    public int getPic_position() {
        return pic_position;
    }

    public void setPic_position(int pic_position) {
        this.pic_position = pic_position;
    }
}

自定义适配器Adapter

mytest9\src\main\java\com\example\mytest9\MyPicAdapter.java :

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.List;

public class MyPicAdapter extends RecyclerView.Adapter<MyPicAdapter.MyViewHolder> {

    private List<MyPicBean> mPicBeanList;
    Context context;

    // 构造函数,list为需要展示的数据源
    public MyPicAdapter(Context context, List<MyPicBean> list) {
        this.context = context;
        this.mPicBeanList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.pic_item, parent, false);

        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyPicBean mPicBean = mPicBeanList.get(position);

        int mTotal = mPicBeanList == null ? 0 : mPicBeanList.size();

        Glide.with(context).load(mPicBean.getPic_id()).into(holder.pic_id_iv);
        holder.pic_name_tv.setText(mPicBean.getPic_name());
        holder.pic_position_tv.setText(mPicBean.getPic_position()+"/" + mTotal);
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mPicBeanList == null ? 0 : mPicBeanList.size();
    }

    static class MyViewHolder extends RecyclerView.ViewHolder{
        ImageView pic_id_iv;
        TextView pic_name_tv;
        TextView pic_position_tv;

        public MyViewHolder(View itemView) {
            super(itemView);
            pic_id_iv = itemView.findViewById(R.id.pic_id_iv);
            pic_name_tv = itemView.findViewById(R.id.pic_name_tv);
            pic_position_tv = itemView.findViewById(R.id.pic_position_tv);
        }
    }
}

自定义LayoutManager

mytest9\src\main\java\com\example\mytest9\MyLayoutManager.java :

import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;

public class MyLayoutManager extends RecyclerView.LayoutManager {
    private static final String TAG = "AAAAAAA";

    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {

        detachAndScrapAttachedViews(recycler);

        int itemCount = getItemCount();
        Log.d(TAG, "itemCount: " +itemCount);

        for (int i=0; i < itemCount; i++) {
            View view = recycler.getViewForPosition(i);

            addView(view);

            measureChildWithMargins(view, 0, 0);

            int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
            int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);

            layoutDecoratedWithMargins(view,
                    widthSpace/2,
                    heightSpace/2,
                    widthSpace/2 + getDecoratedMeasuredWidth(view),
                    heightSpace/2 + getDecoratedMeasuredHeight(view)
                    );

            // X、Y轴进行平移
            view.setTranslationY(10 * (itemCount -i));
            view.setTranslationX(10 * (itemCount -i));
        }
    }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.Window;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        List<MyPicBean> mPicList = new ArrayList<>();
        for (int i = 1; i <= 9; i++) {
            mPicList.add(new MyPicBean(R.drawable.ic_launcher_background, "图" + i, i));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 LinearLayoutManager 对象
//        LinearLayoutManager mManager = new LinearLayoutManager(this);
        MyLayoutManager mManager = new MyLayoutManager();

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyPicAdapter mAdapter = new MyPicAdapter(this, mPicList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

自定义ItemTouchHelper.SimpleCallback

添加依赖库

mytest10\build.gradle :

dependencies {
    implementation 'androidx.cardview:cardview:1.0.0'

    implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}

修改xml布局

mytest10\src\main\res\layout\activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/test_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

mytest10\src\main\res\layout\pic_item.xml :
这个是 RecyclerView 的子项布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="260dp"
    android:layout_height="360dp"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:foreground="?android:attr/selectableItemBackground"
    app:cardBackgroundColor="@color/teal_200"
    android:clickable="true"
    >

    <ImageView
        android:id="@+id/pic_id_iv"
        android:layout_width="200dp"
        android:layout_height="300dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:src="@mipmap/ic_launcher"/>

    <TextView
        android:id="@+id/pic_name_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="310dp"
        android:paddingTop="10dp"
        android:text="name"/>

    <TextView
        android:id="@+id/pic_position_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right|bottom"
        android:paddingRight="20dp"
        android:paddingBottom="20dp"
        android:layout_margin="3dp"
        android:textColor="@android:color/white"
        android:text="1/10"
        />

</androidx.cardview.widget.CardView>

创建MyPicBean实体类

mytest10\src\main\java\com\example\mytest10\MyPicBean.java

public class MyPicBean {
    // 图片资源
    private int pic_id;
    // 图片名称描述
    private String pic_name;
    // 图片位置索引
    private int pic_position;

    public MyPicBean(int pic_id, String pic_name, int pic_position) {
        this.pic_id = pic_id;
        this.pic_name = pic_name;
        this.pic_position = pic_position;
    }

    public int getPic_id() {
        return pic_id;
    }

    public void setPic_id(int pic_id) {
        this.pic_id = pic_id;
    }

    public String getPic_name() {
        return pic_name;
    }

    public void setPic_name(String pic_name) {
        this.pic_name = pic_name;
    }

    public int getPic_position() {
        return pic_position;
    }

    public void setPic_position(int pic_position) {
        this.pic_position = pic_position;
    }
}

自定义适配器Adapter

mytest10\src\main\java\com\example\mytest10\MyPicAdapter.java :

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.List;

public class MyPicAdapter extends RecyclerView.Adapter<MyPicAdapter.MyViewHolder> {
    private List<MyPicBean> mPicBeanList;
    Context context;

    // 构造函数,list为需要展示的数据源
    public MyPicAdapter(Context context, List<MyPicBean> list) {
        this.context = context;
        this.mPicBeanList = list;
    }

    // 用于创建 ViewHolder 实例
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.pic_item, parent, false);

        MyViewHolder holder = new MyViewHolder(view);

        return holder;
    }

    // 对 RecyclerView子项 的数据进行赋值,会在每个子项被滚到到屏幕内的时候执行
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        MyPicBean mPicBean = mPicBeanList.get(position);

        int mTotal = mPicBeanList == null ? 0 : mPicBeanList.size();

        Glide.with(context).load(mPicBean.getPic_id()).into(holder.pic_id_iv);
        holder.pic_name_tv.setText(mPicBean.getPic_name());
        holder.pic_position_tv.setText(mPicBean.getPic_position()+"/" + mTotal);
    }

    // 告诉 RecyclerView 一共有多少个子项,返回数据长度即可
    @Override
    public int getItemCount() {
        return mPicBeanList == null ? 0 : mPicBeanList.size();
    }

    static class MyViewHolder extends RecyclerView.ViewHolder{
        ImageView pic_id_iv;
        TextView pic_name_tv;
        TextView pic_position_tv;

        public MyViewHolder(View itemView) {
            super(itemView);
            pic_id_iv = itemView.findViewById(R.id.pic_id_iv);
            pic_name_tv = itemView.findViewById(R.id.pic_name_tv);
            pic_position_tv = itemView.findViewById(R.id.pic_position_tv);
        }
    }
}

自定义LayoutManager

mytest10\src\main\java\com\example\mytest10\MyLayoutManager.java :

import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;

public class MyLayoutManager extends RecyclerView.LayoutManager {
    private static final String TAG = "AAAAAAA";

    // 显示的item个数
    public static final int showCount = 4;

    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {

        detachAndScrapAttachedViews(recycler);

        int itemCount = getItemCount();
        Log.d(TAG, "itemCount: " +itemCount); // 9

        // 当前底部item 的 position
        int bottomPosition;

        // 如果item个数 小于 需要展示的个数,那就全部展示
        if (itemCount < showCount) {
            bottomPosition = 0;
        } else {
            bottomPosition = itemCount - showCount; // 5
        }

        for (int i=bottomPosition; i < itemCount; i++) {
            View view = recycler.getViewForPosition(i);

            addView(view);

            measureChildWithMargins(view, 0, 0);

            int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
            int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);

            layoutDecoratedWithMargins(view,
                    widthSpace/2,
                    heightSpace/2,
                    widthSpace/2 + getDecoratedMeasuredWidth(view),
                    heightSpace/2 + getDecoratedMeasuredHeight(view)
                    );

            // 从下往上
            int level = itemCount - i - 1; // 3

            if (level > 0) {
                // bottom 和 上面的一个偏移一样,即position 5 和 position 6,一样的
                if (level < showCount - 1) {
                    // X、Y轴进行平移
                    view.setTranslationY(40 * level);
                    view.setTranslationX(40 * level);
                } else {
                    // X、Y轴进行平移   最下面的跟它上面的一样
                    view.setTranslationY(40 * (level-1));
                    view.setTranslationX(40 * (level-1));
                }
            }
        }
    }
}

自定义ItemTouchHelper.SimpleCallback

mytest10\src\main\java\com\example\mytest10\MyItemTouchHelperCallback.java

import android.graphics.Canvas;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class MyItemTouchHelperCallback extends ItemTouchHelper.SimpleCallback {
    private static final String TAG = "AAAAAA";

    private RecyclerView recyclerView;
    private MyPicAdapter adapter;
    private List<MyPicBean> mDatas;

//    public MyItemTouchHelperCallback(int dragDirs, int swipeDirs) {
//        super(dragDirs, swipeDirs);
//    }

    public MyItemTouchHelperCallback(RecyclerView recyclerView, MyPicAdapter adapter, List<MyPicBean> mDatas) {
        // 设置方向,拖拽、滑动,15为ox1111,即上下左右
        super(0, 15);

        this.recyclerView = recyclerView;
        this.adapter = adapter;
        this.mDatas = mDatas;
    }

    // drag用的
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView,
                          @NonNull RecyclerView.ViewHolder viewHolder,
                          @NonNull RecyclerView.ViewHolder target) {
        return false;
    }

    // 滑动结束后的操作,滑动上面的item,再添加到下面
    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        MyPicBean picBean = mDatas.remove(viewHolder.getLayoutPosition());
        mDatas.add(0, picBean);
        adapter.notifyDataSetChanged();
        Log.d(TAG, "notifyDataSetChanged: ");
    }

    // 回弹时间
    @Override
    public long getAnimationDuration(@NonNull RecyclerView recyclerView,
                                     int animationType, float animateDx, float animateDy) {
//        return super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy);
        return 1000;
    }

    // 回弹距离
    @Override
    public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
//        return super.getSwipeThreshold(viewHolder);
        return 0.2f;
    }

    // 绘制
    @Override
    public void onChildDraw(@NonNull Canvas c,
                            @NonNull RecyclerView recyclerView,
                            @NonNull RecyclerView.ViewHolder viewHolder,
                            float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

        double maxDistance = recyclerView.getWidth() * 0.5f;
        // 根据x和y,求三角行的另外一个边长,即移动的直线距离
        double distance = Math.sqrt(dX * dX + dY * dY);
        // 放大系数
        double fraction = distance / maxDistance;
        if (fraction > 1) {
            fraction = 1;
        }

        int itemCount = recyclerView.getChildCount(); // 4

        for (int i = 0; i < itemCount; i++) {
            View view = recyclerView.getChildAt(i);

            int level = itemCount - i - 1;
            if (level > 0) { // 最下面一张不偏移
                if (level < MyLayoutManager.showCount -1) { // 最上面一张不管它
                    view.setTranslationY((float) (40 * (level - fraction)));
                    view.setTranslationX((float) (40 * (level - fraction)));
                }
            }
        }
    }
}

MainActivity.java

import android.os.Bundle;
import android.view.Window;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        List<MyPicBean> mPicList = new ArrayList<>();
        for (int i = 1; i <= 9; i++) {
            mPicList.add(new MyPicBean(R.drawable.ic_launcher_background, "图" + i, i));
        }

        // 获取到 RecyclerView 实例
        RecyclerView mRecyclerView = findViewById(R.id.test_rv);

        // 创建 LinearLayoutManager 对象
//        LinearLayoutManager mManager = new LinearLayoutManager(this);
        MyLayoutManager mManager = new MyLayoutManager();

        // LayoutManager 用于指定 RecyclerView 的布局方式
        // 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
        mRecyclerView.setLayoutManager(mManager);

        // 创建自定义的 Adapter 实例
        MyPicAdapter mAdapter = new MyPicAdapter(this, mPicList);

        // 设置适配器
        mRecyclerView.setAdapter(mAdapter);

        // 创建 ItemTouchHelper
        ItemTouchHelper.Callback callback = new MyItemTouchHelperCallback(
                mRecyclerView, mAdapter, mPicList);
        ItemTouchHelper helper = new ItemTouchHelper(callback);
        helper.attachToRecyclerView(mRecyclerView);
    }
}

效果

在这里插入图片描述

代码示例

地址:GitHub

ItemDecoration分析

ItemDecoration 可以用作设置分割线等效果;
当自定义 ItemDecoration 时,需要继承自 RecyclerView.ItemDecoration,根据需要重写方法 getItemOffsets、onDraw、onDrawOver。

getItemOffsets 方法,获取 Item 的偏移量,结果保存在第一个参数outRect中;
它仅仅获取屏幕内的 item;

/**
 * 获取Item的偏移量,结果保存在第一个参数outRect中
 * @param outRect 是一个矩形,可以设置上下左右四个值。对应view在四个方向上的偏移量。默认是全为0的Rect
 *                设置rect,会透漏出下面的背景色,看起来像是分割线,颜色取决于背景色
 * @param view RecyclerView 中的 item
 * @param parent RecyclerView本身
 * @param state 状态
 */
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)

onDraw 方法,进行绘制,是在 item 之前进行绘制,也就是会被 item 给挡住。

/**
 * 会在 绘制item之前调用,绘制的内容,会显示在 item 的底部,如果有重叠,会被 item 挡住
 * @param c 为 Canvas
 * @param parent RecyclerView本身
 * @param state 状态
 */
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)

onDrawOver 方法,也是进行绘制,是在 item 之后进行绘制,也就是会把 item 给挡住。

public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state)

RecyclerView的方法

RecyclerView.setLayoutManager

设置 RecyclerView 使用的 LayoutManager

// 创建 LinearLayoutManager 对象
LinearLayoutManager mManager = new LinearLayoutManager(this);
mManager.setOrientation(LinearLayoutManager.HORIZONTAL); // 横向布局

// LayoutManager 用于指定 RecyclerView 的布局方式
// 将 LinearLayoutManager 设置到 RecyclerView 中,即设置线性布局
mRecyclerView.setLayoutManager(mManager);

RecyclerView.getLayoutManager()

获取 RecyclerView 之前设置的 LayoutManager,比如之前设置的 LinearLayoutManager

LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();

RecyclerView.getAdapter()

获取设置的适配器;

if (recyclerView.getAdapter() instanceof MyFruitAdapter) {
	for (int i = 0; i < recyclerView.getChildCount(); i++) {
		View view = recyclerView.getChildAt(i);
		
		int index = recyclerView.getChildAdapterPosition(view);
	}
}

RecyclerView.getChildCount()

获取子 item的个数,可见的 item 数量,并不一定是所有的 item 。

RecyclerView.getChildAt()

获取第 index 个子view

if (recyclerView.getAdapter() instanceof MyFruitAdapter) {
	for (int i = 0; i < recyclerView.getChildCount(); i++) {
		View view = recyclerView.getChildAt(i);
		
		int index = recyclerView.getChildAdapterPosition(view);
	}
}

RecyclerView.getChildAdapterPosition()

获取 item 在 adapter 中的位置

if (recyclerView.getAdapter() instanceof MyFruitAdapter) {
	for (int i = 0; i < recyclerView.getChildCount(); i++) {
		View view = recyclerView.getChildAt(i);
		
		int index = recyclerView.getChildAdapterPosition(view);
	}
}

RecyclerView.findViewHolderForAdapterPosition

获取 item 对应 position 位置,所属于的 ViewHolder

// 返回可见区域的第一个 item 的 position
int position = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();

// 获取 item 对应 position 位置,所属于的 ViewHolder
RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position);

// 获取 itemView
View itemView = viewHolder.itemView;

LinearLayoutManager 的方法

LinearLayoutManager.findFirstVisibleItemPosition

返回可见区域的第一个 item 的 position

int position = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();

参考文档

RecyclerView 之 ItemDecoration 讲解及高级特性实践

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 21:02:21  更:2022-03-21 21:04:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 18:47:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码