美文网首页Android开发Android开发经验谈Android开发
一个让你爱不释手的万能Adapter(Kotlin+Databi

一个让你爱不释手的万能Adapter(Kotlin+Databi

作者: 一只懂音乐的码虫 | 来源:发表于2018-10-24 22:41 被阅读200次

    感谢点进来看的各位技术小可爱,本篇文章为纯干货,希望阅读完本文的你能有所收获,帮助大家提高项目的开发效率。

    阅读本文你将收获:

    1、简洁好用的万能适配器一个
    2、DataBinding的简单使用
    3、Kotlin和DataBinding结合使用遇到的问题
    4、Github制作自己的在线Library

    废话不多说,先来直接看一下,CommonAdaper结合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
        }
    }
    
    是,你没看没错,就是这么简洁!!!省去了一大堆数据装载时的setText等冗余代码。下面是RecyclerView实现的列表效果: demo.gif

    《一》本篇简介

    关于通用的适配器,相信大家也看过不少博客,如果还有在用传统的方式写列表适配器的新手,那要赶快跟紧步伐啦,因为你可能已经落后不是一点点了哦。当然,其实即使是要自己去手写一套万能Adapter,也并不是很困难的事情,所以大家不需要畏惧,其实核心思想就是代码的封装和抽象,以及一些设计模式的运用,感兴趣的可以自己动手试试。本篇文章的基础,是鸿洋大神的BaseAdapter,支持ListView和RecyclerView的Adapter,且能支持多类型列表的适配 ,能很好的满足日常项目开发的需求。本篇文章就是在他写的BaseAdapter的基础上,进行了改造,所以下面开始介绍经我简单改造后的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>() //模拟数据
            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>() //模拟数据
            var mAdapter = ADA_ListItem(this)
            listview.adapter = mAdapter
            mAdapter.update(dataList,true)
    

    《三》对BaseAdapter的改造思路

    不了解鸿洋大神的万能适配方案的,可以点击:BaseAdapter先学习了解一下,也可以直接下载文末我改造后的案例。本篇文章着重讲解RecyclerView的Adapter的改造,完整的源码请去文末下载哦~

    我们来看看,BaseAdapter未改造前,RecyclerView的Adapter写法可能是这样的: image.png

    其实相比原始的写法还是很简单的,但是如果涉及的字段比较多,那么就会有大量的setText()等,虽然BaseAdapter已经很大程度上简化了Adapter,但是我们还是每次都要写很多重复的简单代码。

    分析:归根结底,其实写Adapter无非就是下面几个要素
    (1)写一个item布局文件
    (2)告诉Adapter每个item对应的bean是什么
    (3)绑定数据:给item布局中的控件设置对应的数据

    ①item布局,不管你如何简化,都得写上,这个毋庸置疑;
    ②可以看到未改造前的BaseAdapter,已经将第二点bean类以泛型的形式抽离出来了;
    ③那么我们看看第三点,是不是可以对它做点什么。借鉴着把bean类抽离一个泛型的思想,结合DataBinding,每个item布局文件会对应一个ViewDataBinding,所以我把ViewDataBinding抽离出一个泛型出来。

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


    image.png

    2、在ViewHolder中使用DataBinding绑定布局:


    image.png

    3、在onBindViewHolder中把布局对应的某一个具体的ViewBinding传出去,供数据更新时给控件设置数据使用:


    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
        }
    }
    
    总结:

    通过简单的改造,我们写Adapter时,就只需要给Adapter一个布局、一个具体的bean、一个布局文件对应的具体的ViewDataBinding,然后在布局文件中使用DataBinding把数据绑定写好,一个Adapter的工作就完成了。

    结合了DataBinding后,我们将数据与页面的绑定,在写布局的时候就把页面控件对应的数据绑定了,这样省去了大量的BindView操作和对view设置数据的处理。

    《四》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 , 这样我们可以通过声明式布局以精简的代码来绑定应用程序逻辑和布局
        dataBinding{
            enabled = true
        }
    

    (3)XML布局中做声明数据绑定

    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节点下一个variable节点代表一个变量,
        name属性根据需要自己取名,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)定义数据绑定的Data对象:

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

    (4)使用DataBindingUtil,绑定布局与数据。

    Android studio会根据layout文件自动生成一个默认的Binding类,类名是根据layout文件名生成的,并有"Binding"后缀结束。

    情景1:在Activity中

    MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
    User user = new User("Test", "User");
    binding.setUser(user); //数据更新,设置给绑定的控件,此时即完成了页面的数据刷新
    

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

    关于DataBinding,更多详细的介绍及一些高级用法,我就不在本文多加赘述了

    《五》遇到的问题及解决

    (1)如果不熟悉DataBinding的朋友可能会有疑问。如果是加载图片或者是某个控件绑定的数据展示需要特殊处理咋办?这就是DataBinding的知识了,这里我简单说一下这种情况的处理方法。

    DataBinding有个BindingAdapter,它的功能是用来设置view的属性值。

    假设你要在布局中显示一个圆角图片,咋办?你可以新建一个类,叫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节点下一个variable节点代表一个变量,
        name属性根据需要自己取名,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里创建多个@BindingAdapter注解的方法,来特殊处理你在布局文件要给控件绑定的数据值。

    (2)Kotlin下使用DataBinding遇到的问题
    dataBinding+kotlin环境下会报错:Error: Unresolved reference: databinding
    解决:在app的build.gradle下添加,sync now即可恢复正常。
    我的app的Android plugin版本为 classpath 'com.android.tools.build:gradle:3.0.1'

    
    dependencies {
      
        kapt 'com.android.databinding:compiler:3.0.1'
     
    }
    kapt {
        generateStubs = true
    }
    
    写在结尾:

    对本文有问题的朋友欢迎大家留言交流哦~

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

    相关文章

      网友评论

      本文标题:一个让你爱不释手的万能Adapter(Kotlin+Databi

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