(jetPack)数据绑定(二)

使用可观察对象

当一个对象发生变更会发出通知,数据绑定库允许使用对象,字段以及 集合 进行观察

一个就旧的对象可以使用数据绑定但是修改对象不会自动引起UI 的更新,数据绑定可用于使您的数据对象在数据更改时通知其他对象(称为侦听器)

当这些可观察数据对象之一绑定到UI且数据对象的属性更改时,UI将自动更新。

可观察字段

创建实现该Observable接口的类涉及一些工作 ,如果您的类仅具有一些属性,那么这是不值得的。在这种情况下,可以使用泛型 Observable类和以下特定于原始类型的类来使字段可观察:

ObservableBoolean

ObservableByte

ObservableChar

ObservableShort

ObservableInt

ObservableLong

ObservableFloat

ObservableDouble

ObservableParcelable

可观察字段是具有单个字段的自包含可观察对象。原始版本避免在访问操作期间装箱和拆箱。要使用此机制,请使用public finalJava编程语言创建一个属性,或使用Kotlin 创建一个只读属性,如以下示例所示:

package union.com.viewgroup.jetpack;

import androidx.databinding.ObservableField;
import androidx.databinding.ObservableInt;

import io.reactivex.Observable;

public class User {

    public final ObservableField<String> weight = new ObservableField<>();
    public final ObservableInt age = new ObservableInt();


}

actvity:

package union.com.viewgroup.jetpack;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.gcm.Task;

import union.com.viewgroup.R;
import union.com.viewgroup.databinding.ActivityDataBindingBinding;

public class MyActivity extends AppCompatActivity {

    private User user1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityDataBindingBinding binding = DataBindingUtil.setContentView(MyActivity.this, R.layout.activity_data_binding);
        user1 = new User("张", "三");
        user1.age.set(10);
        user1.weight.set("160");
        binding.setUser1(user1);
        binding.setPresenter(new Presenter());


    }


    public class Presenter {
        public void onSaveClick() {
            user1.age.set(20);
        }

        public void onCompletedChanged(String task, boolean completed) {
        }

    }
}
     

xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="presenter"
            type="union.com.viewgroup.jetpack.MyActivity.Presenter" />
        
        <import type="android.view.View" alias="view" />
        <import type="union.com.viewgroup.jetpack.User" />
        <variable
            name="user1"
            type="User" />
        <import type="java.util.List"/>
        <variable
            name="list"
            type="List&lt;User>" />
    </data>

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

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{user1.firstName}" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{String.valueOf(user1.age)}" />
            <Button
                android:onClick="@{()-> presenter.onSaveClick()}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="点击事件"
                />
        </LinearLayout>

    </LinearLayout>



</layout>

点击button age 显示为 20

AndroidStudio 3.1 以上允许你使用 LiveData 对象

可观察集合

xml:

    <import type="androidx.databinding.ObservableList"/>
        <variable
            name="list"
            type="ObservableList&lt;User>" />
            
                <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{String.valueOf(list.size())}" />

activity:

      list= new ObservableArrayList<>() ;
        list.add(user1);
        list.add(new User());
        list.add(new User());
        binding.setList(list);

----
    
         list.add(new User());

界面显示listSize 发生了变化

可观察对象

实现该Observable接口的类 允许注册要在可观察对象上进行属性更改通知的侦听器。

Observable接口具有添加和删除侦听器的机制,但是您必须确定何时发送通知。为了简化开发,数据绑定库提供了BaseObservable该类,该类实现了侦听器注册机制。实现的数据类BaseObservable负责通知属性何时更改。这是通过Bindable为getter 分配 注释并notifyPropertyChanged() 在setter中调用 方法来完成的,如以下示例所示

xml:

        <variable
            name="car"
            type="union.com.viewgroup.jetpack.Car" />


<TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{car.color}" />
          <Button
                android:onClick="@{()-> presenter.setColor()}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="设置颜色"
                />

activity:

        car  = new Car();
        car.setColor("red");
        binding.setCar(car);


public void setColor(){
            Log.i("grage","setColor");
            car.setColor("blue");
        }

Car:

package union.com.viewgroup.jetpack;

import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;

import union.com.viewgroup.BR;

public class Car extends BaseObservable {
    private String color;
    private String brand;

    @Bindable
    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
        notifyPropertyChanged(BR.color);
    }

    @Bindable
    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }
}

数据绑定生成一个BR在模块包中命名的类,该类包含用于数据绑定的资源的ID。该 Bindable注释产生的一个条目BR编译期间类文件。如果不能更改数据类的基类,则 Observable可以使用PropertyChangeRegistry 对象来实现接口, 以高效地注册和通知侦听器。

生成的绑定类

创建一个绑定对象

膨胀布局后,应尽快创建绑定对象,以确保在使用布局内的表达式将视图绑定到视图之前,不会修改视图层次结构。将对象绑定到布局的最常见方法是在绑定类上使用静态对象。您可以使用inflate() 方法绑定类的方法扩展视图层次结构并将对象绑定到该视图层次结构

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
     ActivityDataBindingBinding.inflate(getLayoutInflater());
}

绑定View

ActivityDataBindingBinding.bind(viewRoot)

有些情况下我们无法获取绑定对象 我们可以用DateBindingUtils 来创建绑定对象,代码如下:

View rootView = LayoutInflater.from(this).inflate(laout,parent,attachToParent); ViewDataBinding binding1 = DataBindingUtil.bind(rootView);

如果你在Fragment ,ListView或者 RecyclerView adapter 使用数据绑定,你需要用到inflate 或者 DataBindingUtils 方法来绑定类,

ListItemBinding.inflate(layoutInflater,viewGroup,false) 
// or
ListItemBinding binding = DataBindUtils.inflate(layoutInflater,R.layout.item,viewGroup,false)

具有Id 的视图

数据绑定库为每个在布局中具有Id 的视图在绑定类中创建一个不可变的字段。

xml:

  <TextView
                android:id="@+id/tvFirstName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{user1.firstName}" />

activity:

 ActivityDataBindingBinding binding = DataBindingUtil.setContentView(MyActivity.this, R.layout.activity_data_binding);
        binding.tvFirstName

该库可以单次从视图层次结构中提取包含Id 的视图,这种机制比FindViewById()为每个布局视图调用方法要快。

ViewStubs

与普通视图不同,ViewStub对象从一个不可见的视图开始。当它们变为可见或被明确告知要膨胀时,它们会通过膨胀另一个布局来替换自身。

因为ViewStub本质上从视图层次结构中消失,所以绑定对象中的视图也必须消失以允许被垃圾回收声明。由于视图是最终视图 ViewStubProxy,因此ViewStub在生成的绑定类中,对象将代替,从而使您可以访问ViewStub存在的,并且可以在膨胀后访问膨胀的视图层次结构ViewStub

当膨胀另一个布局时,必须为新布局建立一个绑定。因此,ViewStubProxy必须监听ViewStub OnInflateListener并在需要时建立绑定。由于在给定时间只能存在一个侦听器,因此 ViewStubProxy允许您设置OnInflateListener建立绑定后调用的。

立即绑定

当变量或可观察对象发生更改时,绑定将安排在下一帧之前更改。但是,有时绑定必须立即执行。要强制执行,请使用executePendingBindings() 方法。

进阶绑定

动态绑定

有时,特定的绑定类是未知的。例如,RecyclerView.Adapter针对任意布局的操作不知道特定的绑定类。在调用onBindViewHolder()方法期间,它仍然必须分配绑定值。

在下面的示例中,RecyclerView绑定到的所有布局都具有一个 item变量。该BindingHolder对象具有一个getBinding()返回ViewDataBinding基类的方法 。

public void onBindViewHolder(BindingHolder holder, int position) {
    final T item = items.get(position);
    holder.getBinding().setVariable(BR.item, item);
    holder.getBinding().executePendingBindings();
}

**注意:**数据绑定库生成一个BR在模块包中命名的类,该类包含用于数据绑定的资源的ID。在上面的示例中,库自动生成BR.item变量。

自定义绑定类名称

默认情况下,绑定类基于布局文件的名称生成,从大写字母开始,删除下划线(_),大写以下字母,并在单词Binding后加后缀。该类放在 databinding模块包下面的包中。例如,布局文件 contact_item.xml生成ContactItemBinding类。如果模块包为com.example.my.app,则将绑定类放置在 com.example.my.app.databinding包中。

通过调整元素的class属性,可以将绑定类重命名或放置在不同的包中 data。例如,以下布局在当前模块ContactItemdatabinding包中生成绑定类:

<data class="ContactItem">
    …
</data>

您可以通过在类名中添加句点作为前缀来在另一个包中生成绑定类。以下示例在模块包中生成绑定类:

<data class=".ContactItem">
    …
</data>

您还可以在要生成绑定类的位置使用完整的程序包名称。以下示例ContactItemcom.example包中创建绑定类 :

<data class="com.example.ContactItem">
    …
</data>

绑定适配器

设置属性值

每当绑定值更改时,生成的绑定类都必须使用绑定表达式在视图上调用setter方法。您可以允许数据绑定库自动确定方法,显式声明该方法或提供自定义逻辑以选择一种方法。

自动方法选择

1、存在属性名的方法选择

数据绑定库会自动查找 设置属性的setXXX 方法 接收对应正确的返回类型

注意:setXXX 方法返回值类型需要对应 xxx 属性的正确属性类型

2、无属性名

数据绑定即使给定的属性名没有也可以使用, 你可以使用绑定器创建属性。例如:DrawerLayout 类无任何属性和setter 方法,下面例子使用 setScrimColor() 和 setDrawerListener() 方法 对应xml 文件中 scrimColor 和 drawerListener 属性

<android.support.v4.widget.DrawerLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:scrimColor="@{@color/scrim}"
    app:drawerListener="@{fragment.drawerListener}">

自定义方法名

一些属性拥有setter 方法 但是不是跟母名称来匹配的,这种场景下,一个属性是根据bindingMethods 注解来完成和setter 方法关联的,一个类中可以拥有多个 BindingMethod 方法 ,绑定注解可以在添加到你应用的任何 类中,下面的例子 android:tint 属性通过 setImageTintList(ColorStateList) 方法进行绑定而不是setTint:

@Bindmethods({@BindingMetod(type = "android.widget.ImageView",attribute="android:tint", method="setImageTintList"),})

提供自定义 逻辑

部分属性需要自定义一个逻辑来实现,例如,在没有android:paddingLeft 属性对应的setter 方法的情况下,用setPadding(left,top,right,bottom) 方法需要自定义提供,这个需要你使用BindingAdapter 绑定一个static 方法来在逻辑上提供一个 setter 方法

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
  view.setPadding(padding,
                  view.getPaddingTop(),
                  view.getPaddingRight(),
                  view.getPaddingBottom());
}

注意:方法类型非常重要,第一个参数的类型决定是哪个类型的View 需要被关联。第二个参数决定你的表达式设置的属性

Bindding adapter 对于一些自定义类型是非常有用的。例如一个自定义加载器 通过一个工作线程去加载图片

@BindingAdapter({"imageUrl", "error"})public static void loadImage(ImageView view, String url, Drawable error) {  Picasso.get().load(url).error(error).into(view);}

注意:为了匹配,数据绑定库忽略了自定义名称空间。

如果imageUrl和error都用于ImageView对象,并且imageUrl是字符串,error是可绘制的,则调用适配器。如果您希望在设置任何属性时调用适配器,您可以将适配器的可选requireAll标志设置为false,如下面的示例所示:

@BindingAdapter(value={"imageUrl", "placeholder"}, requireAll=false)
public static void setImageUrl(ImageView imageView, String url, Drawable placeHolder) {
  if (url == null) {
    imageView.setImageDrawable(placeholder);
  } else {
    MyImageLoader.loadInto(imageView, url, placeholder);
  }
}

注意:当存在冲突时,您的绑定适配器将覆盖默认的数据绑定适配器。

对象转换

从绑定表达式返回对象时,库选择用于设置属性值的方法。对象被转换为所选方法的参数类型。这种行为在使用ObservableMap类来存储数据的应用程序中很方便,如下面的例子所示:

<TextView
   android:text='@{userMap["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content" />

自定义转换

在一些场景,一个自定义转换被要求在特殊的类型中。例如 android:background 属性需要一个drawable 但是color 值需要的是一个 Integer 类型

<View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

无论什么时候 一个drawable 需要返回实际上返回一个int 类型,这时就需要转换int 类型为drawable 类型,转换器方法可以通过一个静态的BindingConversion 注解来完成转换

@BindingConversion
public static ColorDrawable converColorToDrawable(int color){
	return new ColorDrawable(color)
}		

绑定layout 到 架构组件中

AndroidX库包含体系结构组件,您可以使用它们来设计健壮、稳定的系统,数据绑定库与体系结构组件无缝协作,以进一步简化UI的开发。应用程序中的布局可以绑定到架构组件中的数据,这已经帮助您管理UI控制器生命周期并通知数据中的更改。

LiveData

您可以使用 LiveData 对象作为数据自动绑定数据源来通知UI 的更改,具体的参考LiveData

不像实现 Observable 、或者 Observable fields ,LiveData对象知道订阅数据更改的观察者的生命周期。这些知识带来了许多好处,在使用LiveData的好处中进行了说明。在Android Studio 3.1或更高版本中,您可以用数据绑定代码中的LiveData对象替换可见字段。

要将LiveData对象与绑定类一起使用,您需要指定一个生命周期所有者来定义LiveData对象的范围。下面的示例指定绑定类实例化后的活动作为生命周期所有者:

class ViewModelActivity extends AppCompatActivity{
	@Override
	protected void onCreate(Bundle savedInstanceState){
		// 使用绑定对象来初始化view
		UserBinding binding = DataBindingUtil.setContentView(this,R.layout.user);
		// 指定当前activity 生命周期的管理者
		binding.setLifeCycleOwner(this);
	}
}

你可以使用一个 ViewModel 组件as explained in Use ViewModel to manage UI-related data 绑定数据到layout ,在 ViewModel 组件中,你可以使用LiveData 对象转换数据 或者合并数据源。下面的例子展示了如何在 viewModel 转换数据

class ScheduleViewModel extends viewModel{
	LiveData username;
	public ScheduleViewModel(){
		String result = Repository.userName;
		userName = TransFormations.map(result,result -> resutl.value);
	}
}

使用 ViewModel 管理UI 数据

数据绑定库与ViewModel组件无缝协作,后者公开布局观察到的数据并对其更改做出反应。使用带有数据绑定库的ViewModel组件,可以将UI逻辑从布局中移到组件中,这样更容易测试。数据绑定库确保在需要时从数据源绑定和解除绑定视图。剩下的大部分工作是确保公开正确的数据。有关此体系结构组件的更多信息,请参见ViewModel概述。

要将ViewModel组件与数据绑定库一起使用,您必须实例化您的组件(它继承自ViewModel类),获取绑定类的一个实例,并将ViewModel组件分配给绑定类中的一个属性。下面的例子展示了如何使用组件与库:

class ViewModelActivity extends AppComponentActivity{
	@Override
	protected void onCreate(Bundle savedInstanceState){
		//创建viewModel 组件
		UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
		// 使用绑定对象来初始化view
		UserBinding binding = DataBindingUtil.setContentView(this,R.layout.user);
		 // 
        binding.viewmodel = userModel;
	}

}

使用一个 Observable ViewModel 来更多的控制绑定适配器

在某些情况下,您可能更喜欢使用ViewModel组件来实现Observable接口,而不是使用LiveData对象,即使您失去了LiveData的生命周期管理功能。使用一个视图模型组件,实现了可观测的给你更多的控制绑定在你的应用程序适配器。例如,这个模式会给你更多的控制通知当数据发生变化时,它还允许您指定一个自定义的方法来设置一个属性的值在双向数据绑定。

要实现一个observable ViewModel组件,您必须创建一个继承自ViewModel类并实现了observable接口的类。当观察者使用addOnPropertyChangedCallback()和removeOnPropertyChangedCallback()方法订阅或取消订阅通知时,您可以提供自定义逻辑。您还可以在notifyPropertyChanged()方法中提供在属性更改时运行的自定义逻辑。下面的代码示例展示了如何实现一个可观察的视图模型:

class ObservableViewModel extends ViewModel implements Observale{
    private PropertyChangeRegistry  callBacks = new PropertyChangeRegistry();
    @Override
    protected void addOnpropertyChangedCallBack(Observable.OnpropertyChangedCallBack callback){
        ballbacks.add(callback);
    }
    
    @Override
    protected void removeOnPropertyChangedCallback(Observable.OnpropertyChangedCallback callback){
        callbacks.remove(callback);
    }
    
    void notifyChange(){
        callbacks.notifyCallBacks(this,0,null);
    }
    
    void notifyPropertyChanged(int fieldId){
        callbacks.notifyCallbacks(this,fieldId,null);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值