Data Binding学习(二)

Data Binding 布局文件 - (View)

Data binding 的布局文件与传统布局文件有一点不同。它以一个 layout 标签作为根节点,里面是 data 标签与 view 标签。view 标签的内容就是不使用 Data Binding 时的普通布局文件内容。以下是一个例子:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
        <!-- 变量user, 描述了一个布局中会用到的属性 -->
       <variable name="user" type="com.connorlin.databinding.model.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}"/>

        <!-- 布局文件中的表达式使用 “@{}” 的语法 -->
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>

数据对象 - (Model)

假设你有一个 plain-old Java object(POJO) 的 User 对象。

public class User {
   private final String mFirstName;
    private final String mLastName;
    private int mAge;

    public User(String firstName, String lastName, int age) {
        mFirstName = firstName;
        mLastName = lastName;
        mAge = age;
    }
}
或者是 JavaBean 对象:
public class User {
   private final String mFirstName;
    private final String mLastName;
    private int mAge;

    public User(String firstName, String lastName, int age) {
        mFirstName = firstName;
        mLastName = lastName;
        mAge = age;
    }

    public String getFirstName() {
        return mFirstName;
    }

    public String getLastName() {
        return mLastName;
    }

    public int getAge() {
        return mAge;
    }
}

从 Data Binding 的角度看,这两个类是一样的。用于 TextView 的 android:text 属性的表达式@{user.firstName},会读取 POJO 对象的 firstName 字段以及 JavaBeans 对象的 getFirstName()方法。

绑定数据 - (ViewModel)

在默认情况下,会基于布局文件生成一个继承于 ViewDataBinding 的 Binding 类,将它转换成帕斯卡命名并在名字后面接上Binding。例如,布局文件叫 main_activity.xml,所以会生成一个 MainActivityBinding 类。这个类包含了布局文件中所有的绑定关系,会根据绑定表达式给布局文件赋值。在 inflate 的时候创建 binding 的方法如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   
   //  ActivityBaseBinding 类是自动生成的
   ActivityBaseBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_base);
   User user = new User("Connor", "Lin");
   // 所有的 set 方法也是根据布局中 variable 名称生成的
   binding.setUser(user);
}

事件处理

本部分源码请参考 DataBindingDemo -> EventActivity 部分。

类似于 android:onClick 可以指定 Activity 中的函数,Data Binding 也允许处理从视图中发送的事件。

有两种实现方式:

方法调用

  • 监听绑定

二者主要区别在于方法调用在编译时处理,而监听绑定于事件发生时处理。

方法调用

相较于 android:onClick ,它的优势在于表达式会在编译时处理,如果函数不存在或者函数签名不对,编译将会报错。

以下是个例子:

public class EventHandler {
    private Context mContext;
    public EventHandler(Context context) {
        mContext = context;
    }

    public void onClickFriend(View view) {
        Toast.makeText(mContext, "onClickFriend", Toast.LENGTH_LONG).show();
    }
}
表达式如下:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="handler"
            type="com.connorlin.databinding.handler.EventHandler"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{handler::onClickFriend}"/>
        <!-- 注意:函数名和监听器对象必须对应 -->
        <!-- 函数调用也可以使用 `.` , 如handler.onClickFriend , 不过已弃用 -->
    </LinearLayout>
</layout>

监听绑定

监听绑定在事件发生时调用,可以使用任意表达式

此功能在 Android Gradle Plugin version 2.0 或更新版本上可用.

在方法引用中,方法的参数必须与监听器对象的参数相匹配。在监听绑定中,只要返回值与监听器对象的预期返回值相匹配即可。

以下是个例子:

public class EventHandler
{

    public void onTaskClick(Task task){
        task.task("task------");
    }
}

public class Task {
    public void task(String task){
        Log.e("TAG",task);
    }
}

<?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"
    >
    <data>
       
    <variable
        name="eventHandler"
        type="com.example.administrator.myapplication.EventHandler"></variable>
    <variable
        name="task"
        type="com.example.administrator.myapplication.Task"></variable>
    </data>
        <Button
            android:layout_width="wrap_content"
            android:textAllCaps="false"
            android:text="方法调用"
            android:onClick="@{()->eventHandler.onTaskClick(task)}"
            android:layout_height="wrap_content"/>
</layout>

当一个回调函数在表达式中使用时,数据绑定会自动为事件创建必要的监听器并注册监听。
关于参数
  • 参数有两种选择:要么不写,要么就要写全。
<Button 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="@{() -> handler.onTaskClick(task)}" />
或
<Button 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="@{(view) -> handler.onTaskClick(task)}"/>

  • lambda 表达式可添加一个或多个参数,同时参数可任意命名
public class EventHandler {
    public void onTaskClickWithParams(View view, Task task) {
        task.run();
    }
}
<Button 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="@{(theview) -> handler.onTaskClickWithParams(theview, task)}" />

或者
public class EventHandler {
    public void onCompletedChanged(Task task, boolean completed) {
        if(completed) {
            task.run();
        }
    }
}

<CheckBox 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:onCheckedChanged="@{(cb, isChecked) -> handler.onCompletedChanged(task, isChecked)}" />

  • 表达式结果有默认值 null、0、false等等

  • 表达式中可以使用void

<Button 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}" />

关于表达式
  • 复杂的表达式会使布局难以阅读和维护,这种情况我们最好将业务逻辑写到回调函数中

  • 也有一些特殊的点击事件 我们需要使用不同于 android:onClick 的属性来避免冲突。

下面是一些用来避免冲突的属性:

Class Listener Setter Attribute
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

布局详情

本部分源码请参考 DataBindingDemo -> CombineActivity 部分

导入(Imports)

  • data 标签内可以有多个 import 标签。你可以在布局文件中像使用 Java 一样导入引用
<data>
    <import type="android.view.View"/>
</data>

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
  • 当类名发生冲突时,可以使用 alias
<import type="android.view.View"/>
<import type="com.connorlin.databinding.ui.View" alias="AliasView"/>

  • 导入的类型也可以用于变量的类型引用和表达式中
<data>
    <import type="com.connorlin.databinding.model.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List<User>"/>
</data>

注意:Android Studio 还没有对导入提供自动补全的支持。你的应用还是可以被正常编译,要解决这个问题,你可以在变量定义中使用完整的包名。

  • 导入也可以用于在表达式中使用静态方法
public class MyStringUtils {
    public static String capitalize(final String word) {
        if (word.length() > 1) {
            return String.valueOf(word.charAt(0)).toUpperCase() + word.substring(1);
        }
        return word;
    }
}

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


  • java.lang.* 包中的类会被自动导入,可以直接使用,例如, 要定义一个 String 类型的变量
<variable name="test" type="String" />

变量 Variables

  • data 标签中可以有任意数量的 variable 标签。每个 variable 标签描述了会在 binding 表达式中使用的属性。
<data>
    <import type="android.graphics.drawable.Drawable"/>
    <variable name="user"  type="com.connorlin.databinding.model.User"/>
    <variable name="image" type="Drawable"/>
    <variable name="note"  type="String"/>
</data>

  • 可以在表达式中直接引用带 id 的 view,引用时采用驼峰命名法。
<TextView
    android:id="@+id/first_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@={user.firstName}" />

<TextView
    android:text="@{user.lastName}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="@{firstName.getVisibility() == View.GONE ? View.GONE : View.VISIBLE}" />
    <!-- 这里TextView直接引用第一次TextView,firstName为id 的驼峰命名 -->
  • binding 类会生成一个命名为 context 的特殊变量(其实就是 rootView 的 getContext() ) 的返回值),这个变量可用于表达式中。 如果有名为 context 的变量存在,那么生成的这个 context 特殊变量将被覆盖。
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{handler.loadString(context)}"/>
public String loadString(Context context) {
    // 使用生成的context变量
    return context.getResources().getString(R.string.string_from_context);
}
参考链接: http://connorlin.github.io/2016/07/02/Android-Data-Binding-%E7%B3%BB%E5%88%97-%E4%B8%80-%E8%AF%A6%E7%BB%86%E4%BB%8B%E7%BB%8D%E4%B8%8E%E4%BD%BF%E7%94%A8/

https://github.com/LyndonChin/MasteringAndroidDataBinding


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值