利用databinding快速實(shí)現(xiàn)RecyclerView的adapter支子,支持多種item

閱讀這篇筆記你需要了解安卓的數(shù)據(jù)綁定框架databinding
首先貼上校長看到的感覺寫得最好的兩篇 介紹databinding的文章:
1. CornorLin:Android Data Binding 系列(一) -- 詳細(xì)介紹與使用
2. QQ音樂技術(shù)團(tuán)隊(duì):Android DataBinding 數(shù)據(jù)綁定

不管作為一名安卓還是android程序猿昨登,總是少不了一直沒完沒了的重復(fù)制造adapter禽捆,viewholder,就像


Paste_Image.png

等等,啊喂,這根本是同一個(gè)類呀!哦不好意思代虾,實(shí)在太像了学辱,搞錯(cuò)了,真實(shí)的情況是這樣的:

Paste_Image.png

這簡直可以做一個(gè)找茬游戲了,整天在這弄重復(fù)的代碼,不禁要想妆偏,除了那些骯臟(滑稽)的大洋我們整天這樣圖的是什么挪鹏。作為一名有追求的立志成為一名架構(gòu)師返顺,從來懶得多寫代碼的程序猿這樣的情況怎么能忍!
于是不禁讓人想我們寫這些代碼屎味的什么?

Adapter與ViewHolder的作用:
  • ViewHolder是用來通過findviewById來存放item對(duì)應(yīng)的layout里邊的View控件的,
  • AdapteronCreateViewHolder(ViewGroup parent, int viewType)方法負(fù)責(zé)將獲取將ViewHolder取出;void onBindViewHolder(BindingHolder holder, int position)負(fù)責(zé)將實(shí)體類的內(nèi)容一條一條的通過set方法顯示到對(duì)應(yīng)的界面上。
再看看databinding的作用:
  • 通過DatabindingUtils的public static <T extends ViewDataBinding> T inflate(LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent, boolean attachToParent)方法獲取一個(gè)ViewDataBinding,包含了Layout中所有的控件;
  • boolean setVariable(int variableId, Object value)負(fù)責(zé)將數(shù)據(jù)與界面綁定自動(dòng)完成類似textview.setText(item.text)這樣的工作妙色,

是不是感覺職能高度重合呢煌珊,而且databinding好像用起來更省力,那是不是可以利用ViewDataBinding 替代ViewHoldr里邊的沒完沒了的findViewById呢,能不能用一個(gè)boolean setVariable(int variableId, Object value)來替代onBindViewHolder(BindingHolder holder, int position)中沒完沒了的set房呢。答案是可以的

現(xiàn)在假定你已經(jīng)閱讀過那兩篇文章,對(duì)于databinding有了一定的理解溢吻。先上demo的代碼地址
先看一下效果圖:

Paste_Image.png

可以看出來在demo中有三種item且蓬,按照以前的慣例。我們需要三個(gè)Adapter焦匈,三個(gè)ViewHolder,三個(gè)實(shí)體Bean命辖,三個(gè)layout文件。但是呢,讓我們看一下demode代碼結(jié)構(gòu)

Paste_Image.png

三個(gè)實(shí)體bean昧辽,三個(gè)layout框咙,但是只有一個(gè)Adapter者铜,里邊有一個(gè)ViewHolder如蚜。但是實(shí)現(xiàn)了三種item的效果好神奇吧兴猩,并且即使我想再加一種item,只需要添加一個(gè)實(shí)體Bean,再加一個(gè)layout文件就好了不用去寫什么ViewHolder跟Adapter了茂契,哈哈神奇吧厌小。

首先看看我們是怎么用的吧:

 List<BindingAdapterItem> items = new ArrayList<>();
        items.add(new TextBean("哈哈哈哈"));
        items.add(new ImageBean());
        items.add(new Image2Bean());
        items.add(new Image2Bean());
        items.add(new TextBean("我又來啦"));
        items.add(new Image2Bean());
        items.add(new ImageBean());
        items.add(new TextBean("我還來"));
        items.add(new TextBean("就是不讓你看美女"));
        items.add(new Image2Bean());
        items.add(new ImageBean());
        items.add(new TextBean("哈哈你當(dāng)不住我看見啦"));
        BindingAdapter adapter = new BindingAdapter();
        adapter.setItems(items);
        //這也是一個(gè)坑涨岁,經(jīng)常忘了加LayoutManger導(dǎo)致東西Item無法顯示规阀,RecyclerView把測量屠缭,布局的工作甩給了LayoutManager
        LinearLayoutManager manager = new LinearLayoutManager(getApplicationContext());
        binding.rv.setLayoutManager(manager);
        binding.rv.setAdapter(adapter);
        adapter.notifyDataSetChanged();

就像平常使用RecyclerView一樣之剧,用一個(gè)List包裝要顯示的數(shù)據(jù)蟹肘,其中TextBean阳欲,ImageBean,Image2Bean是對(duì)應(yīng)三種不同布局的實(shí)體類。那么這些實(shí)體類里邊一定要有一些信息能夠讓BindingAdapter識(shí)別他們的布局信息钾军,最簡單的方法就是在這些實(shí)體重直接返回布局文件阅束,把他們返回布局的共同方法命名為int getViewType()并創(chuàng)造一個(gè)新的iterface來封裝這個(gè)方法:

public interface BindingAdapterItem {
    int getViewType();
}

以后每一個(gè)Item只需要實(shí)現(xiàn)這個(gè)接口中的int getViewType()方法就能告訴Adapter自己的布局了呼胚。

例如TextItem的實(shí)現(xiàn)為:

public class TextBean extends BaseObservable implements BindingAdapterItem {
    @Override
    public int getViewType() {
        return R.layout.adapter_text;
    }

    public TextBean(String text) {
        this.text = text;
    }

    private String text;

    @Bindable
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
        notifyPropertyChanged(BR.text);
    }
}


繼承BaseObservalable是為了將數(shù)據(jù)與界面綁定,詳情請(qǐng)閱讀開頭的兩篇文章息裸。
再看一下TextItem的layout的內(nèi)容:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="item"
            type="com.example.m.bean.TextBean"/>
    </data>
    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{item.text}"
            android:gravity="center"
            android:textSize="25sp"
            />
</layout>

剛剛講過ViewDataBing通過

boolean setVariable(int variableId, Object value)

方法來將數(shù)據(jù)綁定到界面上,其中int variableId指的是變量在BR類中的ID,

 <data>
        <variable
            name="item"
            type="com.example.m.bean.TextBean"/>
    </data>

中的name,而Object value對(duì)應(yīng)其中的type,在

 android:text="@{item.text}"

中將TextItem中的text屬性綁定到對(duì)對(duì)應(yīng)的控件上.

  <data>
        <variable
            name="item"
            type="com.example.m.bean.TextBean"/>
    </data>

好的下面去往通用的BindingAdapter去看看

public class BindingAdapter extends RecyclerView.Adapter<BindingAdapter.BindingHolder> {


    public List<BindingAdapterItem> getItems() {
        return items;
    }

    public void setItems(List<BindingAdapterItem> items) {
        this.items = items;
    }
    List<BindingAdapterItem> items = new ArrayList<>();
    
    /**
     * @return 返回的是adapter的view
     */
    @Override
    public BindingHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), viewType, parent, false);
        return new BindingHolder(binding);
    }
    /*
    * 數(shù)據(jù)綁定
    * */
    @Override
    public void onBindViewHolder(BindingHolder holder, int position) {
        holder.bindData(items.get(position));
    }
    @Override
    public int getItemCount() {
        return items.size();
    }
    @Override
    public int getItemViewType(int position) {
        return items.get(position).getViewType();
    }

    static class BindingHolder extends RecyclerView.ViewHolder {

        ViewDataBinding binding;
         /**
         * @param binding   可以看作是這個(gè)hodler代表的布局的馬甲蝇更,getRoot()方法會(huì)返回整個(gè)holder的最頂層的view
         * */
        public BindingHolder(ViewDataBinding binding) {
            //
            super(binding.getRoot());
            this.binding = binding;
        }

        public void bindData(BindingAdapterItem item) {
            binding.setVariable(BR.item,item);
        }

    }
}

這個(gè)類的基本要求是:

  • 能夠根據(jù)傳進(jìn)來的對(duì)定的item判斷對(duì)應(yīng)的布局,
  • 能夠自動(dòng)的把傳進(jìn)來的數(shù)據(jù)顯示到對(duì)應(yīng)的布局上沪编;

adpter獲取正確的布局很簡單,只需要重寫int getItemViewType(int position)方法年扩,在里邊直接返回item里邊的layout就行了:

 @Override
    public int getItemViewType(int position) {
        return items.get(position).getViewType();
    }

現(xiàn)在先用ViewDataBinding來取代View蚁廓,標(biāo)準(zhǔn)的VIewholder應(yīng)該是通過layout的rootView來構(gòu)造,我們可以通過ViewDataBinding.getRoot()來返回這個(gè)rootview

  ViewDataBinding binding;
         /**
         * @param binding   可以看作是這個(gè)hodler代表的布局的馬甲厨幻,getRoot()方法會(huì)返回整個(gè)holder的最頂層的view
         * */
        public BindingHolder(ViewDataBinding binding) {
            //
            super(binding.getRoot());
            this.binding = binding;
        }

綁定數(shù)據(jù)的時(shí)候只需要將實(shí)例化后的實(shí)體類對(duì)象傳入ViewDataBinding的對(duì)應(yīng)的virable中就好了:

public void bindData(BindingAdapterItem item) {
            //
            binding.setVariable(BR.item,item);
        }

因?yàn)檫@里的int variableId是固定的BR.item所以每一個(gè)layout中variable的name屬性必須為item!
在Adapter的onCreateViewHolder(ViewGroup parent, int viewType)中改用獲取對(duì)應(yīng)的ViewDataBing來初始化ViewHodler:

 @Override
    public BindingHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), viewType, parent, false);
        return new BindingHolder(binding);
    }

然后在onBindViewHolder(BindingHolder holder, int position)中調(diào)用就好了:

 @Override
    public void onBindViewHolder(BindingHolder holder, int position) {
        holder.bindData(items.get(position));
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末相嵌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子况脆,更是在濱河造成了極大的恐慌饭宾,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漠另,死亡現(xiàn)場離奇詭異捏雌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)笆搓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門性湿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人满败,你說我怎么就攤上這事肤频。” “怎么了算墨?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵宵荒,是天一觀的道長。 經(jīng)常有香客問我净嘀,道長报咳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任挖藏,我火速辦了婚禮暑刃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘膜眠。我一直安慰自己岩臣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布宵膨。 她就那樣靜靜地躺著架谎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辟躏。 梳的紋絲不亂的頭發(fā)上谷扣,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音捎琐,去河邊找鬼会涎。 笑死涯曲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的在塔。 我是一名探鬼主播幻件,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛔溃!你這毒婦竟也來了绰沥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤贺待,失蹤者是張志新(化名)和其女友劉穎徽曲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體麸塞,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡秃臣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哪工。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奥此。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖雁比,靈堂內(nèi)的尸體忽然破棺而出稚虎,到底是詐尸還是另有隱情,我是刑警寧澤偎捎,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布蠢终,位于F島的核電站,受9級(jí)特大地震影響茴她,放射性物質(zhì)發(fā)生泄漏寻拂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一丈牢、第九天 我趴在偏房一處隱蔽的房頂上張望祭钉。 院中可真熱鬧,春花似錦赡麦、人聲如沸朴皆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至肮疗,卻和暖如春晶姊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伪货。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工们衙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钾怔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓蒙挑,卻偏偏與公主長得像宗侦,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子忆蚀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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