一個讓你愛不釋手的萬能Adapter(Kotlin+Databinding+CommonAdapter的碰撞)

感謝點進來看的各位技術(shù)小可愛窗声,本篇文章為純干貨,希望閱讀完本文的你能有所收獲彼妻,幫助大家提高項目的開發(fā)效率嫌佑。

閱讀本文你將收獲:

1豆茫、簡潔好用的萬能適配器一個
2、DataBinding的簡單使用
3屋摇、Kotlin和DataBinding結(jié)合使用遇到的問題
4揩魂、Github制作自己的在線Library

廢話不多說,先來直接看一下炮温,CommonAdaper結(jié)合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
    }
}

是倦挂,你沒看沒錯,就是這么簡潔597皆!省去了一大堆數(shù)據(jù)裝載時的setText等冗余代碼涛癌。下面是使用它實現(xiàn)的列表效果:
demo.gif

《一》本篇簡介

關(guān)于通用的適配器犯戏,相信大家也看過不少博客,如果還有在用傳統(tǒng)的方式寫列表適配器的新手拳话,那要趕快跟緊步伐啦先匪,因為你可能已經(jīng)落后不是一點點了哦。當然弃衍,其實即使是要自己去手寫一套萬能Adapter呀非,也并不是很困難的事情爽篷,所以大家不需要畏懼洞拨,其實核心思想就是代碼的封裝和抽象茅茂,以及一些設(shè)計模式的運用辽聊,感興趣的可以自己動手試試。本篇文章的基礎(chǔ)闻妓,是鴻洋大神的BaseAdapter岁钓,支持ListView和RecyclerView的Adapter狸捅,且能支持多類型列表的適配 激涤,能很好的滿足日常項目開發(fā)的需求。本篇文章就是在他寫的BaseAdapter的基礎(chǔ)上判呕,進行了改造倦踢,所以下面開始介紹經(jīng)我簡單改造后的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>() //模擬數(shù)據(jù)
        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>() //模擬數(shù)據(jù)
        var mAdapter = ADA_ListItem(this)
        listview.adapter = mAdapter
        mAdapter.update(dataList,true)

《三》對BaseAdapter的改造思路

不了解鴻洋大神的萬能適配方案的侠草,可以點擊:BaseAdapter先學習了解一下辱挥,也可以直接下載文末我改造后的案例。本篇文章著重講解RecyclerView的Adapter的改造边涕,完整的源碼請去文末下載哦~

我們來看看晤碘,BaseAdapter未改造前褂微,RecyclerView的Adapter寫法可能是這樣的:
image.png

其實相比原始的寫法還是很簡單的,但是如果涉及的字段比較多园爷,那么就會有大量的setText()等宠蚂,雖然BaseAdapter已經(jīng)很大程度上簡化了Adapter,但是我們還是每次都要寫很多重復(fù)的簡單代碼童社。

分析:歸根結(jié)底求厕,其實寫Adapter無非就是下面幾個要素
(1)寫一個item布局文件
(2)告訴Adapter每個item對應(yīng)的bean是什么
(3)綁定數(shù)據(jù):給item布局中的控件設(shè)置對應(yīng)的數(shù)據(jù)

①item布局,不管你如何簡化扰楼,都得寫上呀癣,這個毋庸置疑;
②可以看到未改造前的BaseAdapter弦赖,已經(jīng)將第二點bean類以泛型的形式抽離出來了项栏;
③那么我們看看第三點,是不是可以對它做點什么蹬竖。借鑒著把bean類抽離一個泛型的思想沼沈,結(jié)合DataBinding,每個item布局文件會對應(yīng)一個ViewDataBinding案腺,所以我把ViewDataBinding抽離出一個泛型出來庆冕。

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


image.png

2劈榨、在ViewHolder中使用DataBinding綁定布局:


image.png

3访递、在onBindViewHolder中把布局對應(yīng)的某一個具體的ViewBinding傳出去,供數(shù)據(jù)更新時給控件設(shè)置數(shù)據(jù)使用:


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
    }
}
總結(jié):

通過簡單的改造拷姿,我們寫Adapter時,就只需要給Adapter一個布局旱函、一個具體的bean响巢、一個布局文件對應(yīng)的具體的ViewDataBinding,然后在布局文件中使用DataBinding把數(shù)據(jù)綁定寫好棒妨,一個Adapter的工作就完成了踪古。

結(jié)合了DataBinding后,我們將數(shù)據(jù)與頁面的綁定券腔,在寫布局的時候就把頁面控件對應(yīng)的數(shù)據(jù)綁定了伏穆,這樣省去了大量的BindView操作和對view設(shè)置數(shù)據(jù)的處理。

《四》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 , 這樣我們可以通過聲明式布局以精簡的代碼來綁定應(yīng)用程序邏輯和布局
    dataBinding{
        enabled = true
    }

(3)XML布局中做聲明數(shù)據(jù)綁定

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節(jié)點下一個variable節(jié)點代表一個變量,
    name屬性根據(jù)需要自己取名酷勺,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)定義數(shù)據(jù)綁定的Data對象:

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

(4)使用DataBindingUtil,綁定布局與數(shù)據(jù)脆诉。

Android studio會根據(jù)layout文件自動生成一個默認的Binding類甚亭,類名是根據(jù)layout文件名生成的,并有"Binding"后綴結(jié)束击胜。

情景1:在Activity中

MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user); //數(shù)據(jù)更新亏狰,設(shè)置給綁定的控件,此時即完成了頁面的數(shù)據(jù)刷新

情景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();
}

關(guān)于DataBinding偶摔,更多詳細的介紹及一些高級用法暇唾,我就不在本文多加贅述了

《五》遇到的問題及解決

(1)如果不熟悉DataBinding的朋友可能會有疑問。如果是加載圖片或者是某個控件綁定的數(shù)據(jù)展示需要特殊處理咋辦辰斋?這就是DataBinding的知識了策州,這里我簡單說一下這種情況的處理方法。

DataBinding有個BindingAdapter宫仗,它的功能是用來設(shè)置view的屬性值够挂。

假設(shè)你要在布局中顯示一個圓角圖片,咋辦藕夫?你可以新建一個類孽糖,叫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節(jié)點下一個variable節(jié)點代表一個變量毅贮,
    name屬性根據(jù)需要自己取名梭姓,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里創(chuàng)建多個@BindingAdapter注解的方法,來特殊處理你在布局文件要給控件綁定的數(shù)據(jù)值罪既。

(2)Kotlin下使用DataBinding遇到的問題
dataBinding+kotlin環(huán)境下會報錯:Error: Unresolved reference: databinding
解決:在app的build.gradle下添加铸题,sync now即可恢復(fù)正常铡恕。
我的app的Android plugin版本為 classpath 'com.android.tools.build:gradle:3.0.1'


dependencies {
  
    kapt 'com.android.databinding:compiler:3.0.1'
 
}
kapt {
    generateStubs = true
}
寫在結(jié)尾:

對本文有問題的朋友歡迎大家留言交流哦~

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

最后丢间,附上我的一個Kotlin編寫+組件化開發(fā)的開源項目Designer

Kotlin+組件化開發(fā)實踐—開源項目Designer-App

Designer項目算是傾注了我蠻多心血了探熔,每個頁面和功能都當成是上線的App來做,App的logo還特地做了UI設(shè)計??力求做到精致和完善烘挫,其中還包括了很多自己項目開發(fā)中的經(jīng)驗匯總和對新技術(shù)的探索和整合诀艰,希望對各位讀者有所幫助,歡迎點個star饮六,follow其垄,或者給個小心心,嘻嘻??也可以分享給你更多的朋友一起學習卤橄,您的支持是我不斷前進的動力绿满。如果有任何問題,歡迎在GitHub上給我提issue或者留言窟扑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喇颁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嚎货,更是在濱河造成了極大的恐慌橘霎,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殖属,死亡現(xiàn)場離奇詭異姐叁,居然都是意外死亡,警方通過查閱死者的電腦和手機忱辅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門七蜘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人墙懂,你說我怎么就攤上這事橡卤。” “怎么了损搬?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵碧库,是天一觀的道長。 經(jīng)常有香客問我巧勤,道長嵌灰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任颅悉,我火速辦了婚禮沽瞭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘剩瓶。我一直安慰自己驹溃,他們只是感情好城丧,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著豌鹤,像睡著了一般亡哄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上布疙,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天蚊惯,我揣著相機與錄音,去河邊找鬼灵临。 笑死截型,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的俱诸。 我是一名探鬼主播菠劝,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼睁搭!你這毒婦竟也來了赶诊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤园骆,失蹤者是張志新(化名)和其女友劉穎舔痪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锌唾,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡锄码,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了晌涕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滋捶。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖余黎,靈堂內(nèi)的尸體忽然破棺而出重窟,到底是詐尸還是另有隱情,我是刑警寧澤惧财,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布巡扇,位于F島的核電站,受9級特大地震影響垮衷,放射性物質(zhì)發(fā)生泄漏厅翔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一搀突、第九天 我趴在偏房一處隱蔽的房頂上張望刀闷。 院中可真熱鬧,春花似錦、人聲如沸涩赢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筒扒。三九已至,卻和暖如春绊寻,著一層夾襖步出監(jiān)牢的瞬間花墩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工澄步, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冰蘑,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓村缸,卻偏偏與公主長得像祠肥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子梯皿,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容