一个让你爱不释手的万能Adapter(Kotlin+Databinding+CommonAdapter的碰撞)

感谢点进来看的各位技术小可爱,本篇文章为纯干货,希望阅读完本文的你能有所收获,帮助大家提高项目的开发效率。

阅读本文你将收获:

1、简洁好用的万能适配器一个
2、DataBinding的简单使用
3、Kotlin和DataBinding结合使用遇到的问题
4、Github制作自己的在线Library

废话不多说,先来直接看一下,CommonAdaper结合DataBinding后,咱们写一个列表的Adapter代码成本,仅仅只需要7行代码。如下:

class ADA_ChapterFilter constructor(context: Context): CommonAdapter<DataBean, ItemLayoutBinding>(context) {
    override fun convert(viewBinding: ItemLayoutBinding?, holder: ViewHolder.BindingHolder?, bean: DataBean?, position: Int) {
        viewBinding!!.dataBean= bean
    }
    override fun itemLayoutId(): Int {
        return R.layout.item_layout
    }
}

是,你没看没错,就是这么简洁!!!省去了一大堆数据装载时的setText等冗余代码。下面是使用它实现的列表效果:
demo.gif

《一》本篇简介

关于通用的适配器,相信大家也看过不少博客,如果还有在用传统的方式写列表适配器的新手,那要赶快跟紧步伐啦,因为你可能已经落后不是一点点了哦。当然,其实即使是要自己去手写一套万能Adapter,也并不是很困难的事情,所以大家不需要畏惧,其实核心思想就是代码的封装和抽象,以及一些设计模式的运用,感兴趣的可以自己动手试试。本篇文章的基础,是鸿洋大神的BaseAdapter,支持ListView和RecyclerView的Adapter,且能支持多类型列表的适配 ,能很好的满足日常项目开发的需求。本篇文章就是在他写的BaseAdapter的基础上,进行了改造,所以下面开始介绍经我简单改造后的DataBindingCommonAdapter。

《二》使用方法

(1)在你的工程根目录下添加:

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
}

(2)在你的app的build.gradle下添加依赖

dependencies {
       implementation 'com.github.GraceJoJo:DataBindingBaseAdapter:1.0.1'
}

(3)RecyclerView中Adapter的用法:

class ADA_RecyclerItem constructor(context: Context): CommonAdapter<DataBean, ItemLayoutBinding>(context) {
    override fun convert(viewBinding: ItemLayoutBinding?, holder: ViewHolder.BindingHolder?, bean: DataBean?, position: Int) {
        viewBinding!!.dataBean = bean
    }

    override fun itemLayoutId(): Int {
        return R.layout.item_layout
    }
}

Activity或Fragment使用:

        val dataList= ArrayList<DataBean>() //模拟数据
        recyclerview.layoutManager = LinearLayoutManager(this)
        var mAdapter = ADA_RecyclerItem(this)
        recyclerview.adapter = mAdapter
        mAdapter.update(dataList,true)

(4)ListView或者GridView的Adapter的用法:

class ADA_ListItem constructor(context: Context): CommonAdapterListView<DataBean, ItemLayoutBinding>(context) {

    override fun convert(viewBinding: ItemLayoutBinding?, holder: ViewHolderListView?, bean: DataBean?, position: Int) {
        viewBinding!!.dataBean = bean
    }

    override fun itemLayoutId(): Int {
        return R.layout.item_layout
    }
}

Activity或Fragment使用:

        val dataList= ArrayList<DataBean>() //模拟数据
        var mAdapter = ADA_ListItem(this)
        listview.adapter = mAdapter
        mAdapter.update(dataList,true)

《三》对BaseAdapter的改造思路

不了解鸿洋大神的万能适配方案的,可以点击:BaseAdapter先学习了解一下,也可以直接下载文末我改造后的案例。本篇文章着重讲解RecyclerView的Adapter的改造,完整的源码请去文末下载哦~

我们来看看,BaseAdapter未改造前,RecyclerView的Adapter写法可能是这样的:
image.png

其实相比原始的写法还是很简单的,但是如果涉及的字段比较多,那么就会有大量的setText()等,虽然BaseAdapter已经很大程度上简化了Adapter,但是我们还是每次都要写很多重复的简单代码。

分析:归根结底,其实写Adapter无非就是下面几个要素
(1)写一个item布局文件
(2)告诉Adapter每个item对应的bean是什么
(3)绑定数据:给item布局中的控件设置对应的数据

①item布局,不管你如何简化,都得写上,这个毋庸置疑;
②可以看到未改造前的BaseAdapter,已经将第二点bean类以泛型的形式抽离出来了;
③那么我们看看第三点,是不是可以对它做点什么。借鉴着把bean类抽离一个泛型的思想,结合DataBinding,每个item布局文件会对应一个ViewDataBinding,所以我把ViewDataBinding抽离出一个泛型出来。

1、在CommonAdapter中抽离出ViewDataBinding的泛型:


image.png

2、在ViewHolder中使用DataBinding绑定布局:


image.png

3、在onBindViewHolder中把布局对应的某一个具体的ViewBinding传出去,供数据更新时给控件设置数据使用:


image.png

4、改造后的使用:

class ADA_RecyclerItem constructor(context: Context): CommonAdapter<DataBean, ItemLayoutBinding>(context) {
    override fun convert(viewBinding: ItemLayoutBinding?, holder: ViewHolder.BindingHolder?, bean: DataBean?, position: Int) {
        viewBinding!!.dataBean = bean
    }

    override fun itemLayoutId(): Int {
        return R.layout.item_layout
    }
}
总结:

通过简单的改造,我们写Adapter时,就只需要给Adapter一个布局、一个具体的bean、一个布局文件对应的具体的ViewDataBinding,然后在布局文件中使用DataBinding把数据绑定写好,一个Adapter的工作就完成了。

结合了DataBinding后,我们将数据与页面的绑定,在写布局的时候就把页面控件对应的数据绑定了,这样省去了大量的BindView操作和对view设置数据的处理。

《四》DataBinding的简单介绍——MVVM

DataBinding不知道大家熟悉与否,不管怎样,我都要在这里隆重的介绍一下它,因为让代码如此简洁的大功臣,正是DataBinding。

(1)DataBinding是什么?

① DataBinding是一个support library,所以它可以支持所有的android sdk,最低可以到android2.1(API7)。
② 使用DataBinding需要Android Gradle插件的支持,版本至少在1.5以上,需要的Android studio的版本在1.3以上。

(2)首先,我们在需要用到DataBinding的module或者library的build.gradle中,使其支持DataBinding。android{ }下添加如下代码:

// 打开Data Binding , 这样我们可以通过声明式布局以精简的代码来绑定应用程序逻辑和布局
    dataBinding{
        enabled = true
    }

(3)XML布局中做声明数据绑定

image.png

例如:以本例Adapter对于的item_layout的布局为例,写法如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@android:color/white">
    <!--data节点下一个variable节点代表一个变量,
    name属性根据需要自己取名,type为需要用到的Model的全路径,
    功能相当于写代码的时候引入一个类的功能-->
    <data>

        <variable
            name="dataBean"
            type="com.example.jojo.databinding_commonadapter.DataBean"></variable>
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="15dp"
        android:paddingRight="15dp">

        <FrameLayout
            android:id="@+id/ll_rank"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true">


            <TextView
                android:id="@+id/tv_rank_num"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="3dp"
                android:layout_marginRight="5dp"
                android:textColor="#2E3439"
                android:textSize="12sp"
                android:textStyle="bold" />
        </FrameLayout>

        <ImageView
            android:id="@+id/iv_cover"
            android:layout_width="60dp"
            android:layout_height="80dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="12dp"
            android:layout_toRightOf="@+id/ll_rank"
            android:padding="1px"
            android:scaleType="fitXY"
            app:imageUrl="@{bean.covor_url}" />

        <LinearLayout
            android:id="@+id/ll_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="12dp"
            android:layout_toRightOf="@+id/iv_cover"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginBottom="7dp"
                android:includeFontPadding="false"
                android:text="@{bean.name_cn}"
                android:textColor="#2E3439"
                android:textSize="14sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginBottom="6dp"
                android:text="@{bean.author}"
                android:textColor="#666666"
                android:textSize="12sp" />

            <TextView
                android:id="@+id/tv_comment"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginBottom="11dp"
                android:ellipsize="end"
                android:inputType="textMultiLine"
                android:lines="2"
                android:text="@{bean.comment}"
                android:textColor="#999999"
                android:textSize="10sp" />
        </LinearLayout>
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_below="@+id/ll_info"
            android:layout_marginTop="10dp"
            android:background="#f9f9f9"></View>

    </RelativeLayout>
</layout>

(3)定义数据绑定的Data对象:

data class DataBean constructor(val name_cn: String, val comment: String, val author: String, val covor_url: String)

(4)使用DataBindingUtil,绑定布局与数据。

Android studio会根据layout文件自动生成一个默认的Binding类,类名是根据layout文件名生成的,并有"Binding"后缀结束。

情景1:在Activity中

MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user); //数据更新,设置给绑定的控件,此时即完成了页面的数据刷新

情景2:在Fragment中

 @Override
 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            // Inflate the layout for this fragment
   FragmentLayoutBinding   viewBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_layout, container, false);
   return viewDataBinding.getRoot();
}

关于DataBinding,更多详细的介绍及一些高级用法,我就不在本文多加赘述了

《五》遇到的问题及解决

(1)如果不熟悉DataBinding的朋友可能会有疑问。如果是加载图片或者是某个控件绑定的数据展示需要特殊处理咋办?这就是DataBinding的知识了,这里我简单说一下这种情况的处理方法。

DataBinding有个BindingAdapter,它的功能是用来设置view的属性值。

假设你要在布局中显示一个圆角图片,咋办?你可以新建一个类,叫ViewBindingAdapter。

public class ViewBindingAdapter {
 @BindingAdapter({"app:imageUrl"})
    public static void loadImage(ImageView imageView, String url) {
        RequestOptions requestOptions = new RequestOptions()
                .priority(Priority.HIGH)
                .transform(new CircleCrop());
        Glide.with(MyApplication.context)
                .load(url)
                .apply(requestOptions)
                .transition(new DrawableTransitionOptions().crossFade())
                .into(imageView);
    }

    @BindingAdapter({"app:date_text"})
    public static void setDateText(TextView tv, String text) {
        //处理文本显示
        tv.setText(text + "年");
    }
}

布局文件中引用:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@android:color/white">
    <!--data节点下一个variable节点代表一个变量,
    name属性根据需要自己取名,type为需要用到的Model的全路径,
    功能相当于写代码的时候引入一个类的功能-->
    <data>

        <variable
            name="dataBean"
            type="com.example.jojo.databinding.DataBean"></variable>
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:id="@+id/iv_cover"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:imageUrl="@{dataBean.covor_url}" />
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:date_text="@{dataBean.time}"
                android:textSize="14sp" />
    </RelativeLayout>
</layout>

像这样,你可以在ViewBindingAdapter里创建多个@BindingAdapter注解的方法,来特殊处理你在布局文件要给控件绑定的数据值。

(2)Kotlin下使用DataBinding遇到的问题
dataBinding+kotlin环境下会报错:Error: Unresolved reference: databinding
解决:在app的build.gradle下添加,sync now即可恢复正常。
我的app的Android plugin版本为 classpath 'com.android.tools.build:gradle:3.0.1'


dependencies {
  
    kapt 'com.android.databinding:compiler:3.0.1'
 
}
kapt {
    generateStubs = true
}
写在结尾:

对本文有问题的朋友欢迎大家留言交流哦~

(1)本文完整Demo请戳github地址
(2)感谢鸿洋大神的BaseAdapter
(3)GitHub上制作自己的Library,直接compile使用

最后,附上我的一个Kotlin编写+组件化开发的开源项目Designer

Kotlin+组件化开发实践—开源项目Designer-App

Designer项目算是倾注了我蛮多心血了,每个页面和功能都当成是上线的App来做,App的logo还特地做了UI设计😃力求做到精致和完善,其中还包括了很多自己项目开发中的经验汇总和对新技术的探索和整合,希望对各位读者有所帮助,欢迎点个star,follow,或者给个小心心,嘻嘻😝也可以分享给你更多的朋友一起学习,您的支持是我不断前进的动力。如果有任何问题,欢迎在GitHub上给我提issue或者留言。

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

推荐阅读更多精彩内容