美文网首页Jetpack组件
JetPack--DataBinding

JetPack--DataBinding

作者: aruba | 来源:发表于2021-09-11 16:08 被阅读0次

    MVP和MVVM中,ViewModel与Presenter很类似,只不过ViewModel和View层多了双向绑定,当ViewModel中数据更改,View层能够知道,反之,View层数据改变,ViewModel也能够改变。ViewModel中LiveData可以被观测,进而在Activity中观测到值发生变化来更新ui,结合DataBinding,我们可以直接在xml中设置ViewModel,进一步简化逻辑,使我们的MVVM构架更加低耦合

    一、初识DataBinding

    DataBinding是一个可以将xml转换为对象的工具,我们知道对象的属性是可以赋值的,所以我们可以直接只用变量来赋值xml上的属性,使得它更据灵活性
    1.在xml中使用实体类

    在gradle中添加DataBinding支持

    defaultConfig{
    ...
            dataBinding {
                enabled = true
            }
    }
    

    定义一个实体类,我们将把它放入布局文件

    package com.aruba.databinding;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class Idol {
        public String name;
        public String star;
    
        public Idol(String name, String star) {
            this.name = name;
            this.star = star;
        }
    }
    
    

    在将xml转换后,在xml中定义实体类对象

    <?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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">
    
            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="104dp"
                android:text="点赞"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.498"
                app:layout_constraintStart_toStartOf="parent"
                tools:text="appear" />
    
            <ImageView
                android:id="@+id/imageView"
                android:layout_width="200dp"
                android:layout_height="200dp"
                android:src="@drawable/sijiali"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.498"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.248"
                tools:srcCompat="@tools:sample/avatars" />
    
            <TextView
                android:id="@+id/textView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{idol.name}"
                android:textSize="30sp"
                app:layout_constraintBottom_toTopOf="@+id/button"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/imageView"
                app:layout_constraintVertical_bias="0.232"
                tools:text="name" />
    
            <TextView
                android:id="@+id/textView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{idol.star}"
                android:textSize="24sp"
                app:layout_constraintBottom_toTopOf="@+id/button"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView2"
                app:layout_constraintVertical_bias="0.244"
                tools:text="Star" />
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
        <data>
    
            <variable
                name="idol"
                type="com.aruba.databinding.Idol" />
    
        </data>
    </layout>
    

    Activity中使用DataBinding将布局转换成对象

    package com.aruba.databinding;
    
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.databinding.DataBindingUtil;
    import androidx.databinding.ViewDataBinding;
    
    import android.os.Bundle;
    
    import com.aruba.databinding.databinding.ActivityMainBinding;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            activityMainBinding.setIdol(new Idol("斯嘉丽·约翰逊", "五星"));
        }
    }
    

    效果:


    2.xml中调用函数

    如果我们的实体类中star属性为int型,有一个静态全局函数根据传入的star返回一个字符串,那么怎么调用它

    package com.aruba.databinding;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class Idol {
        public String name;
        public int star;
    
        public Idol(String name, int star) {
            this.name = name;
            this.star = star;
        }
    }
    
    
    package com.aruba.databinding;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class StarUtils {
        public static String getStar(int star) {
            String starStr = null;
            switch (star) {
                case 1:
                    starStr = "一星";
                    break;
                case 2:
                    starStr = "二星";
                    break;
                case 3:
                    starStr = "三星";
                    break;
                case 4:
                    starStr = "四星";
                    break;
                case 5:
                    starStr = "五星";
                    break;
            }
    
            return starStr;
        }
    }
    
    

    在xml中使用import标签导入工具类,在相应的地方调用工具类方法

    <?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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">
    
            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="104dp"
                android:text="点赞"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.498"
                app:layout_constraintStart_toStartOf="parent"
                tools:text="appear" />
    
            <ImageView
                android:id="@+id/imageView"
                android:layout_width="200dp"
                android:layout_height="200dp"
                android:src="@drawable/sijiali"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.498"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.248"
                tools:srcCompat="@tools:sample/avatars" />
    
            <TextView
                android:id="@+id/textView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{idol.name}"
                android:textSize="30sp"
                app:layout_constraintBottom_toTopOf="@+id/button"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/imageView"
                app:layout_constraintVertical_bias="0.232"
                tools:text="name" />
    
            <TextView
                android:id="@+id/textView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{StarUtils.getStar(idol.star)}"
                android:textSize="24sp"
                app:layout_constraintBottom_toTopOf="@+id/button"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView2"
                app:layout_constraintVertical_bias="0.244"
                tools:text="Star" />
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
        <data>
    
            <import type="com.aruba.databinding.StarUtils" />
    
            <variable
                name="idol"
                type="com.aruba.databinding.Idol" />
    
        </data>
    </layout>
    

    我们还要想要给点赞button调用一个对象的方法,定义对象如下:

    package com.aruba.databinding;
    
    import android.content.Context;
    import android.view.View;
    import android.widget.Toast;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class OnClickHandler {
        private Context context;
    
        public OnClickHandler(Context context) {
            this.context = context;
        }
    
        public void onClick(View view) {
            Toast.makeText(context, "点赞", Toast.LENGTH_SHORT).show();
        }
    }
    
    

    xml中加入,并调用函数

    
        <data>
    
            <import type="com.aruba.databinding.StarUtils" />
    
            <variable
                name="idol"
                type="com.aruba.databinding.Idol" />
    
            <variable
                name="onClickHandler"
                type="com.aruba.databinding.OnClickHandler" />
        </data>
    
    
            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="104dp"
                android:onClick="@{onClickHandler.onClick}"
                android:text="点赞"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.498"
                app:layout_constraintStart_toStartOf="parent"
                tools:text="appear" />
    

    Activty中修改传入的参数类型和点击事件对象后,效果如下:


    3.传递对象至include标签

    有时我们会使用include标签封装一些布局,那么这些布局怎么拿到主布局中定义的对象呢

    首先定义include布局,转换成DataBing布局

    <?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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingVertical="10dp">
    
            <TextView
                android:id="@+id/textView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:text="@{idol.name}"
                android:textSize="30sp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:text="name" />
    
            <TextView
                android:id="@+id/textView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="36dp"
                android:text="@{StarUtils.getStar(idol.star)}"
                android:textSize="24sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView2"
                tools:text="Star" />
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
        <data>
    
            <import type="com.aruba.databinding.StarUtils" />
            
            <variable
                name="idol"
                type="com.aruba.databinding.Idol" />
    
        </data>
    </layout>
    

    Activity的xml中引入include布局

    <?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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">
    
            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="104dp"
                android:onClick="@{onClickHandler.onClick}"
                android:text="点赞"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.498"
                app:layout_constraintStart_toStartOf="parent"
                tools:text="appear" />
    
            <ImageView
                android:id="@+id/imageView"
                android:layout_width="200dp"
                android:layout_height="200dp"
                android:src="@drawable/sijiali"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.498"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.248"
                tools:srcCompat="@tools:sample/avatars" />
    
            <include
                layout="@layout/include_layout_info"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                app:idol="@{idol}"
                app:layout_constraintBottom_toTopOf="@+id/button"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/imageView"
                app:layout_constraintVertical_bias="0.32" />
    
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
        <data>
    
            <variable
                name="idol"
                type="com.aruba.databinding.Idol" />
    
            <variable
                name="onClickHandler"
                type="com.aruba.databinding.OnClickHandler" />
        </data>
    </layout>
    

    只需要改xml就可以了,非常方便
    效果:


    4.自定义BindingAdapter

    虽然我们可以在xml调用函数,但只能针对一些简单逻辑,如果需要加载网络图片,并实现更复杂的逻辑代码,那么使用BindingAdapter是实现它们的好工具,它支持我们自定义一个属性,并实现相应的方法
    定义一个类,使用BindingAdapter注解:

    package com.aruba.databinding;
    
    import android.widget.ImageView;
    
    import androidx.databinding.Bindable;
    import androidx.databinding.BindingAdapter;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class ImageViewBindingAdapter {
        @BindingAdapter("image")
        public static void setImage(ImageView imageView, String url) {
    
        }
    }
    
    

    这样我们就可以在xml中使用image了,我们使用image传入一个变量,界面渲染后,DataBinding会自动调用上面定义的setImage方法

            <variable
                name="imageUrl"
                type="String" />
    
    
            <ImageView
                android:id="@+id/imageView"
                android:layout_width="200dp"
                android:layout_height="200dp"
                app:image="@{imageUrl}"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.498"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.248"
                tools:srcCompat="@tools:sample/avatars" />
    

    在Activity中传入这个imageUrl变量

    activityMainBinding.setImageUrl("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp3.ssl.cdn.btime.com%2Ft01ef53f09683630079.jpg%3Fsize%3D640x1137&refer=http%3A%2F%2Fp3.ssl.cdn.btime.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633920976&t=59dab0f58d59d80e00bf3ef182728a80");
    

    我们再对setImage方法进行加工处理

    package com.aruba.databinding;
    
    import android.text.TextUtils;
    import android.widget.ImageView;
    
    import androidx.databinding.Bindable;
    import androidx.databinding.BindingAdapter;
    
    import com.squareup.picasso.Picasso;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class ImageViewBindingAdapter {
        @BindingAdapter("image")
        public static void setImage(ImageView imageView, String url) {
            if (!TextUtils.isEmpty(url)) {
                Picasso.get()
                        .load(url)
                        .placeholder(R.drawable.sijiali)
                        .into(imageView);
            } else {
                imageView.setImageResource(R.drawable.sijiali);
            }
        }
    }
    
    

    BindingAdapter也支持重载

    package com.aruba.databinding;
    
    import android.text.TextUtils;
    import android.widget.ImageView;
    
    import androidx.databinding.Bindable;
    import androidx.databinding.BindingAdapter;
    
    import com.squareup.picasso.Picasso;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class ImageViewBindingAdapter {
        @BindingAdapter("image")
        public static void setImage(ImageView imageView, String url) {
            if (!TextUtils.isEmpty(url)) {
                Picasso.get()
                        .load(url)
                        .placeholder(R.drawable.sijiali)
                        .into(imageView);
            } else {
                imageView.setImageResource(R.drawable.sijiali);
            }
        }
    
        @BindingAdapter("image")
        public static void setImage(ImageView imageView, int resId) {
            imageView.setImageResource(resId);
        }
    
        @BindingAdapter(value = {"image", "default"}, requireAll = false)
        public static void setImage(ImageView imageView, String url, int resId) {
            if (!TextUtils.isEmpty(url)) {
                Picasso.get()
                        .load(url)
                        .placeholder(resId)
                        .into(imageView);
            } else {
                imageView.setImageResource(resId);
            }
        }
    }
    
    

    二、双向绑定

    DataBinding支持双向绑定,前面我们实现了单向绑定,当变量值发生变化,那么控件上也会更新,双向绑定和单向绑定对比,新增了如果控件中属性的值发生变化,那么变量的值也会发生变化


    1.BaseObservable

    首先定义一个实体类

    package com.aruba.databinding2;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class User {
        public String userName;
    
        public User(String userName) {
            this.userName = userName;
        }
    }
    
    

    再定义一个ViewModel继承至BaseObservable,并在想要双向绑定的方法上加上@Bindable注解,那么相应的get和set方法就会被自动调用,当值发生变化时,调用set方法,我们还可以通过notifyPropertyChanged方法来通知绑定get方法的控件重新执行get方法

    package com.aruba.databinding2;
    
    import android.util.Log;
    import android.widget.Toast;
    
    import androidx.databinding.BaseObservable;
    import androidx.databinding.Bindable;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class UserViewModel extends BaseObservable {
        private static final String TAG = UserViewModel.class.getSimpleName();
        private User user;
    
        public UserViewModel() {
            user = new User("张三");
        }
    
        @Bindable
        public String getUserName() {
            return user.userName;
        }
    
        public void setUserName(String userName) {
            if (userName != null && !userName.equals(getUserName())) {
                user.userName = userName;
                notifyPropertyChanged(BR.userName);
                Log.i(TAG, userName);
            }
        }
    }
    
    

    xml中使用变量时,和之前有点不同,需要加上"="号,布局文件如下:

    <?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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">
    
            <androidx.appcompat.widget.AppCompatEditText
                android:layout_width="300dp"
                android:layout_height="wrap_content"
                android:minHeight="48dp"
                android:text="@={userViewModel.userName}"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
        <data>
    
            <variable
                name="userViewModel"
                type="com.aruba.databinding2.UserViewModel" />
        </data>
    </layout>
    

    最后在Activity中绑定ViewModel

    package com.aruba.databinding2;
    
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.databinding.DataBindingUtil;
    import androidx.databinding.ViewDataBinding;
    
    import android.os.Bundle;
    
    import com.aruba.databinding2.databinding.ActivityMainBinding;
    
    public class MainActivity extends AppCompatActivity {
    
        private UserViewModel userViewModel;
        private ActivityMainBinding activityMainBinding;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            userViewModel = new UserViewModel();
            activityMainBinding.setUserViewModel(userViewModel);
        }
    }
    

    效果:



    日志打印:
    2021-09-11 12:41:07.601 5811-5811/com.aruba.databinding2 I/UserViewModel: 张
    2021-09-11 12:41:08.854 5811-5811/com.aruba.databinding2 I/UserViewModel: h
    2021-09-11 12:41:09.060 5811-5811/com.aruba.databinding2 I/UserViewModel: hh
    2021-09-11 12:41:09.834 5811-5811/com.aruba.databinding2 I/UserViewModel: hhu
    2021-09-11 12:41:10.550 5811-5811/com.aruba.databinding2 I/UserViewModel: hhut

    2.ObservableField

    除了继承BaseObservable外,还可以使用ObservableField对象,重新定义一个ViewModel,ObservableField不需要加注解了,使用方便很多

    package com.aruba.databinding2;
    
    import android.util.Log;
    
    import androidx.databinding.ObservableField;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class UserViewModel2 {
        private static final String TAG = UserViewModel2.class.getSimpleName();
        private ObservableField<User> userObservableField;
    
        public UserViewModel2() {
            User user = new User("赵四");
            userObservableField = new ObservableField<>(user);
        }
    
        public String getUserName() {
            return userObservableField.get().userName;
        }
    
        public void setUserName(String userName) {
            Log.i(TAG,userName);
            userObservableField.get().userName = userName;
        }
    }
    
    

    布局中运用变量

    <?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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">
    
            <androidx.appcompat.widget.AppCompatEditText
                android:id="@+id/appCompatEditText"
                android:layout_width="300dp"
                android:layout_height="wrap_content"
                android:minHeight="48dp"
                android:text="@={userViewModel.userName}"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
    
            <androidx.appcompat.widget.AppCompatEditText
                android:layout_width="300dp"
                android:layout_height="wrap_content"
                android:minHeight="48dp"
                android:text="@={userViewModel2.userName}"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintHorizontal_bias="0.495"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/appCompatEditText"
                app:layout_constraintVertical_bias="0.133" />
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
        <data>
    
            <variable
                name="userViewModel"
                type="com.aruba.databinding2.UserViewModel" />
            <variable
                name="userViewModel2"
                type="com.aruba.databinding2.UserViewModel2" />
            
        </data>
    </layout>
    

    Activity中传入绑定后,效果:



    日志打印:
    2021-09-11 12:50:39.530 6277-6277/com.aruba.databinding2 I/UserViewModel2: 赵
    2021-09-11 12:50:40.634 6277-6277/com.aruba.databinding2 I/UserViewModel2: b
    2021-09-11 12:50:41.292 6277-6277/com.aruba.databinding2 I/UserViewModel2: bf
    2021-09-11 12:50:41.457 6277-6277/com.aruba.databinding2 I/UserViewModel2: bff
    2021-09-11 12:50:42.560 6277-6277/com.aruba.databinding2 I/UserViewModel2: bffy

    三、RecyclerView的绑定

    定义实体类

    package com.aruba.databinding3;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class Idol {
        public String chName;
        public String enName;
        public String image;
    
        public Idol(String chName, String enName, String image) {
            this.chName = chName;
            this.enName = enName;
            this.image = image;
        }
    }
    
    

    定义item的布局文件

    <?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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingVertical="10dip">
    
            <ImageView
                android:id="@+id/imageView"
                android:layout_width="100dip"
                android:layout_height="100dip"
                app:image="@{idol.image}"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toStartOf="@+id/guideline2"
                app:layout_constraintHorizontal_bias="0.432"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.054"
                tools:srcCompat="@tools:sample/avatars" />
    
            <TextView
                android:id="@+id/textViewChName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{idol.chName}"
                android:textSize="24sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.138"
                app:layout_constraintStart_toStartOf="@+id/guideline2"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.063"
                tools:text="斯嘉丽.约翰逊" />
    
            <TextView
                android:id="@+id/textViewEnName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="28dp"
                android:text="@{idol.enName}"
                android:textSize="18sp"
                app:layout_constraintStart_toStartOf="@+id/textViewChName"
                app:layout_constraintTop_toBottomOf="@+id/textViewChName"
                tools:text="Scarlett Johansson" />
    
            <androidx.constraintlayout.widget.Guideline
                android:id="@+id/guideline2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_constraintGuide_percent="0.4" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    
        <data>
    
            <variable
                name="idol"
                type="com.aruba.databinding3.Idol" />
        </data>
    </layout>
    

    定义adapter,转换databinding对象和Activity中稍有不同

    package com.aruba.databinding3;
    
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import androidx.annotation.NonNull;
    import androidx.databinding.DataBindingUtil;
    import androidx.recyclerview.widget.RecyclerView;
    
    import com.aruba.databinding3.databinding.ItemBinding;
    
    import java.util.List;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {
        private List<Idol> datas;
    
        public RecyclerViewAdapter(List<Idol> datas) {
            this.datas = datas;
        }
    
        @NonNull
        @Override
        public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            ItemBinding inflate = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item, parent, false);
    
            return new MyViewHolder(inflate);
        }
    
        @Override
        public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
            Idol idol = datas.get(position);
            holder.itemBinding.setIdol(idol);
        }
    
    
        @Override
        public int getItemCount() {
            return datas.size();
        }
    
        static class MyViewHolder extends RecyclerView.ViewHolder {
            private ItemBinding itemBinding;
    
            public MyViewHolder(ItemBinding itemBinding) {
                super(itemBinding.getRoot());
                this.itemBinding = itemBinding;
            }
    
            public MyViewHolder(@NonNull View itemView) {
                super(itemView);
            }
        }
    }
    
    

    Activity中配置RecyclerView

    package com.aruba.databinding3;
    
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.databinding.DataBindingUtil;
    import androidx.recyclerview.widget.LinearLayoutManager;
    
    import android.os.Bundle;
    
    import com.aruba.databinding3.databinding.ActivityMainBinding;
    
    public class MainActivity extends AppCompatActivity {
    
        private RecyclerViewAdapter adapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            adapter = new RecyclerViewAdapter(IdolUtils.get());
            activityMainBinding.recyclerview.setAdapter(adapter);
            activityMainBinding.recyclerview.setLayoutManager(new LinearLayoutManager(this));
        }
    }
    

    效果:


    四、DataBinding+ViewModel+LiveData组合使用

    我们现在将最开始的明星界面完全使用ViewModel代替xml中的变量,并新增了分数,我们点击点赞时,分数+1

    首先实体类如下:

    package com.aruba.databingviewmodellivedata;
    
    import androidx.lifecycle.MutableLiveData;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class Idol {
        private MutableLiveData<String> name;
        private MutableLiveData<Integer> star;
        private MutableLiveData<String> imageUrl;
        private MutableLiveData<Integer> score;
    
        public Idol(String name, int star, String imageUrl,int score) {
            this.name = new MutableLiveData<>();
            this.name.setValue(name);
            this.star = new MutableLiveData<>();
            this.star.setValue(star);
            this.imageUrl = new MutableLiveData<>();
            this.imageUrl.setValue(imageUrl);
            this.score = new MutableLiveData<>();
            this.score.setValue(score);
        }
    
        public MutableLiveData<String> getName() {
            return name;
        }
    
        public MutableLiveData<Integer> getStar() {
            return star;
        }
    
        public MutableLiveData<String> getImageUrl() {
            return imageUrl;
        }
    
        public void setName(String name) {
            this.name.setValue(name);
        }
    
        public void setStar(Integer star) {
            this.star.setValue(star);
        }
    
        public void setImageUrl(String imageUrl) {
            this.imageUrl.setValue(imageUrl);
        }
    
        public MutableLiveData<Integer> getScore() {
            return score;
        }
    
        public void setScore(Integer score) {
            this.score.setValue(score);
        }
    }
    
    

    定义ViewModel,注意要实现双向绑定,返回的是LiveData对象

    package com.aruba.databingviewmodellivedata;
    
    import androidx.lifecycle.MutableLiveData;
    import androidx.lifecycle.ViewModel;
    
    /**
     * Created by aruba on 2021/9/11.
     */
    public class IdolViewModel extends ViewModel {
        private Idol idol;
    
        public IdolViewModel() {
    
        }
    
        public void setIdolMutableLiveData(Idol idol) {
            if (idol == null) throw new NullPointerException("idol can not be null");
    
            this.idol = idol;
        }
    
        public MutableLiveData<String> getIdolName() {
            return idol.getName();
        }
    
        public MutableLiveData<Integer> getIdolStar() {
            return idol.getStar();
        }
    
        public MutableLiveData<String> getIdolImageUrl() {
            return idol.getImageUrl();
        }
    
        public MutableLiveData<Integer> getScore() {
            return idol.getScore();
        }
    
        public void addScore(int add) {
            idol.setScore(idol.getScore().getValue() + add);
        }
    }
    
    

    在xml中引用:

    <?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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">
    
            <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="32dp"
                android:onClick="@{()->idolViewModel.addScore(2)}"
                android:text="点赞"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.498"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/include"
                tools:text="appear" />
    
            <ImageView
                android:id="@+id/imageView"
                android:layout_width="200dp"
                android:layout_height="200dp"
                app:image="@{idolViewModel.getIdolImageUrl()}"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.497"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.08"
                tools:srcCompat="@tools:sample/avatars" />
    
            <include
                android:id="@+id/include"
                layout="@layout/include_layout_info"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="24dp"
                app:idolViewModel="@{idolViewModel}"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/imageView" />
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
        <data>
            <variable
                name="idolViewModel"
                type="com.aruba.databingviewmodellivedata.IdolViewModel" />
        </data>
    </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"
        xmlns:tools="http://schemas.android.com/tools">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingVertical="10dp">
    
            <TextView
                android:id="@+id/textView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:text="@{idolViewModel.getIdolName()}"
                android:textSize="30sp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:text="name" />
    
            <TextView
                android:id="@+id/textView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="12dp"
                android:text="@{StarUtils.getStar(idolViewModel.getIdolStar())}"
                android:textSize="24sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView2"
                tools:text="Star" />
    
            <TextView
                android:id="@+id/textView3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="12dp"
                android:text="@{String.valueOf(idolViewModel.getScore())}"
                android:textSize="30sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textView"
                tools:text="score" />
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
        <data>
    
            <variable
                name="idolViewModel"
                type="com.aruba.databingviewmodellivedata.IdolViewModel" />
            
            <import type="com.aruba.databingviewmodellivedata.StarUtils"/>
    
        </data>
    </layout>
    

    Activity中配置

    package com.aruba.databingviewmodellivedata;
    
    import android.os.Bundle;
    
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.databinding.DataBindingUtil;
    import androidx.lifecycle.ViewModelProvider;
    
    import com.aruba.databingviewmodellivedata.databinding.ActivityMainBinding;
    
    
    public class MainActivity extends AppCompatActivity {
    
        private ActivityMainBinding activityMainBinding;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
            IdolViewModel idolViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(IdolViewModel.class);
            Idol idol = new Idol("斯嘉丽·约翰逊", 4, "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp3.ssl.cdn.btime.com%2Ft01ef53f09683630079.jpg%3Fsize%3D640x1137&refer=http%3A%2F%2Fp3.ssl.cdn.btime.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633920976&t=59dab0f58d59d80e00bf3ef182728a80",10);
            idolViewModel.setIdolMutableLiveData(idol);
            activityMainBinding.setIdolViewModel(idolViewModel);
            activityMainBinding.setLifecycleOwner(this);
        }
    }
    

    效果:


    通过DataBinding+ViewModel+LiveData组合使用,我们再也不需要findbyid了,并且MVVM架构层次清晰,ViewModel中值的改变,会自动更新ui,耦合性降低,大大减少了后期维护成本
    Demo地址:https://gitee.com/aruba/my-jetpack-application.git

    相关文章

      网友评论

        本文标题:JetPack--DataBinding

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