美文网首页
DataBinding入门介绍

DataBinding入门介绍

作者: 若无初见 | 来源:发表于2020-01-20 10:40 被阅读0次

    本文将介绍以下databinding的基本使用方式,主要包括数据的绑定,事件的监听,自定义监听方法等

    介绍

    DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现,用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。MVVM 相对于 MVP,其实就是将 Presenter 层替换成了 ViewModel 层。DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常

    一、使用

    1、在 app module 的 build.gradle 中添加如下代码:

    android {
        dataBinding {
            enabled = true
        }
    }
    

    2、xml布局中生成 DataBinding 需要的布局规则
    在布局的根目录快捷键Alt+Enter,点击 “Convert to data binding layout”即可生成如下符合规则的代码

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
    
        </data>
    
        <LinearLayout
            android:layout_margin="20dp"
            android:gravity="center_horizontal"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:layout_width="wrap_content"
                android:id="@+id/button"
                android:text="点击"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </layout>
    

    我们知道使用当前的button控件,通常在activity中都是通过findViewById的方式,这样代码就会显得特别的臃肿。
    databingding和kotlin一样,最大的不一样就是可以直接通过id找到控件。
    上面的例子我们已经生成了一个符合DataBinding规则的一个布局文件,那么此时系统就会默认帮我们生成了一个viewBinding的类。

    public class TestActivity extends AppCompatActivity {
        private AcitivityTestBinding mBinding;
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            int layoutId = R.layout.acitivity_test;
            mBinding = DataBindingUtil.setContentView(this, layoutId);
            mBinding.button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    
                }
            });
    
        }
    }
    
    

    可以看到我们定义了一个Binding,他的命名规则是 布局文件名字+Binding 。例如activity_test.xml对应的就是ActivityTestBinding 。 可以看到使用控件直接调用mBinding.button

    事件监听

    我们知道事件监听包括点击、长按等,在activity中代码通常需要写上一大段的回调,而DataBinding却为我们提供了很好的方式,网上有很多文章关于事件监听的,但是经过测试很多都会出错或者不能用。下面我们好好总结下。
    首先定义的方法和事件匿名内部类中定义的返回类型必须相同,比如点击长按事件

     mBinding.button.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    return false;
                }
            });
    

    对于DataBinding的事件监听的写法我们可以写一个内部类,然后定义类中的方法。针对上面的长按事件首先根据规则定义方法

     public class OnEventListener{
            public boolean onLongClick(){
                Log.d(TAG, "onLongClick: ");
                return true;
            }
        }
    

    对应的xml布局文件中的定义

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
    
            <variable
                name="listener"
                type="com.yaxon.yxproj.bean.TestActivity.OnEventListener" />
        </data>
    
        <LinearLayout
            android:layout_margin="20dp"
            android:gravity="center_horizontal"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:layout_width="wrap_content"
                android:id="@+id/button"
                android:text="点击"
                android:onLongClick="@{()->listener.onLongClick()}"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </layout>
    

    注意必须引入OnEventListener。
    事件监听有了,接着记得绑定上内部类。因为我们在布局中生命了listener。所以databinding就会自动生成一个设置的方法

      mBinding.setListener(new OnEventListener());
    

    这句话就相当于原始绑定监听事件的代码,不同的是它可以同时把所有内部类中定义的方法都绑定,比起源生代码又减少了一部分。
    那么有人会问,onLongClick(View view)参数列表中的view呢? 如果我需要使用呢?或者我想另外添加一个参数又要怎么定义呢?
    其实事件绑定本人总结了三种的定义的方式:第一种不带任何参数(参考以上写法)、第二种带上原始的参数、第三种附加自定义参数

    对于第一种写法请参考以上代码
    第二种带原始参数:

    public class OnEventListener{
            public boolean onLongClick(View view){
                Log.d(TAG, "onLongClick: ");
                return true;
            }
        }
    

    对应的xml布局

     <Button
                android:layout_width="wrap_content"
                android:id="@+id/button"
                android:text="点击"
                android:onLongClick="@{listener::onLongClick}"
                android:layout_height="wrap_content"/>
    

    第三种写法带上自定义参数:
    分为两种:
    1、直接添加额外的参数

      public class OnEventListener {
            public boolean onLongClick(String url) {
                Log.d(TAG, "onLongClick: "+url);
                return true;
            }
        }
    

    对应的xml布局

     <Button
                android:layout_width="wrap_content"
                android:id="@+id/button"
                android:text="点击"
                android:onLongClick="@{()->listener.onLongClick(url)}"
                android:layout_height="wrap_content"/>
    

    2、添加原始参数+自定义参数

      public class OnEventListener {
            public boolean onLongClick(View view,String url) {
                Log.d(TAG, "onLongClick: "+view.getId()+url);
                return true;
            }
        }
    

    对应的xml布局

    
            <Button
                android:layout_width="wrap_content"
                android:id="@+id/button"
                android:text="点击"
                android:onLongClick="@{(view)->listener.onLongClick(view,url)}"
                android:layout_height="wrap_content"/>
    

    其他的事件监听只要遵循以上的规则即可,例如checkbox的事件监听

      public void onCheck(CompoundButton buttonView, boolean isChecked) {
                Log.d(TAG, "onCheck: " + isChecked);
            }
    
    <CheckBox
                android:id="@+id/checkBox"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="73dp"
                android:layout_marginLeft="73dp"
                android:layout_marginTop="31dp"
                android:text="CheckBox"
                android:onCheckedChanged="@{click::onCheck}"
                app:layout_constraintStart_toEndOf="@+id/imageView"
                app:layout_constraintTop_toBottomOf="@+id/button4" />
    
    当然写法可能有很多种例如网上还出现其他的写法可以运行,但是以上写法基本满足我们日常的开发。

    数据绑定

    通常情况下想要设置一个TextView的值需要写类似的代码 tv.setText("content");
    DataBinding提供了数据和布局绑定的功能。例如我们可以申明一个ViewModel,以LoginViewModel作为例子

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
    
            <variable
                name="model"
                type="com.yaxon.yxproj.model.LoginViewModel" />
            <variable
                name="listener"
                type="com.yaxon.yxproj.bean.TestActivity.OnEventListener" />
        </data>
    
        <LinearLayout
            android:layout_margin="20dp"
            android:gravity="center_horizontal"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:layout_width="wrap_content"
                android:id="@+id/button"
                android:text="登录"
                android:onLongClick="@{()->listener.onLoginClick()}"
                android:layout_height="wrap_content"/>
    
            <TextView
                android:layout_width="wrap_content"
                android:text="@={model.respondData}"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </layout>
    

    可以看到 TextView的值已经绑定了model.respondData,一旦定义了一个变量相应的DataBinding就可以设置属性,例如我们设置了listener,DataBinding就有了setListener,定义了一个model它就有了setModel的方法。

    public class TestActivity extends AppCompatActivity {
        private static final String TAG = TestActivity.class.getSimpleName();
    
        private AcitivityTestBinding mBinding;
        private LoginViewModel loginViewModel ;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            int layoutId = R.layout.acitivity_test;
            mBinding = DataBindingUtil.setContentView(this, layoutId);
            mBinding.setListener(new OnEventListener());
            //这句话不设置则无法监听
            mBinding.setLifecycleOwner(this);
            loginViewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
            mBinding.setModel(loginViewModel);
    
        }
    
    
        public class OnEventListener {
            public boolean onLongClick(View view,String url) {
                Log.d(TAG, "onLongClick: "+view.getId()+url);
                return true;
            }
    
            public void onLoginClick(){
                loginViewModel.doLogin("xxx","xxx");
            }
        }
    }
    

    一旦TextView绑定了model中respondData的值,那么TextView就会和该值绑定双向绑定,一方改变另一方必然改变。
    当调用doLogin接口,respondData的值会被设置那么TextView显示的值也会被改变。

    //LoginViewModel.class (doLogin)
    
     public void doLogin(final String userName, final String paw) {
            Observable<LoginRespondBean> observable = Observable.create(
                    new ObservableOnSubscribe<LoginRespondBean>() {
                @Override
                public void subscribe(ObservableEmitter<LoginRespondBean> emitter) {
                    Log.d("TAG", "subscribe:" + Thread.currentThread().getName());
                    LoginRespondBean login = ControlApi.getInstance().login(userName, paw);
                    emitter.onNext(login);
                }
            });
            //切换到io异步线程执行耗时操作,并将结果返回到主线程
            observable.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<LoginRespondBean>() {
                        @Override
                        public void accept(LoginRespondBean loginRespondBean) {
                            Log.d("TAG", "accept:" + Thread.currentThread().getName());
                            respondData.setValue(loginRespondBean);
                        }
                    });
    
        }
    

    这里我们使用了LiveData配合DataBinding的使用。完整的代码如下

    public class LoginViewModel extends ViewModel {
        public MutableLiveData<LoginRespondBean> respondData = new MutableLiveData<>();
        public MutableLiveData<String> registerData = new MutableLiveData<>();
    
        /**
         * 需要关心结果
         *
         * @param userName
         * @param paw
         */
    
        public void doLogin(final String userName, final String paw) {
            Observable<LoginRespondBean> observable = Observable.create(
                    new ObservableOnSubscribe<LoginRespondBean>() {
                @Override
                public void subscribe(ObservableEmitter<LoginRespondBean> emitter) {
                    Log.d("TAG", "subscribe:" + Thread.currentThread().getName());
                    LoginRespondBean login = ControlApi.getInstance().login(userName, paw);
                    emitter.onNext(login);
                }
            });
            //切换到io异步线程执行耗时操作,并将结果返回到主线程
            observable.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<LoginRespondBean>() {
                        @Override
                        public void accept(LoginRespondBean loginRespondBean) {
                            Log.d("TAG", "accept:" + Thread.currentThread().getName());
                            respondData.setValue(loginRespondBean);
                        }
                    });
    
        }
    }
    

    目前支持双向绑定的参数列表如下:


    自定义参数绑定:BindingAdapter

    最常见的例子就是一个ImageView需要设置图片
    一般的是写法是 获取到图片的url后借助Glide设置编写java代码。 同样的DataBinding也提供了一种自定义参数绑定的方式BindingAdapter
    同样在ViewModel中定义一个方法:

    public class DriverViewModel extends ViewModel {
        @BindingAdapter({"imageUrl"})
        public static void loadImage(ImageView imageView, String url){
            Glide.with(imageView.getContext()).load(url)
                    .placeholder(R.mipmap.ic_launcher)
                    .into(imageView);
        }
    
    }
    

    "imageUrl"表示xml控件中设置的属性名
    参数列表中第一个必须是View或者子View的类型,第二个参数是自定义参数。

       <ImageView
                android:id="@+id/imageView"
                imageUrl="@{url}"
                android:layout_width="164dp"
                android:layout_height="156dp"
                android:layout_marginStart="53dp"
                android:layout_marginLeft="53dp"
                android:layout_marginTop="38dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:srcCompat="@tools:sample/avatars" />
    

    布局中设置了一个url,属于String类型。不管引入什么对象类型的变量都需要申明,

      <data>
    
            <variable
                name="url"
                type="String" />
    
            <variable
                name="model"
                type="com.yaxon.yxproj.model.DriverViewModel" />
    
            <variable
                name="click"
                type="com.yaxon.yxproj.DriverInfoActivity.EventListener" />
        </data>
    
    

    同样的道理我们的DataBinding就多了一个setUrl的方法,只要将正常的url地址设置进去就可以完成图片加载了。
    未完待续...

    相关文章

      网友评论

          本文标题:DataBinding入门介绍

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