Jetpack 架构组件:Data Binding

通过Data Binding,通过声明式布局以精简的代码来绑定应用程序逻辑和布局,这样就不用编写大量的模板代码。

配置

在对应的 Module 的 build.gradle 中添加:

buildFeatures {
        dataBinding = true
    }

如果要用kotlin 开发,还需要添加

apply plugin: 'kotlin-kapt'

// dependencies
kapt "com.android.databinding:compiler:$rootProject.ext.gradle_version"

用法

基础使用

  • 创建Animal实体类
public class Animal {

    private String name;

    private String area;

    public Animal(String name, String area) {
        this.name = name;
        this.area = area;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }
}
  • 布局文件

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <import type="com.xf.databindingdemo.Animal"/>
        <variable
            name="animal"
            type="Animal" />
    data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:text="@{animal.name}"
            android:layout_height="wrap_content"/>

        <TextView
            android:layout_width="wrap_content"
            android:text="@{animal.area}"
            android:layout_height="wrap_content"/>
    LinearLayout>
layout>

根节点由某一个容器(比如LinearLayout)变为layout ,layout中包含了data节点和传统的视图,在data中定义了variable节点。其中,name属性表示变量的名称,type表示这个变量的类型。variable节点的每一个变量都会在Binding辅助类(ActivityExampleBinding.java)中生成对应的getters和setters。接着将@{animal.name}和@{animal.area}赋值给TextView 的text 属性。

  • 在Activity 中将实体类和布局文件进行了绑定
public  class ExampleActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 注释1
        ActivityExampleBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_example);
       // 注释2
        Animal animal=new Animal("巨松鼠","西南部");
        binding.setAnimal(animal);
    }
}

上面的代码注释1处的代码替换setContentView(),注释2处的代码用来给布局中的控件进行赋值。其中ActivityExampleBinding是系统根据activity_example.xml 生成的一个ViewModel类,它包含了布局文件中所有的绑定关系,并会根据绑定表达式给布局文件赋值。

效果图如下:
在这里插入图片描述

点击事件处理

DataBinding 对点击事件处理,有两种方式:

  • 第一种
// xml 文件
<Button
     android:id="@+id/btn"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="按钮" />
            

// 代码
 binding.btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(ExampleActivity.this,"点击",Toast.LENGTH_SHORT).show();
            }
        });
  • 第二种
// xml 文件

<import type="android.view.View.OnClickListener"/>
        <variable
            name="Onclicklistener"
            type="OnClickListener" />

<Button
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:onClick="@{Onclicklistener}"
   android:text="按钮" />


// 代码
binding.setOnclicklistener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(ExampleActivity.this,"点击",Toast.LENGTH_SHORT).show();
            }
        });

变量和集合应用

// xml

        <variable
            name="name"
            type="String" />

        <variable
            name="age"
            type="int" />
            
       <import type="java.util.ArrayList" />
        <variable
            name="list"
            type="ArrayList<String>" />

        <import type="java.util.Map" />
        <variable
            name="map"
            type="Map<String,String>" />

        <variable
            name="arrays"
            type="String[]" />   
            
        ...
        
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{name}" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{String.valueOf(age)+"周期"}' />  
        
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{list.get(1)}" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{map["name"]}' />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{arrays[2]}" />

为了支持泛型,可以使用转义字符。List集合通过lis[index]来获取数值,也可以调用list.get(index);同样,Map集合也是如此。

// 代码
        binding.setName("巨松鼠");
        binding.setAge(2);

        ArrayList<String> animals=new ArrayList<>();
        animals.add("中华鬣羚");
        animals.add("猕猴");
        animals.add("白枕鹤");
        binding.setList(animals);

        String[] animalsArray={"白琵鹭","白头鹤","金雕"};
        binding.setArrays(animalsArray);

        Map<String,String> animalMap=new HashMap<>();
        animalMap.put("name","中华鬣羚");
        binding.setMap(animalMap);

支持表达式

  • 数学表达式: + - / * %
  • 字符串合并: +
  • 逻辑表达式: && ||
  • 位操作符: & | ^
  • 一元操作符: + - ! ~
  • 位移操作符: >> >>> <<
  • 比较操作符: == > < >= <=
  • Instanceof
  • 分组操作符: ()
  • character, String, numeric, null
  • 强转、方法调用
  • 字段访问
  • 数组访问:[]
  • 三元操作符: ?:

Converter 应用

使用DataBinding提供的Converter,把数据格式转为需要的格式。

// xml
     <variable
        name="time"
        type="java.util.Date" />

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{time}" />

TextView 的text属性需要的是String类型的值,这里给了一个Date 类型的值,因此,需要写一个Converter 来进行类型转换。

public class BindingUtils {
    @BindingConversion
    public static String converDate(Date date){
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
        return format.format(date);
    }
}

// Activity
binding.setTime(new Date());

converDate 方法在哪个类中并不重要,重要的是@BindingConversion注解。converDate方法会将Date类型转换为String类型的日期并返回。

include 布局应用

  • include的布局
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="user"
            type="com.xf.databindingdemo.Human" />
    data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:text='@{user.name,default="小米"}'
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{user.age+"",default="18"}'
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    androidx.constraintlayout.widget.ConstraintLayout>
layout>
  • Activity的布局文件
  <variable
            name="user"
            type="com.xf.databindingdemo.Human" />
        
        ...
        
        <include layout="@layout/item_layout"
            app:user="@{user}"/>       

注意: app:user="@{user}"。第一个user是include里name的引用。第二user是当前传入的值。

动态更新和双向绑定

DateBinding 提供了3种动态更新机制,根据Model 实体类的内容来动态更新UI,分别对应于类(Observable)、字段(ObservableField)和集合类型(Observable 容器类)。

使用Observable

通过继承BaseObservable来实现动态更新

public class Animal extends BaseObservable {

    private String name;

    private String area;

    public Animal(String name, String area) {
        this.name = name;
        this.area = area;
    }

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
        notifyPropertyChanged(BR.area);
    }
}

只需要在getter 上使用@Bindable注解,在setter中通知更新就可以了。其中BR 是编译时生成的类,用@Bindable标记过的getter方法会在BR中生成一个相应的字段。在setter中调用notifyPropertyChanged(BR.name)通知系统BR.name这个字段的数据已经发生变化并更新UI。

// xml 
        <TextView
            android:layout_width="wrap_content"
            android:layout_marginTop="20dp"
            android:layout_height="wrap_content"
            android:text='@{animal.name+"-->"+animal.area}' />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:onClick="@{Onclicklistener}"
            android:text="使用Observable更新数据" />
        
        // Activity 中代码    
        Animal animal=new Animal("巨松鼠","西南部");
        binding.setAnimal(animal);

        binding.setOnclicklistener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animal.setName("华南虎");
                animal.setArea("华南地区");
            }
        });

效果如图
Jetpack 架构组件:Data Binding_第1张图片

使用ObservableField

系统为我们提供的所有基本数据类型对应的Observable 类,比如ObservableInt、ObservableFloat、ObservableBoolean 等,也可以使用引用数据类型和基本数据类型通用的ObservableField,它们都继承自BaseObservable。

public class Human {
    public ObservableField<String> name=new ObservableField<>();
    public ObservableInt age=new ObservableInt();

    public Human(String name, int age) {
        this.name.set(name);
        this.age.set(age);
    }

    public ObservableField<String> getName() {
        return name;
    }

    public void setName(ObservableField<String> name) {
        this.name = name;
    }

    public ObservableInt getAge() {
        return age;
    }

    public void setAge(ObservableInt age) {
        this.age = age;
    }
}

// 代码
Human human=new Human("小溪",18);
binding.setHuman(human);
binding.setOnclicklistener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                human.name.set("小米");
                human.age.set(20);
            }
        });

效果如图
Jetpack 架构组件:Data Binding_第2张图片

使用Observable容器类

Observable容器类包括ObservableArrayList 和ObservableArrayMap。

// xml 
        <import type="androidx.databinding.ObservableList"/>
        <import type="com.xf.databindingdemo.Human"/>

        <variable
            name="dataList"
            type="ObservableList<Human>" />
            
        ...
        
        <TextView
            android:layout_width="wrap_content"
            android:layout_marginTop="20dp"
            android:layout_height="wrap_content"
            android:text="@{dataList[0].name}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_marginTop="20dp"
            android:layout_height="wrap_content"
            android:text="@{dataList[1].name}" />

// 代码
        
        ObservableArrayList<Human> dataList=new ObservableArrayList<>();
        Human human1=new Human("小溪",18);
        dataList.add(human1);
        Human human2=new Human("小米",18);
        dataList.add(human2);
        binding.setDataList(dataList);

        binding.setOnclicklistener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                human1.name.set("花花");
                human2.name.set("小夕");
            }
        });

效果如图
Jetpack 架构组件:Data Binding_第3张图片

双向绑定

从MVVM模式的角度来讲,双向绑定就是Model 和View 通过ViewModel 进行双向动态更新。

  <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={user.name}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@{user.name}" />


        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:onClick="@{Onclicklistener}"
            android:text="双向绑定" />

定义EditText来改变Human 的name 字段,关键就是将“@{user.name}”改为“@={user.name}”。TextView来动态显示Human 的name 字段变化。

 Human human2=new Human("小米",18);
        binding.setUser(human2);
        binding.setOnclicklistener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                human2.name.set("小夕");
                human2.age.set(20);
            }
        });

效果如图
Jetpack 架构组件:Data Binding_第4张图片

结合RecyclerView

  • 直接看 Item 的布局
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="user"
            type="com.xf.databindingdemo.Human" />
    data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:text='@{user.name,default="小米"}'
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{user.age+"",default="18"}'
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    androidx.constraintlayout.widget.ConstraintLayout>
layout>
  • RecyclerView 的数据绑定是在 Adapter 中完成的
class MyAdapter extends RecyclerView.Adapter<MyAdapter.VH>{

        private List<Human> dataLIst;

        public MyAdapter(List<Human> dataLIst) {
            this.dataLIst = dataLIst;
        }

        @NonNull
        @Override
        public MyAdapter.VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            ItemLayoutBinding inflate = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_layout, parent, false);

            return new VH(inflate);
        }

        @Override
        public void onBindViewHolder(@NonNull MyAdapter.VH holder, int position) {
            holder.getBinding().setUser(dataLIst.get(position));
        }

        @Override
        public int getItemCount() {
            if (dataLIst==null)
                return 0;
            return dataLIst.size();
        }

        class VH extends RecyclerView.ViewHolder{
            private ItemLayoutBinding binding;

            public VH(@NonNull ItemLayoutBinding binding) {
                super(binding.getRoot());
                this.binding=binding;
            }

            public ItemLayoutBinding getBinding(){
                return binding;
            }
        }
    }
  • Activity 代码
viewDataBinding.recy.setLayoutManager(new LinearLayoutManager(this));
 MyAdapter myAdapter=new MyAdapter(dataList);
 viewDataBinding.recy.setAdapter(myAdapter);

效果如图

Jetpack 架构组件:Data Binding_第5张图片

如果能帮助您,请点赞、关注

你可能感兴趣的:(AndRoid,基础与进阶,Data,Binding,Jetpack,组件)