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回调了~
不过瘾的,看官网吧
绑定类
后面讲的大多数是在布局中去应用表达式,从这开始,讲点代码中的操作。在一开始入门时候,讲到会依据以后布局生成绑定类,绑定类类名由布局名称依据Pascal规定和增加Binding后缀生成。举个栗子就明确了,以后布局名称:activity\_shared.xml。生成绑定类名称:ActivitySharedBinding。
那么绑定类的作用是什么?
绑定类是数据绑定库为让咱们能够拜访布局中的变量和视图而生成的类。
如何创立或者定制绑定类呢?
创立绑定类
- 应用动态inflate()办法
ActivityMainBinding.inflate(LayoutInflater.from(this))
重载版本
ActivityMainBinding.inflate(getLayoutInflater(), viewGroup, false)
- 应用动态bind()办法
//个别这种状况是布局有作其余用处ActivityMainBinding.bind(viewRoot)
- 在Fragment,ListView,或RecyclerView的adapter应用
val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)// 或者val listItemBinding = DataBindingUtil .inflate(layoutInflater, R.layout.list_item, viewGroup, false)
定制绑定类
通过批改data
元素的class属于达到定制不同名称的绑定类,和其所存储地位。
//生成绑定类名为:ContactItem,寄存在以后组件的绑定类包中<data class="ContactItem"> …</data>//生成绑定类名为:ContactItem,寄存在以后组件包中<data class=".ContactItem"> …</data>//生成绑定类名为:ContactItem,寄存在com.gitcode包中<data class="com.gitcode.ContactItem"> …</data>
拜访Views
如果须要拜访布局中Views,须要给Views增加id,数据绑定库会尽快通过findViewById去绑定。并在Activity中通过绑定类应用。例如:
binding.tvName.text="GitCode"
拜访变量
数据绑定库会为在布局中申明的变量在绑定类中生成setter和getter。例如:
binding.user=User("GitCode",3)
绑定类官网
绑定适配器
每个布局表达式都对应着一个绑定适配器,用于进行设置相应属性或监听器所需的框架调用.艰深点说,咱们通过调用什么办法去给属性赋值?咱们在代码通过setText()办法给view的text属性赋值。讲的就是上面的代码:
binding.tvAge.text="20" //通过tvAge的setText()给TextView的android:text属性赋值
如同跟咱们平时调用的没什么区别:
tvAge.text="20"
这里讲的就是这个,当数据变动时,咱们调用适合的办法(例如setText办法),去给view的属性赋值(例如android:text的text属性)。还不懂的话,持续看~
给View的属性赋值
数据绑定库提供三种形式让咱们去给View的属性赋值:库本人决定抉择调用办法;明确指定调用办法;自定义调用逻辑办法。
库主动抉择
如果View有个属性color,库会尝试去查找setColor(args)办法,参数args的类型须要和表达式的返回类型统一。例如android:color=@{"black"}
,因为"black"
是字符串类型,所以args的参数类型就是String。命名空间android
并没有作强制要求,也能够是gitcode:color=@{"black"}
。库查找办法的规范是setXXX()
办法名和参数类型,这里的XXX
是指属性名。
明确指定
尽管库主动抉择曾经很智能了,但有时view的属性和办法名并不统一,这是就须要咱们明确指定,防止库主动抉择找不到。例如ImageView的android:tint
属性是关联到setImageTintList(ColorStateList)
办法,而不是setTint()
,这时,就须要明确指定了。
@BindingMethods(value = [BindingMethod( type = android.widget.ImageView::class, attribute = "android:tint", method = "setImageTintList")])
BindingMethods是注解在类上的,例如Activity。能够蕴含一个到多个BindingMethod注解。BindingMethod中type示意以后办法(method)匹配到到哪个View的属性(attribute)上。
定制逻辑办法
尽管下面两者曾经满足了大多数状况,但一些非凡状况还是须要本人解决逻辑的。例如,view的android:paddingLeft
属性,没有setPaddingLeft(int)
办法,但提供了setPadding(left, top, right, bottom)
办法。这时候就须要咱们自定义逻辑了。
@BindingAdapter("android:paddingLeft")fun setPaddingLeft(view: View, padding: Int) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom())}
BindingAdapter注解容许定制属性的setter逻辑。setPaddingLeft办法的第一个参数必须是咱们要解决属性的逻辑的View,前面的参数是依据BindingAdapter注解的属性来定位的。例如这里BindingAdapter注解只申明了android:paddingLeft
属性,那么参数padding就是paddigLeft对应的值。设置多个属性是这样子的:
@BindingAdapter("imageUrl", "error")fun loadImage(view: ImageView, url: String, error: Drawable) { Picasso.get().load(url).error(error).into(view)}<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
从这里能够看出,库对命名空间并没有作要求。注解的值imageUrl和error类型必须对应办法参数url和error的类型String和Drawable,只有ImageView同时匹配到两个属性,上述办法才会失效。为此,能够通过设置requireAll = false
,匹配一个值也会失效。
@BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false)fun setImageUrl(imageView: ImageView, url: String, placeHolder: Drawable) { 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" />
绑定表达式的userMap["lastName"]
会返回值,该值会查找setText(CharSequence) 办法中主动转型为字符串并设置给TextView的text属性。但参数类型不确定的时候,就须要进行强制类型转换了,以表明类型。
有时候,绑定表达式返回的类型与设置属性办法的参数类型并不统一。例如:android:background
属性期待的是Drawable(setBackground(drawable),但设置color值时的确一个Int。
<View android:background="@{@color/red}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
这时候咱们须要应用BindingConversion注解将返回值类型Int转换成期待的类型Drawable。
@BindingConversionfun convertColorToDrawable(color: Int) = ColorDrawable(color)
本文转自 https://juejin.cn/post/6844903872520011784,如有侵权,请分割删除。
相干视频举荐:
【2021最新版】Android studio装置教程+Android(安卓)零基础教程视频(适宜Android 0根底,Android初学入门)含音视频_哔哩哔哩_bilibili
Android架构设计原理与实战——Jetpack联合MVP组合利用开发一个优良的APP!_哔哩哔哩_bilibili
Android进阶必学:jetpack架构组件—Navigation_哔哩哔哩_bilibili
Android进阶零碎学习——Jetpack先天优良的基因能够防止数据内存透露_哔哩哔哩_bilibili
Android进阶必学:jetpack架构组件—Navigation_哔哩哔哩_bilibili