Android中Databinding+MVVM架构使用心得(一)

Android中Databinding+MVVM架构使用心得(一)

Android小彩虹2021-07-15 7:01:55130A+A-

重构公司一个项目时候使用了MVVM + databinding来当主体架构,这种架构更加偏向于前端的风格,比以往的MVP更加轻量和聪明,也跟flutter的思想有所契合,关键就在于把数据于UI绑定了起来,由数据驱动UI改变,少了很多get() set()方法,实现了“所见即所得”

首先在Android的gradle中配置databindding开启,因为databinding是谷歌的官方库,所以加这一行自动集成了相关代码:

    dataBinding {
        enabled true
    }

一.Binding类生成及使用

在项目的activity中初始化 注入绑定,这里有几种不同的方法,根据项目需要使用

  • 1.修改BaseActivity类,适合新建的项目,用 DataBindingUtil.setContentView

这个ActivityDemoBinding类是由databinding根据xml文件自动生成的,跟butterknife一样的原理

activity_demo.xml :

  • 2.直接在Activity实现类绑定xml,还是上图的那个activity,这次不改动原有的项目架构,在demoactivity中绑定xml,适合在原有的项目基础上修改

其实DataBindingUtil.setContentView 方法最后也是调用的bind()方法去绑定布局,只是在前一步先调用了activity的setcontentView

DataBindingUtil.setContentView :

二.构建ViewModel

然后在具体的activity中生成对应的viewmodel对象

@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.bind(getContainerView());
    mBinding.setModel(new DemoViewModel());
}

在viewModel中声明几个数据:

public class DemoViewModel {
    public ObservableField<String> demoString = new ObservableField<>("");
    public ObservableBoolean demoBoolean = new ObservableBoolean();
    public ObservableInt demoNum = new ObservableInt();

    public void onButtonClick(View v){
        //TODO    
    }
}

其中ObservableInt 这种是databinding中的类,封装了一些基本数据类型,也可以自定义封装自己的类,主要实现了监听回调接口,使用观察者模式,在数据变化的时候通知绑定的View刷新UI

android获取View一般是用butterKnife或者更原始一点要用到findviewById(),这类方法非常冗余,比如好用的butterKnife,必须在acticity先声明View对象,View类型,View的id,才能去设置文字或图片之类的东西,而使用databinding则可以直接获取xml布局的映射类,通过binding对象,在Activity中不用声明,直接拿来用,甚至一些简单的操作,如设置文字,图片,点击事件根本不需要activity介入,直接在xml中绑定了具体数据或方法。

使用方法,xml中:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="model"
            type="com.XXX.DemoViewModel" />
        <import type="android.view.View"/>
    </data>
    <FrameLayout
        android:id="@+id/container_demo"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/tv_demo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{model.demoString}"
            android:visibility="@{model.demoBoolean? View.VISIBLE : View.GONE}"
            />
        <Button
            android:id="@+id/bt_demo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{model::onButtonClick}"
            />
    </FrameLayout>
</layout>

在根布局中加入layout标签,加入data标签,type是上面baseActivity创建的viewModel

最基础的textview显示:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#FF4A4A4A"
        android:textSize="18sp"
        android:drawablePadding="10dp"
        android:text="@{model.demoString,default= 123}"
/>

editext

                <EditText
                    android:layout_width="match_parent"
                    android:layout_weight="1"
                    ...
                    android:text="@={viewModel.phoneNo}"
                    binding:textChanged="@{viewModel.textChangeCommand}"
                />

两者的区别在于"@{}"和"@={}"的区别,前者单向绑定,数据变化会通知UI变化,而后者是双向绑定,UI变化也会通知数据的变化,典型的就是Edittext输入框

如果使用原始的方式,如findViewById或者Butteknife 需要在activity一开始声明一个view对象,需要用到View的id及类型,并且点击事件需要一个一个地去添加,否则点击不会起作用,获取界面数据的话需要在输入框使用edittext.getText().toString()才能获取到用户输入数据,非常繁琐,任何一步出错,就会导致bug的出现,影响后面所有操作,而使用databinding,不需要在activity中声明View对象,不需要关注View的类型,不需要关注View的id,只需要在xml中把数据绑定到View上就可以,并且由于bindingadapter的存在,还能做一些中间操作,如防止连续快速点击,加载网络图片等,把很多步骤都简化为一步实现

三.BindingAdapter

像平常的开发中,我们不止使用View的基本属性,在自定义View中,ImageView加载网络图片这些,还是需要获取到View对象在Java或kotlin代码中去实现一些操作,那有没有方式用databinding办到呢,这里就要说到BindingAdapter了,听名字就知道,是View的适配器,采用AOP的模式,在编译时生成代码,修改binding类中用了dadpter的View

比如一个Imageview设置头像,普通方法是获取imageview对象,然后去用glide或其他图片请求框架加载Url,用dababinding则是这样子:

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    binding:url="@{model.imgUrl}"
    binding:placeholderRes="@drawable/bg1"
/>

binding:url 指定图片地址

binding:placeholderRes 占位图片

这是怎么做到的呢,这里就要引入bindingAdapter

    @BindingAdapter(value = {"url", "placeholderRes"}, requireAll = false)
    public static void setImageUri(ImageView imageView, String url, int placeholderRes) {
        if (!TextUtils.isEmpty(url)) {
            //使用Glide框架加载图片
            Glide.with(imageView.getContext())
                    .load(url)
                    .apply(new RequestOptions().placeholder(placeholderRes))
                    .into(imageView);
        }
    }

这里使用@bindingAdapter注解,value是url和placeholder,requireAll是否需要全部参数,然后这里的第一个参数ImageView就指定了view的类型,然后再使用Glide请求网络图片,placeholder设置占位图片,配置一个地方的代码,在所有的imageView都能够使用.

还有很多常用的方法,比如控件的圆角,根据不同type显示不同图片,按下改变背景,列表的item动画,列表的分隔符...等等都能用Adapter来完成,大大减少了Activity中的代码量,并且能够统一格式问题,改一处,处处生效

需要注意的是,bindingadapter在项目中任何一处写过就会生效,因为是用AOP模式,在编译时会检查代码,不用重复写,当bindingadapter和xml中原有属性冲突时,bindingdadpter中设置会覆盖原有属性

使用心得

优点:

代码量大大减少

bindingadapter统一

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们