Android RecyclerVIew 列表实现 编辑、单选、全选、删除、动画效果(附源码)

简介: Android RecyclerVIew 列表实现 编辑、单选、全选、删除、动画效果(附源码)

前言


眼下都2020年了,你不会还在使用ListView吧?


正文


因为最近写的一个项目里面有关于列表的编辑相关的功能,其实也是类似与腾讯视频的观看历史的列表操作,你可以先尝试一下,写这个主要是业务逻辑要清晰,否则你很容易就会陷进去,走死胡同的。


效果图如下:


image.gif


迫不及待想要自己运行在AS中的小伙伴可以直接下载或者克隆,打开即可,想了解具体开发流程和详细的业务逻辑的小伙伴,可以继续往下看,本章很长,请在流量和时间充足的前提下观看,如有问题,评论即可,我会尽快解决,多说无益,开搞~


开发


① 新建项目

20200526114645381.png


这一步没有什么好说的,但是你也要注意自己的Android Studio和Gradle的版本,我的是3.5.2。点击项目创建


② 配置build.gradle


第一步


在工程级别的build.gradle文件中的allprojects{} 闭包中添加maven库

  maven { url "https://jitpack.io" }

20200526140307560.png


第二步


在项目级别的build.gradle文件中的android{}闭包中,配置JDK的版本

  compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }


20200526140517603.png


然后在dependencies{}闭包中添加第三方的依赖库


  //绑定视图
    implementation 'com.jakewharton:butterknife:10.2.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
    //RecyclerView
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    //下拉刷新
    implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-alpha-14'
    //RecyclerView适配器
    implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.22'
    //热门强大的图片加载器
    implementation 'com.github.bumptech.glide:glide:4.9.0'


20200526153556704.png


然后右上角 Sync Now,点击同步你的项目配置文件,同步会下载第三方的库文件,等待完成进行下一步。


③ 修改布局


在此之前,为了增加点击效果,在resdrawable 下创建rounded_corners.xml文件


代码如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#18ffc400"/>
        </shape>
    </item>
    <item android:state_focused="true" android:state_enabled="true">
        <shape android:shape="rectangle">
            <solid android:color="#0f000000"/>
        </shape>
    </item>
</selector>


然后在res下新建一个drawable-v21的文件夹,在文件夹中创建bg_white.xml文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#20000000"
    android:drawable="@drawable/rounded_corners"/>

20200526144842177.png


在drawable文件下再创建一个shape_blue_bg_5.xml

代码如下:


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp"/>
    <solid android:color="#101B53"/>
</shape>


然后是修改values文件夹下的colors.xml文件,修改原来的并增加几个颜色,


20200526144926355.png

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#1F2B5D</color>
    <color name="colorPrimaryDark">#1F2B5D</color>
    <color name="colorAccent">#D81B60</color>
    <color name="default_color">#48558C</color>
    <color name="selected_color">#009FE8</color>
    <color name="app_color">#1F2B5D</color>
    <color name="app_bg_color">#020C49</color>
    <color name="white">#FFFFFF</color>
    <color name="gray">#8A96A3</color>
    <color name="yellow">#F39700</color>
</resources>


再修改一下styles.xml文件

20200526145033427.png


布局中会用到下面的图片


icon_normal.png


20200526145139921.png


icon_choose_selected.png

20200526150731686.png

icon_choose_default.png

20200526150731684.png


修改activity_main.xml文件,修改后如下图所示


20200526145249862.png


代码如下,复制粘贴即可:


<?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"
    android:background="@color/app_bg_color"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">
    <!--头部-->
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="RecyclerView Demo"
            android:textColor="@color/white"
            android:textSize="18sp" />
        <TextView
            android:id="@+id/tv_edit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginRight="8dp"
            android:padding="8dp"
            android:text="编辑"
            android:textColor="@color/white"
            android:textSize="16sp" />
    </androidx.appcompat.widget.Toolbar>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/toolbar"
        android:layout_above="@+id/lay_bottom">
        <!--无数据时的布局-->
        <LinearLayout
            android:id="@+id/rv_normal_show"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical">
            <ImageView
                android:layout_width="264dp"
                android:layout_height="140dp"
                android:background="@mipmap/icon_normal" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/dp_10"
                android:text="空空如也"
                android:textColor="@color/gray"
                android:textSize="18sp" />
        </LinearLayout>
        <!--没有数据时,下拉重新添加数据-->
        <com.scwang.smartrefresh.layout.SmartRefreshLayout
            android:id="@+id/refresh"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!--展示数据的列表-->
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rv"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:padding="12dp"
                android:visibility="gone" />
        </com.scwang.smartrefresh.layout.SmartRefreshLayout>
    </RelativeLayout>
    <!--底部-->
    <LinearLayout
        android:id="@+id/lay_bottom"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:background="@color/colorPrimary"
        android:orientation="horizontal"
        android:visibility="gone">
        <TextView
            android:id="@+id/tv_check_all"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:foreground="@drawable/bg_white"
            android:gravity="center"
            android:text="全选"
            android:textColor="@color/white"
            android:textSize="@dimen/sp_14" />
        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:layout_marginTop="12dp"
            android:layout_marginBottom="12dp"
            android:background="@color/app_bg_color" />
        <TextView
            android:id="@+id/tv_delete"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:foreground="@drawable/bg_white"
            android:gravity="center"
            android:text="删除"
            android:textColor="@color/yellow"
            android:textSize="14sp" />
    </LinearLayout>
</RelativeLayout>


然后是创建一个数据实体DataResponse.java


20200526165108206.png


代码如下,复制粘贴即可:


package com.llw.recyclerviewusedemo;
import java.util.List;
/**
 * 数据实体
 */
public class DataResponse {
    /**
     * data : [{"_id":"5e959250808d6d2fe6b56eda","author":"鸢媛","category":"Girl","createdAt":"2020-05-25 08:00:00","desc":"与其安慰自己平凡可贵,\n不如拼尽全力活得漂亮。 \u200b \u200b\u200b\u200b\u200b","images":["http://gank.io/images/f4f6d68bf30147e1bdd4ddbc6ad7c2a2"],"likeCounts":0,"publishedAt":"2020-05-25 08:00:00","stars":1,"title":"第96期","type":"Girl","url":"http://gank.io/images/f4f6d68bf30147e1bdd4ddbc6ad7c2a2","views":131},{"_id":"5e95923f808d6d2fe6b56ed8","author":"鸢媛","category":"Girl","createdAt":"2020-05-24 08:00:00","desc":"这世界总有人在笨拙地爱着你,想把全部的温柔都给你。 \u200b\u200b\u200b\u200b","images":["http://gank.io/images/dc75cbde1d98448183e2f9514b4d1320"],"likeCounts":0,"publishedAt":"2020-05-24 08:00:00","stars":1,"title":"第95期","type":"Girl","url":"http://gank.io/images/dc75cbde1d98448183e2f9514b4d1320","views":133},{"_id":"5e95922e808d6d2fe6b56ed6","author":"鸢媛","category":"Girl","createdAt":"2020-05-23 08:00:00","desc":"陪伴本来就是这世界上最了不起的安慰\u200b。","images":["http://gank.io/images/6b2efa591564475fb8bc32291fb0007c"],"likeCounts":0,"publishedAt":"2020-05-23 08:00:00","stars":1,"title":"第94期","type":"Girl","url":"http://gank.io/images/6b2efa591564475fb8bc32291fb0007c","views":135},{"_id":"5e959200ee6ba981da2af34d","author":"鸢媛","category":"Girl","createdAt":"2020-05-22 08:00:00","desc":"长不过执念,短不过善变。","images":["http://gank.io/images/d6bba8cf5b8c40f9ad229844475e9149"],"likeCounts":0,"publishedAt":"2020-05-22 08:00:00","stars":1,"title":"第93期","type":"Girl","url":"http://gank.io/images/d6bba8cf5b8c40f9ad229844475e9149","views":192},{"_id":"5e9591dcee6ba981da2af34b","author":"鸢媛","category":"Girl","createdAt":"2020-05-21 08:00:00","desc":"无论多么艰难的现在,终会翻篇。\n朝未来大步向前吧,别丧,别止步。","images":["http://gank.io/images/9fa43020cf724c69842eec3e13f6d21c"],"likeCounts":1,"publishedAt":"2020-05-21 08:00:00","stars":1,"title":"第92期","type":"Girl","url":"http://gank.io/images/9fa43020cf724c69842eec3e13f6d21c","views":176},{"_id":"5e9591c60bd5529b54e712af","author":"鸢媛","category":"Girl","createdAt":"2020-05-20 08:00:00","desc":"希望下一次,能喜欢上一个也喜欢自己的人 \u200b\u200b\u200b\u200b。","images":["http://gank.io/images/d237f507bf1946d2b0976e581f8aab9b"],"likeCounts":0,"publishedAt":"2020-05-20 08:00:00","stars":1,"title":"第91期","type":"Girl","url":"http://gank.io/images/d237f507bf1946d2b0976e581f8aab9b","views":180},{"_id":"5e9591b6808d6d2fe6b56ed5","author":"鸢媛","category":"Girl","createdAt":"2020-05-19 08:00:00","desc":"这个世界上,\n有些人有多冷漠,\n有些人就有多温暖,\n希望你总会遇到那些温暖对你的人。","images":["http://gank.io/images/25d3e3db2c1248bb917c09dc4f50a46f"],"likeCounts":0,"publishedAt":"2020-05-19 08:00:00","stars":1,"title":"第90期","type":"Girl","url":"http://gank.io/images/25d3e3db2c1248bb917c09dc4f50a46f","views":255},{"_id":"5e9591a2ee6ba981da2af34a","author":"鸢媛","category":"Girl","createdAt":"2020-05-18 08:00:00","desc":"以前对你的喜欢,\n是见你,念你,陪伴你。\n现在对你的喜欢,\n是不问,不看,不叨扰。","images":["http://gank.io/images/19c99c447e0a40a6b3ff89951957cfb1"],"likeCounts":0,"publishedAt":"2020-05-18 08:00:00","stars":1,"title":"第89期","type":"Girl","url":"http://gank.io/images/19c99c447e0a40a6b3ff89951957cfb1","views":240},{"_id":"5e959197808d6d2fe6b56ed4","author":"鸢媛","category":"Girl","createdAt":"2020-05-17 08:00:00","desc":"只要结局是喜剧,过程你要我怎么哭都行,幸福可以来的慢一些,\n只要它是真的,如果最后能在一起,晚点我真的无所谓的。","images":["http://gank.io/images/f0c192e3e335400db8a709a07a891b2e"],"likeCounts":0,"publishedAt":"2020-05-17 08:00:00","stars":1,"title":"第88期","type":"Girl","url":"http://gank.io/images/f0c192e3e335400db8a709a07a891b2e","views":275},{"_id":"5e95915f808d6d2fe6b56ed3","author":"鸢媛","category":"Girl","createdAt":"2020-05-16 08:00:00","desc":"若不是情深似海,思念又怎会泛滥成灾。","images":["http://gank.io/images/bdb35e4b3c0045c799cc7a494a3db3e0"],"likeCounts":0,"publishedAt":"2020-05-16 08:00:00","stars":1,"title":"第87期","type":"Girl","url":"http://gank.io/images/bdb35e4b3c0045c799cc7a494a3db3e0","views":437}]
     * page : 1
     * page_count : 10
     * status : 100
     * total_counts : 96
     */
    private int page;
    private int page_count;
    private int status;
    private int total_counts;
    private List<DataBean> data;
    public int getPage() {
        return page;
    }
    public void setPage(int page) {
        this.page = page;
    }
    public int getPage_count() {
        return page_count;
    }
    public void setPage_count(int page_count) {
        this.page_count = page_count;
    }
    public int getStatus() {
        return status;
    }
    public void setStatus(int status) {
        this.status = status;
    }
    public int getTotal_counts() {
        return total_counts;
    }
    public void setTotal_counts(int total_counts) {
        this.total_counts = total_counts;
    }
    public List<DataBean> getData() {
        return data;
    }
    public void setData(List<DataBean> data) {
        this.data = data;
    }
    public static class DataBean {
        /**
         * _id : 5e959250808d6d2fe6b56eda
         * author : 鸢媛
         * category : Girl
         * createdAt : 2020-05-25 08:00:00
         * desc : 与其安慰自己平凡可贵,
         不如拼尽全力活得漂亮。 ​ ​​​​
         * images : ["http://gank.io/images/f4f6d68bf30147e1bdd4ddbc6ad7c2a2"]
         * likeCounts : 0
         * publishedAt : 2020-05-25 08:00:00
         * stars : 1
         * title : 第96期
         * type : Girl
         * url : http://gank.io/images/f4f6d68bf30147e1bdd4ddbc6ad7c2a2
         * views : 131
         */
        private String _id;
        private String author;
        private String category;
        private String createdAt;
        private String desc;
        private int likeCounts;
        private String publishedAt;
        private int stars;
        private String title;
        private String type;
        private String url;
        private int views;
        private List<String> images;
        private boolean select = false;
        public String get_id() {
            return _id;
        }
        public void set_id(String _id) {
            this._id = _id;
        }
        public String getAuthor() {
            return author;
        }
        public void setAuthor(String author) {
            this.author = author;
        }
        public String getCategory() {
            return category;
        }
        public void setCategory(String category) {
            this.category = category;
        }
        public String getCreatedAt() {
            return createdAt;
        }
        public void setCreatedAt(String createdAt) {
            this.createdAt = createdAt;
        }
        public String getDesc() {
            return desc;
        }
        public void setDesc(String desc) {
            this.desc = desc;
        }
        public int getLikeCounts() {
            return likeCounts;
        }
        public void setLikeCounts(int likeCounts) {
            this.likeCounts = likeCounts;
        }
        public String getPublishedAt() {
            return publishedAt;
        }
        public void setPublishedAt(String publishedAt) {
            this.publishedAt = publishedAt;
        }
        public int getStars() {
            return stars;
        }
        public void setStars(int stars) {
            this.stars = stars;
        }
        public String getTitle() {
            return title;
        }
        public void setTitle(String title) {
            this.title = title;
        }
        public String getType() {
            return type;
        }
        public void setType(String type) {
            this.type = type;
        }
        public String getUrl() {
            return url;
        }
        public void setUrl(String url) {
            this.url = url;
        }
        public int getViews() {
            return views;
        }
        public void setViews(int views) {
            this.views = views;
        }
        public List<String> getImages() {
            return images;
        }
        public void setImages(List<String> images) {
            this.images = images;
        }
        public boolean isSelect() {
            return select;
        }
        public void setSelect(boolean select) {
            this.select = select;
        }
    }
}


再创建一个JsonData.java,存放用于转换的JSON数据


20200526145852481.png


代码如下,复制粘贴即可:

package com.llw.recyclerviewusedemo;
public class JsonData {
    public static String JSON = "{\"data\":[{\"_id\":\"5e959250808d6d2fe6b56eda\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-25 08:00:00\",\"desc\":\"\\u4e0e\\u5176\\u5b89\\u6170\\u81ea\\u5df1\\u5e73\\u51e1\\u53ef\\u8d35\\uff0c\\n\\u4e0d\\u5982\\u62fc\\u5c3d\\u5168\\u529b\\u6d3b\\u5f97\\u6f02\\u4eae\\u3002 \\u200b \\u200b\\u200b\\u200b\\u200b\",\"images\":[\"http://gank.io/images/f4f6d68bf30147e1bdd4ddbc6ad7c2a2\"],\"likeCounts\":0,\"publishedAt\":\"2020-05-25 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c96\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/f4f6d68bf30147e1bdd4ddbc6ad7c2a2\",\"views\":131},{\"_id\":\"5e95923f808d6d2fe6b56ed8\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-24 08:00:00\",\"desc\":\"\\u8fd9\\u4e16\\u754c\\u603b\\u6709\\u4eba\\u5728\\u7b28\\u62d9\\u5730\\u7231\\u7740\\u4f60\\uff0c\\u60f3\\u628a\\u5168\\u90e8\\u7684\\u6e29\\u67d4\\u90fd\\u7ed9\\u4f60\\u3002 \\u200b\\u200b\\u200b\\u200b\",\"images\":[\"http://gank.io/images/dc75cbde1d98448183e2f9514b4d1320\"],\"likeCounts\":0,\"publishedAt\":\"2020-05-24 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c95\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/dc75cbde1d98448183e2f9514b4d1320\",\"views\":133},{\"_id\":\"5e95922e808d6d2fe6b56ed6\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-23 08:00:00\",\"desc\":\"\\u966a\\u4f34\\u672c\\u6765\\u5c31\\u662f\\u8fd9\\u4e16\\u754c\\u4e0a\\u6700\\u4e86\\u4e0d\\u8d77\\u7684\\u5b89\\u6170\\u200b\\u3002\",\"images\":[\"http://gank.io/images/6b2efa591564475fb8bc32291fb0007c\"],\"likeCounts\":0,\"publishedAt\":\"2020-05-23 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c94\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/6b2efa591564475fb8bc32291fb0007c\",\"views\":135},{\"_id\":\"5e959200ee6ba981da2af34d\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-22 08:00:00\",\"desc\":\"\\u957f\\u4e0d\\u8fc7\\u6267\\u5ff5\\uff0c\\u77ed\\u4e0d\\u8fc7\\u5584\\u53d8\\u3002\",\"images\":[\"http://gank.io/images/d6bba8cf5b8c40f9ad229844475e9149\"],\"likeCounts\":0,\"publishedAt\":\"2020-05-22 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c93\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/d6bba8cf5b8c40f9ad229844475e9149\",\"views\":192},{\"_id\":\"5e9591dcee6ba981da2af34b\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-21 08:00:00\",\"desc\":\"\\u65e0\\u8bba\\u591a\\u4e48\\u8270\\u96be\\u7684\\u73b0\\u5728\\uff0c\\u7ec8\\u4f1a\\u7ffb\\u7bc7\\u3002\\n\\u671d\\u672a\\u6765\\u5927\\u6b65\\u5411\\u524d\\u5427\\uff0c\\u522b\\u4e27\\uff0c\\u522b\\u6b62\\u6b65\\u3002\",\"images\":[\"http://gank.io/images/9fa43020cf724c69842eec3e13f6d21c\"],\"likeCounts\":1,\"publishedAt\":\"2020-05-21 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c92\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/9fa43020cf724c69842eec3e13f6d21c\",\"views\":176},{\"_id\":\"5e9591c60bd5529b54e712af\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-20 08:00:00\",\"desc\":\"\\u5e0c\\u671b\\u4e0b\\u4e00\\u6b21\\uff0c\\u80fd\\u559c\\u6b22\\u4e0a\\u4e00\\u4e2a\\u4e5f\\u559c\\u6b22\\u81ea\\u5df1\\u7684\\u4eba \\u200b\\u200b\\u200b\\u200b\\u3002\",\"images\":[\"http://gank.io/images/d237f507bf1946d2b0976e581f8aab9b\"],\"likeCounts\":0,\"publishedAt\":\"2020-05-20 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c91\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/d237f507bf1946d2b0976e581f8aab9b\",\"views\":180},{\"_id\":\"5e9591b6808d6d2fe6b56ed5\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-19 08:00:00\",\"desc\":\"\\u8fd9\\u4e2a\\u4e16\\u754c\\u4e0a\\uff0c\\n\\u6709\\u4e9b\\u4eba\\u6709\\u591a\\u51b7\\u6f20\\uff0c\\n\\u6709\\u4e9b\\u4eba\\u5c31\\u6709\\u591a\\u6e29\\u6696\\uff0c\\n\\u5e0c\\u671b\\u4f60\\u603b\\u4f1a\\u9047\\u5230\\u90a3\\u4e9b\\u6e29\\u6696\\u5bf9\\u4f60\\u7684\\u4eba\\u3002\",\"images\":[\"http://gank.io/images/25d3e3db2c1248bb917c09dc4f50a46f\"],\"likeCounts\":0,\"publishedAt\":\"2020-05-19 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c90\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/25d3e3db2c1248bb917c09dc4f50a46f\",\"views\":255},{\"_id\":\"5e9591a2ee6ba981da2af34a\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-18 08:00:00\",\"desc\":\"\\u4ee5\\u524d\\u5bf9\\u4f60\\u7684\\u559c\\u6b22\\uff0c\\n\\u662f\\u89c1\\u4f60\\uff0c\\u5ff5\\u4f60\\uff0c\\u966a\\u4f34\\u4f60\\u3002\\n\\u73b0\\u5728\\u5bf9\\u4f60\\u7684\\u559c\\u6b22\\uff0c\\n\\u662f\\u4e0d\\u95ee\\uff0c\\u4e0d\\u770b\\uff0c\\u4e0d\\u53e8\\u6270\\u3002\",\"images\":[\"http://gank.io/images/19c99c447e0a40a6b3ff89951957cfb1\"],\"likeCounts\":0,\"publishedAt\":\"2020-05-18 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c89\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/19c99c447e0a40a6b3ff89951957cfb1\",\"views\":240},{\"_id\":\"5e959197808d6d2fe6b56ed4\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-17 08:00:00\",\"desc\":\"\\u53ea\\u8981\\u7ed3\\u5c40\\u662f\\u559c\\u5267\\uff0c\\u8fc7\\u7a0b\\u4f60\\u8981\\u6211\\u600e\\u4e48\\u54ed\\u90fd\\u884c\\uff0c\\u5e78\\u798f\\u53ef\\u4ee5\\u6765\\u7684\\u6162\\u4e00\\u4e9b\\uff0c\\n\\u53ea\\u8981\\u5b83\\u662f\\u771f\\u7684\\uff0c\\u5982\\u679c\\u6700\\u540e\\u80fd\\u5728\\u4e00\\u8d77\\uff0c\\u665a\\u70b9\\u6211\\u771f\\u7684\\u65e0\\u6240\\u8c13\\u7684\\u3002\",\"images\":[\"http://gank.io/images/f0c192e3e335400db8a709a07a891b2e\"],\"likeCounts\":0,\"publishedAt\":\"2020-05-17 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c88\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/f0c192e3e335400db8a709a07a891b2e\",\"views\":275},{\"_id\":\"5e95915f808d6d2fe6b56ed3\",\"author\":\"\\u9e22\\u5a9b\",\"category\":\"Girl\",\"createdAt\":\"2020-05-16 08:00:00\",\"desc\":\"\\u82e5\\u4e0d\\u662f\\u60c5\\u6df1\\u4f3c\\u6d77\\uff0c\\u601d\\u5ff5\\u53c8\\u600e\\u4f1a\\u6cdb\\u6ee5\\u6210\\u707e\\u3002\",\"images\":[\"http://gank.io/images/bdb35e4b3c0045c799cc7a494a3db3e0\"],\"likeCounts\":0,\"publishedAt\":\"2020-05-16 08:00:00\",\"stars\":1,\"title\":\"\\u7b2c87\\u671f\",\"type\":\"Girl\",\"url\":\"http://gank.io/images/bdb35e4b3c0045c799cc7a494a3db3e0\",\"views\":437}],\"page\":1,\"page_count\":10,\"status\":100,\"total_counts\":96}";
}


④ 创建列表的item布局文件


layout文件下创建item_data_list.xml布局文件


20200526150152124.png

20200526171746739.png


代码如下:

<?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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="12dp"
    android:orientation="vertical">
    <LinearLayout
        android:id="@+id/item_data"
        android:foreground="@drawable/bg_white"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/shape_blue_bg_5"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:padding="12dp">
        <ImageView
            android:id="@+id/iv_check"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_marginRight="12dp"
            android:background="@mipmap/icon_choose_default"
            android:button="@null" />
        <ImageView
            android:id="@+id/iv_img"
            android:layout_width="150dp"
            android:layout_height="200dp"
            android:background="@drawable/ic_launcher_background"
            android:scaleType="centerCrop" />
        <TextView
            android:id="@+id/tv_video_info"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="12dp"
            android:text="测试数据"
            android:textColor="@color/white"
            android:textSize="16sp" />
    </LinearLayout>
</LinearLayout>


⑤ 列表适配器


创建一个适配器DataAdapter.java


20200526155727280.png


代码如下:

package com.llw.recyclerviewusedemo;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import java.util.List;
/**
 * 适配器
 */
public class DataAdapter extends BaseQuickAdapter<DataResponse.DataBean, BaseViewHolder> {
    private static final int STATE_DEFAULT = 0;//默认状态
    int mEditMode = STATE_DEFAULT;
    public DataAdapter(int layoutResId, @Nullable List<DataResponse.DataBean> data) {
        super(layoutResId, data);
    }
    @Override
    protected void convert(BaseViewHolder helper, DataResponse.DataBean item) {
        //Glide加载网络图片
        Glide.with(mContext).load(item.getUrl()).into((ImageView) helper.getView(R.id.iv_img));
        //TextView赋值
        helper.setText(R.id.tv_video_info, item.getAuthor());
        helper.addOnClickListener(R.id.item_data);//添加item点击事件
        if (mEditMode == STATE_DEFAULT) {
            //默认不显示
            helper.getView(R.id.iv_check).setVisibility(View.GONE);
        } else {
            //显示   显示之后再做点击之后的判断
            helper.getView(R.id.iv_check).setVisibility(View.VISIBLE);
            if (item.isSelect()) {//点击时,true 选中
                helper.getView(R.id.iv_check).setBackgroundResource(R.mipmap.icon_choose_selected);
            } else {//false 取消选中
                helper.getView(R.id.iv_check).setBackgroundResource(R.mipmap.icon_choose_default);
            }
        }
    }
    /**
     * 设置编辑状态   接收Activity中传递的值,并改变Adapter的状态
     */
    public void setEditMode(int editMode) {
        mEditMode = editMode;
        notifyDataSetChanged();//刷新
    }
}


⑥ 主要逻辑编写


进入到MainActivity.java


这里绑定一些控件并声明一些需要用到的变量


20200526165316366.png


初始化列表数据

  //初始化列表数据
    private void initList() {
        mAdapter = new DataAdapter(R.layout.item_data_list, mList);//绑定视图和数据
        rv.setLayoutManager(new LinearLayoutManager(this));//设置线性布局管理器
        rv.setAdapter(mAdapter);//设置适配器
        Gson gson = new Gson();
        DataResponse dataBean = gson.fromJson(JsonData.JSON, DataResponse.class);//解析数据
        List<DataResponse.DataBean> resultsBeans = dataBean.getData();
        if (resultsBeans.size() > 0) {
            mList.clear();
            mList.addAll(resultsBeans);
            mAdapter.notifyDataSetChanged();//刷新数据
            rv.setVisibility(View.VISIBLE);
            rvNormalShow.setVisibility(View.GONE);
        } else {
            rv.setVisibility(View.GONE);
            rvNormalShow.setVisibility(View.VISIBLE);
        }
        refresh.finishRefresh();//数据加载出来之后,结束下拉动作
    }


处理点击事件

  //页面的点击事件
    @OnClick({R.id.tv_edit, R.id.tv_check_all, R.id.tv_delete})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.tv_edit://编辑
                updateEditState();
                break;
            case R.id.tv_check_all://全选
                setAllItemChecked();
                break;
            case R.id.tv_delete://删除
                deleteCheckItem();
                break;
        }
    }


改变编辑状态

  //改变编辑状态
    private void updateEditState() {
        mEditMode = mEditMode == STATE_DEFAULT ? STATE_EDIT : STATE_DEFAULT;
        if (mEditMode == STATE_EDIT) {
            tvEdit.setText("取消");
            layBottom.setVisibility(View.VISIBLE);
            editorStatus = true;//处于编辑状态
        } else {
            tvEdit.setText("编辑");
            layBottom.setVisibility(View.GONE);
            editorStatus = false;//处于默认状态
            setAllItemUnchecked();//取消全选
        }
        mAdapter.setEditMode(mEditMode);//刷新Adapter
        mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                if (editorStatus) {//编辑状态
                    DataResponse.DataBean dataBean = mList.get(position);//赋值
                    boolean isSelect = dataBean.isSelect();
                    if (!isSelect) {
                        index++;
                        dataBean.setSelect(true);
                    } else {
                        dataBean.setSelect(false);
                        index--;
                    }
                    if (index == 0) {//当前选中item数量
                        tvDelete.setText("删除");
                    } else {
                        tvDelete.setText("删除(" + index + ")");
                    }
                    mAdapter.notifyDataSetChanged();
                }
            }
        });
    }


全部选中

  //全部选中
    private void setAllItemChecked() {
        if (mAdapter == null) return;
        for (int i = 0; i < mList.size(); i++) {
            mList.get(i).setSelect(true);
        }
        mAdapter.notifyDataSetChanged();
        index = mList.size();
        tvDelete.setText("删除(" + index + ")");
    }


取消全部选中

  //取消全部选中
    private void setAllItemUnchecked() {
        if (mAdapter == null) return;
        for (int i = 0; i < mList.size(); i++) {
            mList.get(i).setSelect(false);
        }
        mAdapter.notifyDataSetChanged();
        tvDelete.setText("删除");
        index = 0;
    }


删除选中的item

  //删除选中的item
    private void deleteCheckItem() {
        if (mAdapter == null) return;
        for (int i = mList.size() - 1; i >= 0; i--) {
            if (mList.get(i).isSelect() == true) {
                mList.remove(i);
            }
        }
        //删除选中的item之后判断是否还有数据,没有则退出编辑模式
        if (mList.size() != 0) {
            index = 0;//删除之后置为0
            tvDelete.setText("删除");
        }else {
            tvEdit.setText("编辑");
            layBottom.setVisibility(View.GONE);
            editorStatus = false;
            //没有数据自然也不存在编辑了
            tvEdit.setVisibility(View.GONE);
            rvNormalShow.setVisibility(View.VISIBLE);
            //启用下拉
            refresh.setEnableRefresh(true);
            //下拉刷新
            refresh.setOnRefreshListener(refreshLayout -> {
                //重新装填数据
                initList();
                mEditMode = STATE_DEFAULT;//恢复默认状态
                editorStatus = false;//恢复默认状态
                tvEdit.setVisibility(View.VISIBLE);//显示编辑
            });
        }
        mAdapter.notifyDataSetChanged();//刷新
    }


最后在onCreate()方法中使用

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        initList();
        //禁用下拉和上拉
        refresh.setEnableRefresh(false);
        refresh.setEnableLoadMore(false);
    }


做完这些基本上功能就已经都实现了,下面就是锦上添花了,增加动画效果,


⑦ 增加动画效果


列表的出现动画,在res下创建一个anim文件夹,这下面有两个xml文件


20200527093226868.png


layout_animation_from_bottom.xml


<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:animation="@anim/item_animation_from_bottom"
    android:delay="15%"
    android:animationOrder="normal"
    />


item_animation_from_bottom.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">
    <translate
        android:fromYDelta="50%p"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toYDelta="0" />
    <alpha
        android:fromAlpha="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toAlpha="1" />
</set>


然后创建一个动画类RecyclerViewAnimation.java,用来使用


20200527093446864.png


代码如下:


package com.llw.recyclerviewusedemo;
import android.content.Context;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import androidx.recyclerview.widget.RecyclerView;
/**
 * RecyclerView动画
 */
public class RecyclerViewAnimation {
    //数据变化时显示动画
    public static void runLayoutAnimation(final RecyclerView recyclerView) {
        final Context context = recyclerView.getContext();
        final LayoutAnimationController controller =
                AnimationUtils.loadLayoutAnimation(context, R.anim.layout_animation_from_bottom);
        recyclerView.setLayoutAnimation(controller);
        recyclerView.getAdapter().notifyDataSetChanged();
        recyclerView.scheduleLayoutAnimation();
    }
}


使用方法

import static com.llw.recyclerviewusedemo.RecyclerViewAnimation.runLayoutAnimation;//导入

202005270939243.png

20200527094047180.png

20200527094047180.png


MainActivity.java完整代码

package com.llw.recyclerviewusedemo;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.google.gson.Gson;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import static com.llw.recyclerviewusedemo.RecyclerViewAnimation.runLayoutAnimation;
public class MainActivity extends AppCompatActivity {
    @BindView(R.id.tv_edit)
    TextView tvEdit;//编辑
    @BindView(R.id.rv_normal_show)
    LinearLayout rvNormalShow;//没有数据时展示的布局
    @BindView(R.id.rv)
    RecyclerView rv;//列表
    @BindView(R.id.refresh)
    SmartRefreshLayout refresh;//刷新布局
    @BindView(R.id.tv_check_all)
    TextView tvCheckAll;//全选
    @BindView(R.id.tv_delete)
    TextView tvDelete;//删除
    @BindView(R.id.lay_bottom)
    LinearLayout layBottom;//底部布局
    private static final int STATE_DEFAULT = 0;//默认状态
    private static final int STATE_EDIT = 1;//编辑状态
    private int mEditMode = STATE_DEFAULT;
    private boolean editorStatus = false;//是否为编辑状态
    private int index = 0;//当前选中的item数
    List<DataResponse.DataBean> mList = new ArrayList<>();//列表
    DataAdapter mAdapter;//适配器
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        initList();
        //禁用下拉和上拉
        refresh.setEnableRefresh(false);
        refresh.setEnableLoadMore(false);
    }
    //初始化列表数据
    private void initList() {
        mAdapter = new DataAdapter(R.layout.item_data_list, mList);//绑定视图和数据
        rv.setLayoutManager(new LinearLayoutManager(this));//设置线性布局管理器
        rv.setAdapter(mAdapter);//设置适配器
        Gson gson = new Gson();
        DataResponse dataBean = gson.fromJson(JsonData.JSON, DataResponse.class);//解析数据
        List<DataResponse.DataBean> resultsBeans = dataBean.getData();
        if (resultsBeans.size() > 0) {
            mList.clear();
            mList.addAll(resultsBeans);
            mAdapter.notifyDataSetChanged();//刷新数据
            runLayoutAnimation(rv);//动画显示
            rv.setVisibility(View.VISIBLE);
            rvNormalShow.setVisibility(View.GONE);
        } else {
            rv.setVisibility(View.GONE);
            rvNormalShow.setVisibility(View.VISIBLE);
        }
        refresh.finishRefresh();//数据加载出来之后,结束下拉动作
    }
    //页面的点击事件
    @OnClick({R.id.tv_edit, R.id.tv_check_all, R.id.tv_delete})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.tv_edit://编辑
                updateEditState();
                break;
            case R.id.tv_check_all://全选
                setAllItemChecked();
                break;
            case R.id.tv_delete://删除
                deleteCheckItem();
                break;
        }
    }
    //改变编辑状态
    private void updateEditState() {
        mEditMode = mEditMode == STATE_DEFAULT ? STATE_EDIT : STATE_DEFAULT;
        if (mEditMode == STATE_EDIT) {
            tvEdit.setText("取消");
            layBottom.setVisibility(View.VISIBLE);
            editorStatus = true;
        } else {
            tvEdit.setText("编辑");
            layBottom.setVisibility(View.GONE);
            editorStatus = false;
            setAllItemUnchecked();//取消全选
        }
        mAdapter.setEditMode(mEditMode);
        mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                if (editorStatus) {//编辑状态
                    DataResponse.DataBean dataBean = mList.get(position);
                    boolean isSelect = dataBean.isSelect();
                    if (!isSelect) {
                        index++;
                        dataBean.setSelect(true);
                    } else {
                        dataBean.setSelect(false);
                        index--;
                    }
                    if (index == 0) {
                        tvDelete.setText("删除");
                    } else {
                        tvDelete.setText("删除(" + index + ")");
                    }
                    mAdapter.notifyDataSetChanged();
                }
            }
        });
    }
    //全部选中
    private void setAllItemChecked() {
        if (mAdapter == null) return;
        for (int i = 0; i < mList.size(); i++) {
            mList.get(i).setSelect(true);
        }
        mAdapter.notifyDataSetChanged();
        index = mList.size();
        tvDelete.setText("删除(" + index + ")");
    }
    //取消全部选中
    private void setAllItemUnchecked() {
        if (mAdapter == null) return;
        for (int i = 0; i < mList.size(); i++) {
            mList.get(i).setSelect(false);
        }
        mAdapter.notifyDataSetChanged();
        tvDelete.setText("删除");
        index = 0;
    }
    //删除选中的item
    private void deleteCheckItem() {
        if (mAdapter == null) return;
        for (int i = mList.size() - 1; i >= 0; i--) {
            if (mList.get(i).isSelect() == true) {
                mList.remove(i);
            }
        }
        //删除选中的item之后判断是否还有数据,没有则退出编辑模式
        if (mList.size() != 0) {
            index = 0;//删除之后置为0
            tvDelete.setText("删除");
        }else {
            tvEdit.setText("编辑");
            layBottom.setVisibility(View.GONE);
            editorStatus = false;
            //没有数据自然也不存在编辑了
            tvEdit.setVisibility(View.GONE);
            rvNormalShow.setVisibility(View.VISIBLE);
            //启用下拉
            refresh.setEnableRefresh(true);
            //下拉刷新
            refresh.setOnRefreshListener(refreshLayout -> {
                //重新装填数据
                initList();
                index = 0;
                mEditMode = STATE_DEFAULT;//恢复默认状态
                editorStatus = false;//恢复默认状态
                tvDelete.setText("删除");
                tvEdit.setVisibility(View.VISIBLE);//显示编辑
            });
        }
        mAdapter.notifyDataSetChanged();
        runLayoutAnimation(rv);//动画显示
    }
}


尾声


感谢每一个阅读的人,写文章只为积累自己的经验,不至于忘记,如果能对看文章的您有帮助,那就更好了,我是初学者-Study,山高水长,后会有期~

相关文章
|
4月前
|
Ubuntu 开发工具 Android开发
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
本文介绍了在基于Ubuntu 22.04的环境下配置Python 3.9、安装repo工具、下载和同步AOSP源码包以及处理repo同步错误的详细步骤。
277 0
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
|
2月前
|
Android开发 UED
Android 中加载 Gif 动画
【10月更文挑战第20天】加载 Gif 动画是 Android 开发中的一项重要技能。通过使用第三方库或自定义实现,可以方便地在应用中展示生动的 Gif 动画。在实际应用中,需要根据具体情况进行合理选择和优化,以确保用户体验和性能的平衡。可以通过不断的实践和探索,进一步掌握在 Android 中加载 Gif 动画的技巧和方法,为开发高质量的 Android 应用提供支持。
|
3月前
|
Android开发 开发者 索引
Android实战经验之如何使用DiffUtil提升RecyclerView的刷新性能
本文介绍如何使用 `DiffUtil` 实现 `RecyclerView` 数据集的高效更新,避免不必要的全局刷新,尤其适用于处理大量数据场景。通过定义 `DiffUtil.Callback`、计算差异并应用到适配器,可以显著提升性能。同时,文章还列举了常见错误及原因,帮助开发者避免陷阱。
278 9
|
3月前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
79 8
|
4月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
165 1
|
4月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
608 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
4月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
205 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
4月前
|
Android开发
我的Android 进阶修炼(1): AOSP源码根目录结构
本文介绍了AOSP源码的根目录结构,提供了基于MTK9269 Android 9.0源码的目录说明,帮助读者了解AOSP源码的组织方式和各目录的功能。
247 0
我的Android 进阶修炼(1): AOSP源码根目录结构
|
4月前
|
开发工具 Android开发 git
全志H713 Android 11 :给AOSP源码,新增一个Product
本文介绍了在全志H713 Android 11平台上新增名为myboard的产品的步骤,包括创建新的device目录、编辑配置文件、新增内核配置、记录差异列表以及编译kernel和Android系统的详细过程。
220 0