利用databinding快速实现RecyclerView的adapter,支持多种item

阅读这篇笔记你需要了解安卓的数据绑定框架databinding
首先贴上校长看到的感觉写得最好的两篇 介绍databinding的文章:
1. CornorLin:Android Data Binding 系列(一) -- 详细介绍与使用
2. QQ音乐技术团队:Android DataBinding 数据绑定

不管作为一名安卓还是android程序猿,总是少不了一直没完没了的重复制造adapter,viewholder,就像


Paste_Image.png

等等,啊喂,这根本是同一个类呀!哦不好意思,实在太像了,搞错了,真实的情况是这样的:

Paste_Image.png

这简直可以做一个找茬游戏了,整天在这弄重复的代码,不禁要想,除了那些肮脏(滑稽)的大洋我们整天这样图的是什么。作为一名有追求的立志成为一名架构师,从来懒得多写代码的程序猿这样的情况怎么能忍!
于是不禁让人想我们写这些代码屎味的什么?

Adapter与ViewHolder的作用:
  • ViewHolder是用来通过findviewById来存放item对应的layout里边的View控件的,
  • AdapteronCreateViewHolder(ViewGroup parent, int viewType)方法负责将获取将ViewHolder取出;void onBindViewHolder(BindingHolder holder, int position)负责将实体类的内容一条一条的通过set方法显示到对应的界面上。
再看看databinding的作用:
  • 通过DatabindingUtils的public static <T extends ViewDataBinding> T inflate(LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent, boolean attachToParent)方法获取一个ViewDataBinding,包含了Layout中所有的控件;
  • boolean setVariable(int variableId, Object value)负责将数据与界面绑定自动完成类似textview.setText(item.text)这样的工作,

是不是感觉职能高度重合呢,而且databinding好像用起来更省力,那是不是可以利用ViewDataBinding 替代ViewHoldr里边的没完没了的findViewById呢,能不能用一个boolean setVariable(int variableId, Object value)来替代onBindViewHolder(BindingHolder holder, int position)中没完没了的set房呢。答案是可以的

现在假定你已经阅读过那两篇文章,对于databinding有了一定的理解。先上demo的代码地址
先看一下效果图:

Paste_Image.png

可以看出来在demo中有三种item,按照以前的惯例。我们需要三个Adapter,三个ViewHolder,三个实体Bean,三个layout文件。但是呢,让我们看一下demode代码结构

Paste_Image.png

三个实体bean,三个layout,但是只有一个Adapter,里边有一个ViewHolder。但是实现了三种item的效果好神奇吧,并且即使我想再加一种item,只需要添加一个实体Bean,再加一个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("哈哈你当不住我看见啦"));
        BindingAdapter adapter = new BindingAdapter();
        adapter.setItems(items);
        //这也是一个坑,经常忘了加LayoutManger导致东西Item无法显示,RecyclerView把测量,布局的工作甩给了LayoutManager
        LinearLayoutManager manager = new LinearLayoutManager(getApplicationContext());
        binding.rv.setLayoutManager(manager);
        binding.rv.setAdapter(adapter);
        adapter.notifyDataSetChanged();

就像平常使用RecyclerView一样,用一个List包装要显示的数据,其中TextBean,ImageBean,Image2Bean是对应三种不同布局的实体类。那么这些实体类里边一定要有一些信息能够让BindingAdapter识别他们的布局信息,最简单的方法就是在这些实体重直接返回布局文件,把他们返回布局的共同方法命名为int getViewType()并创造一个新的iterface来封装这个方法:

public interface BindingAdapterItem {
    int getViewType();
}

以后每一个Item只需要实现这个接口中的int getViewType()方法就能告诉Adapter自己的布局了。

例如TextItem的实现为:

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是为了将数据与界面绑定,详情请阅读开头的两篇文章。
再看一下TextItem的layout的内容:

<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)

方法来将数据绑定到界面上,其中int variableId指的是变量在BR类中的ID,

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

中的name,而Object value对应其中的type,在

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

中将TextItem中的text属性绑定到对对应的控件上.

  <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);
    }
    /*
    * 数据绑定
    * */
    @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   可以看作是这个hodler代表的布局的马甲,getRoot()方法会返回整个holder的最顶层的view
         * */
        public BindingHolder(ViewDataBinding binding) {
            //
            super(binding.getRoot());
            this.binding = binding;
        }

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

    }
}

这个类的基本要求是:

  • 能够根据传进来的对定的item判断对应的布局,
  • 能够自动的把传进来的数据显示到对应的布局上;

adpter获取正确的布局很简单,只需要重写int getItemViewType(int position)方法,在里边直接返回item里边的layout就行了:

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

现在先用ViewDataBinding来取代View,标准的VIewholder应该是通过layout的rootView来构造,我们可以通过ViewDataBinding.getRoot()来返回这个rootview

  ViewDataBinding binding;
         /**
         * @param binding   可以看作是这个hodler代表的布局的马甲,getRoot()方法会返回整个holder的最顶层的view
         * */
        public BindingHolder(ViewDataBinding binding) {
            //
            super(binding.getRoot());
            this.binding = binding;
        }

绑定数据的时候只需要将实例化后的实体类对象传入ViewDataBinding的对应的virable中就好了:

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

因为这里的int variableId是固定的BR.item所以每一个layout中variable的name属性必须为item!
在Adapter的onCreateViewHolder(ViewGroup parent, int viewType)中改用获取对应的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)中调用就好了:

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

推荐阅读更多精彩内容