DataBinding实践

Binding Data

xml

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

    <data>
        <variable
            name="user"
            type="com.xiyoukeji.databingtest.User"/>
    </data>

    <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="@{user.lastName}"/>

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

    </LinearLayout>

</layout>

注意外层标签为layout, 并且不能有layout_width和layout_height, 否则appt就报xml属性重复了. 内层为data和布局两层

User类

public class User {
    public String firstName;
    public String lastName;

//    public String getFirstName() {
//        return firstName;
//    }
//
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
//
//    public String getLastName() {
//        return lastName;
//    }
//
    public void setLastName(String lastName) {
        this.lastName = lastName;
   }
//
//    public String firstName() {
//        return "hhh";
//    }
}

user.firstName会怎样调用以上方法呢.后面说说结果.代码就不贴了

activity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        User user = new User();
        user.setFirstName("haha");
        user.setLastName("aiyouwei");
        binding.setUser(user);

    }
}

这里说说user.firstName调用User里方法的顺序. 如果字段firstName是公有的优先顺序是调用getFirstName(), 其次是firstName() 再次是firstName字段, 当firstName是私有的 那user.firstName不可能指向firstName字段 编译时报错 data绑定无法访问数据. 当然只能调用一次 这里说的是优先级

这里比较容易迷茫, 做个记录. 哈哈 - -


Event Handling

事件处理机制(google示例如下)

  • Method References
  • Listener Bindings

Method References

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.MyHandlers"/>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{handlers::onClickFriend}"/>
   </LinearLayout>
</layout>

注意:方法参数和返回值必须和事件监听器的方法一样

Listener Bindings

public class Presenter {
    public void onSaveClick(Task task){}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
      <data>
          <variable name="task" 
                    type="com.android.example.Task"/>
          <variable name="presenter"   
                    type="com.android.example.Presenter"/>
      </data>
      <LinearLayout android:layout_width="match_parent"                         
                    android:layout_height="match_parent">
          <Button android:layout_width="wrap_content" 
                  android:layout_height="wrap_content"
          android:onClick="@{() -> 
                  presenter.onSaveClick(task)}" />
      </LinearLayout>
  </layout>
android:onClick="@{(view) -> presenter.onSaveClick(task)}"

如果想用表达式的值的话,则可以这么写

public class Presenter {
    public void onSaveClick(View view, Task task){}
}
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

或者使用lambda来使用更多参数

public class Presenter {
    public void onCompletedChanged(Task task, boolean completed){}
}
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

如果你监听的方法的返回值不为void, 那么你的表达式的返回值必须和该方法一致

public class Presenter {
    public boolean onLongClick(View view, Task task){}
}
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

If the expression cannot be evaluated due to null objects, Data
Binding returns the default Java value for that type. For example,
null for reference types, 0 for int, false for boolean, etc.

If you need to use an expression with a predicate (e.g. ternary), you
can use void as a symbol.

表达式可能为空也没有关系 DataBinding会返回一个java的默认值给你

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

Imports

<data>
    <import type="android.view.View"/>
</data
<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

名字相同则可以使用别名

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
        alias="Vista"/>
<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List&lt;User&gt;"/>
</data>
%lt;代表<,  &gt;代表>,  这里表示为List<User>类型 备注&nbsp;代表空格

Imported types may also be used when referencing static fields and methods in expressions: //也可以导入静态属性和方法哦

<data>
    <import type="com.example.MyStringUtils"/>
    <variable name="user" type="com.example.User"/>
</data><TextView
   android:text="@{MyStringUtils.capitalize(user.lastName)}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

Just as in Java, java.lang.* is imported automatically //java.lang包是自动导入的. 比如String类

android:text="@{user.displayName ?? user.lastName}"

等价于

android:text="@{user.displayName != null ? user.displayName : user.lastName}"

Includes

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:bind="http://schemas.android.com/apk/res-auto">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </LinearLayout>
</layout>

这样user这个变量就在name.xml和contact.xml中存在了

Data binding does not support include as a direct child of a merge element. For example, the following layout is not supported:
不支持merge标签作为直接子控件, 如下

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:bind="http://schemas.android.com/apk/res-auto">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <merge>
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </merge>
</layout>

能使用的表达式和符号基本和java一致。 三元操作符之类的, 不支持this,super,new
而且dataBinding支持非空校验。 不会出现空指针, 如果user为null, user.name为String 默认值就为null user.age为int 值就为0. 依次类推(同java)

Collections

<data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List&lt;String&gt;"/>
    <variable name="sparse" type="SparseArray&lt;String&gt;"/>
    <variable name="map" type="Map&lt;String, String&gt;"/>
    <variable name="index" type="int"/>
    <variable name="key" type="String"/>
</data>
…
android:text="@{list[index]}"
…
android:text="@{sparse[index]}"
…
android:text="@{map[key]}"

如果key为string 需要使用单引号区分开来. 内外层引号不一样就好

Observable Objects

前面讲的是数据变化没有自动更新ui的. 接下来看看怎么实现自动更新

private static class User extends BaseObservable {
   private String firstName;
   private String lastName;
   @Bindable
   public String getFirstName() {
       return this.firstName;
   }
   @Bindable
   public String getLastName() {
       return this.lastName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
       notifyPropertyChanged(BR.firstName);
   }
   public void setLastName(String lastName) {
       this.lastName = lastName;
       notifyPropertyChanged(BR.lastName);
   }
}

添加@Bindable注解 继承BaseObservable,在layout中出现的属性都可以在BR类中找到对应的字段

ObservableFields

来个升级版, 上面还是有点麻烦

A little work is involved in creating Observable classes, so developers who want to save time or have few properties may use ObservableField and its siblings ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable. ObservableFields are self-contained observable objects that have a single field. The primitive versions avoid boxing and unboxing during access operations. To use, create a public final field in the data class:
public class User {
    public final ObservableField<String> firstName = new ObservableField<>();
    public final ObservableField<String> lastName = new ObservableField<>();
    public final ObservableInt age = new ObservableInt();
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        final User user = new User();
        user.firstName.set("haha");
        user.lastName.set("aiyouwei");
        binding.setUser(user);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                user.firstName.set("a jaslkdjfklajdlf");
            }
        }, 1000L);
    }
}

注意到变化了么.user.firstName.set(“xxx”) 设置属性.

来看看Observable的源码

public class ObservableField<T> extends BaseObservable implements Serializable {
    static final long serialVersionUID = 1L;
    private T mValue;

    /**
     * Wraps the given object and creates an observable object
     *
     * @param value The value to be wrapped as an observable.
     */
    public ObservableField(T value) {
        mValue = value;
    }

    /**
     * Creates an empty observable object
     */
    public ObservableField() {
    }

    /**
     * @return the stored value.
     */
    public T get() {
        return mValue;
    }

    /**
     * Set the stored value.
     */
    public void set(T value) {
        if (value != mValue) {
            mValue = value;
            notifyChange();
        }
    }
}

注意到了么, 继承BaseObservable 在set()的时候notifyChange();
相当于更新了整个BaseObservable, 而这个类相当于User的一个属性firstName. 代码也简洁了不少.good job!

也可以使用ObservableArrayMap, ObservableArrayList

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap&lt;String, Object&gt;"/>
</data><TextView
   android:text='@{user["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user["age"])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

Views With IDs

可以告别findViewById()和butterKnife了, 在xml中添加id, 对应的binding文件就会生成对应的View

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

    <data>
        <variable
            name="user"
            type="com.xiyoukeji.databingtest.User"/>
    </data>

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

        <TextView
            android:id="@+id/lastName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}"/>

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

    </LinearLayout>

</layout>
binding.firstName.setVisibility(View.GONE);

dataBinding会为layout的所有变量生成get, set方法

<data>
    <import type="android.graphics.drawable.Drawable"/>
    <variable name="user"  type="com.example.User"/>
    <variable name="image" type="Drawable"/>
    <variable name="note"  type="String"/>
</data>
public abstract com.example.User getUser();
public abstract void setUser(com.example.User user);
public abstract Drawable getImage();
public abstract void setImage(Drawable image);
public abstract String getNote();
public abstract void setNote(String note);

还有一些替换原生方法就不一一细说了。 一般都是自定义类对控件扩展, 或者是自定义控件添加额外属性方法,有需要再去细看了。暂时先到这。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值