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

感謝點進(jìn)來看的各位技術(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
    }
}

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

《一》本篇簡介

關(guān)于通用的適配器,相信大家也看過不少博客,如果還有在用傳統(tǒng)的方式寫列表適配器的新手,那要趕快跟緊步伐啦,因為你可能已經(jīng)落后不是一點點了哦。當(dāng)然,其實即使是要自己去手寫一套萬能Adapter,也并不是很困難的事情,所以大家不需要畏懼,其實核心思想就是代碼的封裝和抽象,以及一些設(shè)計模式的運用,感興趣的可以自己動手試試。本篇文章的基礎(chǔ),是鴻洋大神的BaseAdapter,支持ListView和RecyclerView的Adapter,且能支持多類型列表的適配 ,能很好的滿足日常項目開發(fā)的需求。本篇文章就是在他寫的BaseAdapter的基礎(chǔ)上,進(jìn)行了改造,所以下面開始介紹經(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先學(xué)習(xí)了解一下,也可以直接下載文末我改造后的案例。本篇文章著重講解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的全路徑,
    功能相當(dāng)于寫代碼的時候引入一個類的功能-->
    <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文件自動生成一個默認(rèn)的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,更多詳細(xì)的介紹及一些高級用法,我就不在本文多加贅述了

《五》遇到的問題及解決

(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的全路徑,
    功能相當(dāng)于寫代碼的時候引入一個類的功能-->
    <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項目算是傾注了我蠻多心血了,每個頁面和功能都當(dāng)成是上線的App來做,App的logo還特地做了UI設(shè)計??力求做到精致和完善,其中還包括了很多自己項目開發(fā)中的經(jīng)驗匯總和對新技術(shù)的探索和整合,希望對各位讀者有所幫助,歡迎點個star,follow,或者給個小心心,嘻嘻??也可以分享給你更多的朋友一起學(xué)習(xí),您的支持是我不斷前進(jìn)的動力。如果有任何問題,歡迎在GitHub上給我提issue或者留言。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,627評論 2 380

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