美文网首页安卓开发学习DataBindingAndroid DataBinding
利用databinding快速实现RecyclerView的ad

利用databinding快速实现RecyclerView的ad

作者: 苗校长 | 来源:发表于2017-05-07 14:31 被阅读3407次

    阅读这篇笔记你需要了解安卓的数据绑定框架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));
        }
    

    相关文章

      网友评论

      • 3054a1ca1302:如何实现item中子控件的点击事件呢
      • 兀兀沙弥:那如何实现item点击???我在者遇到麻烦啦...
      • 唠嗑008:确实不错,但是如果想在绑定数据的时候,获取item布局的某个View做些复杂操作的话,这个封装还是不行的
      • A王程:我想问一下,使用viewpager进行图片轮播时,如何向adapter中add DatabindingView
      • 逸_9258:现在想使用onclick事件得到 item.add 添加进去的对应项的数据,如何才能做到? 还是要重新写adapter?
        逸_9258:@苗校长 嗯 其实我用了一个方法实现了得到数据的功能 就是在item.add的同时使用另一个数组把数据记下来,然后根据onclick方法里面的到position得到对应数据,就是感觉有点蠢:mask: 自己造车感觉水平还不行,学安卓还不到两月
        苗校长:@逸_9258 这个其实只是简单的demo,只是用来论证databinding可以用来快速实现adapter的,并没有作封装,你可以尝试一下做一个能够有更多功能的封装,至于拿到item,一般可以将click事件写在item内部就可以,如果非要在外部比如activity中响应,我暂时没找到什么好办法,估计得写一个新的adapter,就暂时用这个实现简单的布局好了,你也可以在git上着一些其他开源库参考一下
      • 1d66a3c50b68:如果要使用上下文呢?是不是new 对象的时候通过构造把activity传递进去
        1d66a3c50b68:@苗校长 大神我还有个问题想问!怎么给 toobar 绑定 menu ? 切换写的代码很多不优雅
        苗校长:@FTD彤 都可以,一般除非是必须用到activity,我都是直接在自定义的application中用静态方法返回全局的context
        1d66a3c50b68:还是点击的时候拿到view的content
      • 0f1d692653ae:那如果布局里面的checkbox的点击事件应该设置在哪里呢?TextBean这个对象里面吗?而且又该如何避免复用呢
        dab87f544b48:@苗校长 是否可以给个Demo?
        苗校长:谷歌官方的是TextBean就是一个普通的pojo类,然后在viewModel里边对textBean的每一个属性进行进行订阅,然后所有的逻辑都写在viewModel里边.我是因为找到的自动生成binding 类的只支持TextBean这种写法,采用的这个结构...

        https://github.com/googlesamples/android-architecture 这个是google的demo,有mvp还有mvvm的示范写法,你可以看一下他们的写法..

        至于避免复用没搞清什么意思...
        苗校长:理论上是可以直接写在TextBean这个对像里头.对于需要处理复杂逻辑的adapter,我一般是再创建一个viewModel,持有这个Bean,将实践写在这个viewModel里边...
      • rome753:github项目xml中TextBean和ImageBean名字都错了,push前至少运行一下吧。。
        苗校长:额,谢谢提醒,已经改过了,之前是item,后来改成Bean了,估计layout文件没提交成功,实在是不好意思.....
      • zxamy:牛逼了 哈哈

      本文标题:利用databinding快速实现RecyclerView的ad

      本文链接:https://www.haomeiwen.com/subject/hspqtxtx.html