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<String>"/>
<variable name="sparse" type="SparseArray<String>"/>
<variable name="map" type="Map<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写这么多了,去歇着了,点个关注私信【更新】我就******奥利给就来更新了
原文:哦豁技术
网友评论