美文网首页Android开发经验谈Android开发
轻轻松松带你入门Jetpack:Data Binding,原来这

轻轻松松带你入门Jetpack:Data Binding,原来这

作者: Android高级架构 | 来源:发表于2020-05-09 15:29 被阅读0次

    About Jetpack


    一年前有缘看了一下Jetpack,但并没有过多的去关注,最近在看Google IO 2019相关资料,看到了Jetpack的身影,不得不陷入深思,无法自拔。

    JetPack的官方说法:

    Jetpack 是 Android 软件组件的集合,使您可以更轻松地开发出色的 Android 应用。这些组件可帮助您遵循最佳做法、让您摆脱编写样板代码的工作并简化复杂任务,以便您将精力集中放在所需的代码上。

    总结性

    • 加速开发:以组件的形式供我们依赖使用。
    • 消除样板代码:还记得在Activity中一大堆findViewById么?能做的不止这么多。
    • 构建高质量应用:现在化设计、避开bug、向后兼容。

    Android Jetpack 组件是库的集合,这些库是为协同工作而构建的,不过也可以单独采用,同时利用 Kotlin 语言功能帮助提高工作效率。可全部使用,也可混合搭配!
    以上是对官网的摘录。作为开山之篇,先从架构方向的数据绑定库入门开始,让同学感受它的魅力。


    Data Binding Library(数据绑定库)

    借助数据绑定库(Data Binding Library),可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源。数据绑定库要求在Android 4.0以上,Gradle 1.5.0以上。实践证明Android SDK和Gradle版本越高,对Data Binding的支持越好,越简单,速度越快。
    举个栗子,这个栗子不重,两只手指可以举起来:

    findViewById<TextView>(R.id.sample_text).apply {
        text = viewModel.userName
    }
    

    栗子中通过findViewById找到TextView组件,并将其绑定到 viewModel 变量的 userName 属性。而下面在布局文件中使用数据绑定库将文本直接分配到TextView组件上,这样就无需调用上述任何 Java 代码。

    <TextView  android:text="@{viewmodel.userName}" />
    

    竟然这么好用,为啥不了解看看呢?

    配置

    在我们的项目build.gradle文件下配置如下代码。

    android {
        ...
        dataBinding {
            enabled = true
        }
    }
    

    如果Gradle插件版本在3.1.0-alpha06以上,可以使用新的Data Binding编译器,有利于加速绑定数据文件的生成。在项目的gradle.properties文件添加如下配置。

    android.databinding.enableV2=true
    

    同步一下,没什么问题的话,配置已经成功了~

    入门
    • 定义一个数据对象
    data class User(var name: String, var age: Int)
    
    • 布局绑定
      我们创建名为activity_main.xml的布局文件,内容如下:
    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="user" type="com.gitcode.jetpack.User"/>
       </data>
    
       <LinearLayout
               android:layout_width="match_parent"
               android:layout_height="match_parent">
           //在TextView中使用
           <TextView android:layout_width="match_parent"
                     android:gravity="center"
                     android:text="@{user.name}"
                     android:layout_height="match_parent"/>
       </LinearLayout>
    </layout>
    

    布局文件的根元素不再是以往的LinearLayout、RelativeLayout等等,而是layout。在data元素内添加variable,其属性表示声明一个com.gitcode.jetpack.User类型的变量user。如果多个变量的话,可在data元素下添加多个varialbe元素,格式是一致的。

    <data>
       <variable name="user" type="com.gitcode.jetpack.User"/>
       <variable name="time" type="com.gitcode.jetpack.Time"/>
    </data>
    

    在@{}语法中使用表达式将变量赋值给view的属性。例如:这里将user变量的firstName属性赋值给TextView的text属性。

    android:text="@{user.firstName}"
    
    • 绑定数据
      此时布局声明的user变量值还是初始值,我们需要为其绑定数据。

    默认情况下,会根据目前布局文件名称来生成一个绑定类(binding class),例如当前布局文件名是activity_main,那么生成的类名就是ActivityMainBinding。

    绑定类会拥有当前布局声明变量,并声明getter或者setter方法,也就是说ActivityMainBinding类会带有user属性和getUser、setUser方法,变量的默认初始化与Java一致:引用类型为null,int为0,bool为false。

    在MainActivity的onCreate()方法中添加如下代码,将数据绑定到布局上。

    val binding: ActivityMainBinding 
            = DataBindingUtil.setContentView(this, R.layout.activity_main);
    binding.user = User("GitCode", 3)
    

    经典代码是这样的:

    setContentView(R.layout.activity_main)
    val user=User("GitCode",3)
    val tvName=findViewById<TextView>(R.id.tvName)
    tvName.text = user.name
    

    可有看出,使用数据绑定库会使代码简洁很多,可读性也很高。 运行一下项目,既可以考到效果了~

    如果是在Fragment、Adapter中使用,那就要换个姿势了。

    val listItemBinding = ListItemBinding
                .inflate(layoutInflater, viewGroup, false)
    //或者
    val listItemBinding = DataBindingUtil
                .inflate(layoutInflater, R.layout.list_item, viewGroup, false)
    
    布局与绑定表达式

    在一开始介绍Data Binding Libaray时,就使用了@{}语法,花括号里面的内容称为绑定表达式,绑定表达式其实并不复杂,跟我们正常使用Java和Kotlin语言的表达式没多大区别。那我们可以在表达式中使用什么类型的运算符或者关键字呢?

    常用运算符

    运算符 符号
    算法 加、减、乘、除、求余(+ 、 - 、* 、/、 %)
    逻辑 与、或(&&、
    一元 + 、-、 !、 ~
    移位 >>、 >>>、 <<
    关系 == 、> 、<、 >= 、<=(使用符号<时,要换成<)

    其他常用的
    同时也支持字符拼接+,instanceof,分组、属性访问、数组访问、?:、转型、访问调用,基本类型等等等。

    也就是说,绑定表达式语言大多数跟宿主代码(Java or Kotlin)的表达式差不多。为什么说是大多数,因为不能使用this、super、new和Explicit generic invocation(明确的泛型调用)等。

    举个栗子:

    android:text="@{String.valueOf(index + 1)}"
    android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
    android:transitionName='@{"image_" + id}'
    

    再举个栗子:

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

    如果user.displayName不为null则使用,否则使用user.lastName.在这里也看得出,可以通过表达式访问类的属性。绑定类会自动检查当前变量是否为null,以避免发生空指针异常。栗子:如果user变量为null,那么user.lastName也会是null。

    集合
    像数组,链表,Maps等常见的集合,都可以采用下标[]访问它们的元素。

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

    注意在data元素内添加了import元素,表示导入该类型的定义,这样表达式中引用属性可读性高点,使用也方便。

    来个容易掰的栗子:

    <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}"/>
    

    通过导入View类型,就可以使用相关属性,例如这里的View.VISIBLE。

    有时导入的类全名太长了或者存在相同类型的类名,我们就可以给它取个别名,然后就可用别名进行coding~

    <import type="android.view.View"/>
    
    <import type="com.gitcode.jetpack.View"
            alias="JView"/>
    

    使用资源
    使用下面语法:

    android:padding="@{@dimen/largePadding}"
    

    相关资源的的表达式引用,贴张官网截图:



    事件处理

    数据绑定库允许我们在事件到View时候通过表达式去处理它。 在数据绑定库中支持两种机制:方法调用和监听器绑定。

    方法调用

    点击事件会直接绑定到处理方法上,当一个事件发生,会直接传给绑定的方法。类似我们在布局上使用android:onclick与Activity 的方法绑定。在编译的时候已经绑定,在@{}表达式中的方法如果在Activity找不到或者方法名错误,就会在编译时期报错,方法签名(返回类型和参数相同)一致。

    丢个栗子:

    定义一个接口,用于处理事件。

    //定义一个处理点击事件的类
    interface  MethodHandler {
        fun onClick(view: View)
    }
    

    在布局声明了methodHandler变量,并在Button的onClick方法使用表达式@{methodHandler::onClick},onClick方法需要与上面接口一致,不然编译器期报错。

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
        <data>
            ...
            <variable name="methodHandler"
                type="com.gitcode.jetpack.MethodHandler"/>
        </data>
    
        <LinearLayout
                android:layout_width="match_parent"
                android:orientation="vertical"
                android:gravity="center_horizontal"
                android:layout_height="match_parent">
             ...
            <Button android:layout_width="wrap_content"
                    android:text="Method references"
                    android:layout_marginTop="10dp"
                    android:onClick="@{methodHandler::onClick}"
                    android:layout_height="wrap_content"/>
        </LinearLayout>
    </layout>
    

    然后在Activity中实现MethodHandler,并赋值给绑定类的变量。

    class MainActivity : AppCompatActivity(), MethodHandler{
        lateinit var binding: ActivityMainBinding
    
          override fun onCreate(savedInstanceState: Bundle?) {
            ...
            binding.methodHandler = this
        }
        
        override fun onClick(view: View) {
            Log.i(TAG, "Method handling")
        }
    }
    

    因此,当我们点击Button的时候,Activity的onClick方法就会被回调。

    监听器绑定

    监听器绑定与方法调用不同的是,监听器不再编译器与处理方法绑定,而是在点击事件传递到当前view时,才与处理方法绑定,而且监听器并不要表达式方法名与处理方法同名,只要返回类型一致即可,如果有返回值得话。

    来个栗子:

    • 定义接口用于处理事件
    interface  ListenerHandler {
        fun onClickListener(view: View)
    }
    
    • 在布局中定义变量和表达式
    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
        <data>
            <variable name="listener" type="com.gitcode.jetpack.ListenerHandler"/>
        </data>
    
        <Button android:layout_width="wrap_content"
                android:text="Listener"
                android:layout_marginTop="10dp"
                android:onClick="@{(view)->listener.onClickListener(view)}"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    <layout>
    

    注意到使用lambda表达式,因此可以在@{}内做更多操作,如预处理数据等。

    • 处理方法 同样在Activity实现ListenerHandler方法,并赋值给绑定类的变量。
    class MainActivity : AppCompatActivity(), ListenerHandler {
        lateinit var binding: ActivityMainBinding
        
        override fun onClickListener(view: View) {
            Log.i(TAG, "Listener handling")
        }
        
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            binding.listener=this
        }
    }
    

    点击Button,就能看到onClickListener回调了~

    wdnmd写这么多了,去歇着了,点个关注私信【更新】我就******奥利给就来更新了


    原文:哦豁技术

    相关文章

      网友评论

        本文标题:轻轻松松带你入门Jetpack:Data Binding,原来这

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