首语AndroID Jetpack组件系列文章:
Android Jetpack组件(一)LifeCycle
Android Jetpack组件(二)Navigation
Android Jetpack组件(三)ViewModel
Android Jetpack组件(四)LiveData
Android Jetpack组件(五)Room
Android JetPack组件(六)DataBinding
Android Jetpack组件(七)Paging
Android Jetpack组件(八)WorkManager
AndroID 布局文件通常只负责UI的布局工作,页面通过setContentVIEw()
关联布局文件,再通过UI控件的ID找到控件,接着在页面中通过代码对控件进行 *** 作,因此,页面承担了很大的工作量,为了减轻页面的工作量,Google推出了DataBinding。使得页面和布局之间的耦合度降低。
findVIEwByID()
。布局文件可以包含简单的业务逻辑。DataBinding是我第一个使用的Jetpack的组件,用起来是真的舒服。之前为了繁杂的findVIEwByID()
,一直使用ButterKnife(参考之前文章)来代替这些工作。现在官方已经不推荐使用它了,且停止维护。因此,使用DataBinding来代替它。
要想使用DataBinding,首先需要在app.gradle中启用它。
androID { ..... dataBinding{ enabled=true }}
接着修改布局文件,需要在布局外层添加<layout></layout>
标签,将鼠标移动至布局文件根目录的位置,使用快捷键(alt+enter),选择“Convert to data binding layout”选项,就会@R_301_6796@DataBinding布局文件。
<?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" xmlns:tools="http://schemas.androID.com/tools"> <data> </data> <androIDx.constraintlayout.Widget.ConstraintLayout androID:layout_wIDth="match_parent" androID:layout_height="match_parent"> <TextVIEw androID:ID="@+ID/text_home" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:layout_marginStart="8dp" androID:layout_margintop="8dp" androID:layout_marginEnd="8dp" androID:textAlignment="center" androID:textSize="20sp" androID:text="0" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constrainttop_totopOf="parent" /> </androIDx.constraintlayout.Widget.ConstraintLayout></layout>
使用经过简单配置以后,就可以实例化布局文件了,我们删除掉传统的setContentVIEw()
,通过 DataBindingUtil.setContentVIEw()
来实例化文件。实例化返回的布局文件对象,名字和布局文字名字一致,遵循大驼峰命名规则,后面加上Binding。然后通过binding对象得到控件,控件命名遵循小驼峰规则。
ActivityMainBinding binding=DataBindingUtil.setContentVIEw(this,R.layout.activity_main);binding.textHome.setText("hello databinding!");
数据绑定如何将数据传递到布局文件中呢?首先,在布局文件中定义布局变量<variable/>
,指定对象的名字和类型,当然数据的 *** 作在<data></data>
标签里。data标签里用于放在布局文件中各个UI控件所需要的数据,这些数据类型可以是自定义类型,也可以是基本类型。
<data> <variable name="book" type="com.yhj.jetpackstudy.Book" /> <variable name="number" type="Integer" /></data>
public class Book { private int ID; private String Title; private String author;}
有时我们需要在布局文件中引入一些Java工具类或静态类,处理一些简单的逻辑在布局中,我们可以使用<import />
标签导入。使用alias
,当类名有冲突时,其中一个类可使用别名重命名。默认导入java.lang.*
;
<data> <import type="com.yhj.jetpackstudy.ui.home.Constants"alias="rename"/></data>
布局中的数据绑定使用“@{}”语法写入属性中,通过布局表达式的形式设置TextVIEw
的text。
<TextVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:text="@{book.Title}" /> <TextVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:text="@{Constants.APP_ID}" />
DataBinding为了方便使用,对布局变量提供了Setter
类,因此,在Activity
中,通过setBook()
,将Book对象传递给布局变量。
Book book = new Book(0, "androID", "yhj");//BR类似于AndroID中的R类,由DataBinding@R_301_6796@,用于存放所有布局变量的ID。//DataBinding为了方便使用提供了Setter类,直接使用setXxx()//binding.setvariable(BR.book,book);binding.setBook(book);
绑定后,就不需要再Activity中设置内容了,实现了布局与页面的解耦。
DataBinding具有Null校验,如果绑定值为null,则分配默认值null,如果类型为int
,默认值为0。
在布局中可以包含简单的数据逻辑,可以使用以下运算符和关键字。
算术运算符 + - / * %字符串连接运算符 +逻辑运算符 && ||二元运算符 & | ^一元运算符 + - ! ~移位运算符 >> >>> <<比较运算符 == > < >= <=(请注意,< 需要转义为 <)instanceof分组运算符 ()字面量运算符 - 字符、字符串、数字、null类型转换方法调用字段访问数组访问 []三元运算符 ?:Null 合并运算符视图引用 androID:text="@{String.valueOf(index + 1)}" androID:visibility="@{age > 13 ? VIEw.GONE : VIEw.VISIBLE}" androID:Transitionname='@{"image_" + ID}' <!--Null 合并运算符--> androID:text="@{user.displayname ?? user.lastname}" <!--集合--> androID:text="@{List[index]}" <!--字符串字面量,两种均可--> androID:text="@{map[`firstname`]}" androID:text='@{map["firstname"]}' <!--资源--> androID:padding="@{large? @dimen/largepadding : @dimen/smallpadding}" <!--TextVIEw视图引用同一布局中的EditText视图--> <EditText androID:ID="@+ID/example_text" androID:layout_height="wrap_content" androID:layout_wIDth="match_parent"/> <TextVIEw androID:ID="@+ID/example_output" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:text="@{exampleText.text}"/>
事件响应DataBinding在布局文件中除了绑定数据外,还能够响应用户事件。
首先创建一个事件类,用于接收和响应onClick()
事件。
public class HandleListener { private Context context; public HandleListener(Context context) { this.context = context; } public voID onClicked(VIEw vIEw) { Toast.makeText(context, "the button was clicked!", Toast.LENGTH_SHORT).show(); }}
在<data>
标签中定义布局变量。通过布局表达式,调用onClicked()
.
<variable name="handler" type="com.yhj.jetpackstudy.ui.home.HandleListener" /> androID:onClick="@{handler::onClicked}"
最后在Activity
中将Activity
与布局绑定,实例化HandleListener
类,传入布局文件。
binding.setHandler(new HandleListener(this));
二级页面的绑定对于布局层次结构复杂的页面,我们会将部分布局独立成一个单独的布局文件,通过<include />
标签去引用单独的布局文件,也被称为二级页面。
我们在一级页面中绑定数据后,如何将数据传递到二级页面呢?
<!--自定义的命名空间--> <!--xmlns:bind="http://schemas.androID.com/apk/res-auto"--> <!--bind:book="@{book}"--> <data> <variable name="book" type="com.yhj.jetpackstudy.Book" /> </data> <androIDx.constraintlayout.Widget.ConstraintLayout androID:layout_wIDth="match_parent" androID:layout_height="match_parent" tools:context=".ui.home.HomeFragment"> <include app:book="@{book}" androID:ID="@+ID/include" layout="@layout/fragment_dashboard"/> </androIDx.constraintlayout.Widget.ConstraintLayout>
命名空间app
名字可以自定义,布局变量book也是命名空间xmlns:app
的一个属性。一级页面正是通过命名空间xmlns:app
引用布局变量book,将数据传递给二级页面的。
需要注意的是,数据绑定不支持include
作为merge
元素的直接子布局。merge
是用来帮助在视图树中减少重复布局的。
在二级页面中,我们需要定义一个和一级页面相同的布局变量,用于接收传递过来的数据。然后就可以使用book进行数据绑定了。
<data> <variable name="book" type="com.yhj.jetpackstudy.Book" /> </data> <TextVIEw androID:ID="@+ID/text_home" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:text="@{book.Title}" app:layout_constraintStart_toStartOf="parent" app:layout_constrainttop_totopOf="parent" />
BindingAdapter的原理DataBinding为我们生成数据绑定需要的各种类,其中包含了大量的静态方法,这些静态方法都有@BindingAdapter
注解,在注解中的别名对应UI控件在布局文件中的属性。
public class VIEwBindingAdapter { .... @BindingAdapter({"androID:padding"}) public static voID setpadding(VIEw vIEw, float paddingfloat) { final int padding = pixelsToDimensionPixelSize(paddingfloat); vIEw.setpadding(padding, padding, padding, padding); }}
@RestrictTo(RestrictTo.Scope.liBRARY)@BindingMethods({ @BindingMethod(type = androID.Widget.ImageVIEw.class, attribute = "androID:tint", method = "setimageTintList"), @BindingMethod(type = androID.Widget.ImageVIEw.class, attribute = "androID:tintMode", method = "setimageTintMode"),})public class ImageVIEwBindingAdapter { @BindingAdapter("androID:src") public static voID setimageUri(ImageVIEw vIEw, String imageUri) { if (imageUri == null) { vIEw.setimageURI(null); } else { vIEw.setimageURI(Uri.parse(imageUri)); } } @BindingAdapter("androID:src") public static voID setimageUri(ImageVIEw vIEw, Uri imageUri) { vIEw.setimageURI(imageUri); } @BindingAdapter("androID:src") public static voID setimageDrawable(ImageVIEw vIEw, Drawable drawable) { vIEw.setimageDrawable(drawable); }}
DataBinding以静态方法的形式为UI控件各个属性绑定了相应的代码逻辑,如果在UI控件中的属性使用了布局表达式,那么当布局文件渲染时,绑定它的静态方法自动被调用。
自定义BindingAdapter在项目开发中,经常使用ImageVIEw
来加载网络图片,但是在布局文件中不能设置图片url,我们可以使用BindingAdapter来解决这个问题。
public class CustomImageVIEw extends AppCompatimageVIEw { public CustomImageVIEw(Context context) { super(context); } public CustomImageVIEw(Context context, AttributeSet attrs) { super(context, attrs); } public CustomImageVIEw(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * @param vIEw 本身 * @param imageUrl 图片地址 */ @BindingAdapter(value = {"image_url"}) public static voID setimageUrl(CustomImageVIEw vIEw, String imageUrl) { //网络图片加载框架选择GlIDe GlIDe.with(vIEw).load(imageUrl).into(vIEw); }}
通过自定义ImageVIEw
的方式添加静态方法,并给静态方法添加@BindingAdapter
的注解,设置别名为image_url
,布局文件通过别名来调用该方法。
<?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="imageUrl" type="String" /> </data> <androIDx.constraintlayout.Widget.ConstraintLayout androID:layout_wIDth="match_parent" androID:layout_height="match_parent"> <com.yhj.jetpackstudy.ui.home.CustomImageVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" app:image_url="@{imageUrl}" app:layout_constraintStart_toStartOf="parent" app:layout_constrainttop_totopOf="parent" /> </androIDx.constraintlayout.Widget.ConstraintLayout></layout>
最后给布局变量赋值。
//Fragment中通过 inflater.inflate,相应的DataBinding通过DataBindingUtil.inflate//VIEw root = inflater.inflate(R.layout.fragment_notifications, container, false);FragmentNotificationsBinding binding= DataBindingUtil.inflate(inflater,R.layout.fragment_notifications,null,false);binding.setimageUrl("https://www.yanghujun.top/web_image/0d9e080e3da409db7d0e0bae3e88bc88.jpg");
多参数重载在项目开发中除了设置网络图片外,还有如设置图片圆角,圆形图片等需求,通过BindingAdapter都可以实现。
/** * @param vIEw 本身 * @param imageUrl 图片地址 */ @BindingAdapter(value = {"image_url"}) public static voID setimageUrl(CustomImageVIEw vIEw, String imageUrl) { setimageUrl(vIEw, imageUrl, false); } /** * @param isCircle 是否圆形图片 */ @BindingAdapter(value = {"image_url", "isCircle"}) public static voID setimageUrl(CustomImageVIEw vIEw, String imageUrl, boolean isCircle) { setimageUrl(vIEw, imageUrl, isCircle, 0); } /** * @param radius 设置图片圆角 */ @BindingAdapter(value = {"image_url", "isCircle", "radius"}, requireAll = false) public static voID setimageUrl(CustomImageVIEw vIEw, String imageUrl, boolean isCircle, int radius) { SecureRandom secureRandom = new SecureRandom(); int i = secureRandom.nextInt(16); Requestoptions mRequestoptions = null; RequestBuilder<Drawable> builder = GlIDe.with(vIEw).load(imageUrl).placeholder(VERTICAL_IMAGES_BG[i]); if (isCircle) { mRequestoptions = Requestoptions.circleCroptransform(); } else if (radius > 0) { //设置图片圆角角度 builder.transform(new RoundedCorners(PixUtils.dp2px(radius))); } if (mRequestoptions != null) { builder.apply(mRequestoptions); } VIEwGroup.LayoutParams layoutParams = vIEw.getLayoutParams(); if (layoutParams != null && layoutParams.wIDth > 0 && layoutParams.height > 0) { builder.overrIDe(layoutParams.wIDth, layoutParams.height); } builder.into(vIEw); }
在@BindingAdapter
注解中,参数以value={"",""}
的形式存在,变量requireAll
设置参数是否必须赋值,默认为true
,同时配合GlIDe设置图片的圆角、展位图和尺寸等。
最后在布局文件中根据需求来调用静态方法。
<com.yhj.jetpackstudy.ui.home.CustomImageVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" app:image_url="@{imageUrl}" app:isCircle="@{true}" app:radius="@{10}"/>
双向绑定之前都是使用单向绑定来传递数据,对于一些与用户产生交互的控件,随着字段的变化能更新控件的内容,用户交互时也可以自动得到更新。这就是双向绑定。
使用项目开发中登录页面必不可少,我们希望用户名字段内容变化时,EditText
自动更新,当用户修改EditText
的内容时,用户名字段同步得到更改。
首先创建一个LoginModel
类,让LoginModel
类的用户名字段和EditText
双向绑定。
public class LoginModel extends BaSEObservable { public String username; public LoginModel() { this.username = "yhj"; } @Bindable public String getUsername() { return username; } public voID setUsername(String username) { //判断解决循环调用的问题 if (username != null && !username.equals(this.username)) { this.username = username; //通知观察者,数据已经更新 notifyPropertyChanged(BR.username); } }}
在构造器中设置字段初始值,并写了getter()
和setter()
,在getter()
设置@Bindable
注解,告诉编译器,对这个字段进行绑定,setter()
在用户编辑EditText
内容时自动调用。需要进行手动更新。
完成双向绑定只需要将布局表达式中的@{}
变为@={}
即可。username
字段会随着EditText
内容的变化而变化。
<EditText androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:text="@={viewmodel.username}" />
优化上面的做法有一些弊端,我们的类必须继承BaSEObservable
,getter()
添加@Bindable
注解,setter()
还需要手动刷新。
DataBinding提供了ObservableFIEld<T>
,它能将普通对象包装成一个可观察对象。 ObservableFIEld
可以包装各种基本类型、集合数组类型及自定义类型数据。将代码修改可以实现同样的效果。
public class LoginModel { public final ObservableFIEld<String> username=new ObservableFIEld<>(); public LoginModel() { this.username.set("yhj"); }}
需要注意的是,此类的字段应声明为final
,因为绑定仅检测字段值的变化,而不检测字段本身的变化。此类是可拆分和可序列化的,但是在对对象进行拆分/序列化时,将忽略回调,具体说明可参考源码。
其实,DatBinding将基本类型、集合数组、自定义类型进行了封装,提供了诸如ObservableInt
、ObservableDouble
、ObservableArrayList
及ObservableParcelable
等特定的可观察类。
@H_568_419@
public final ObservableInt age = new ObservableInt(); public ObservableArrayMap<String, Object> user = new ObservableArrayMap<>(); public ObservableArrayList<Object> List = new ObservableArrayList<>();
与liveData和viewmodelObservableFIEld
和liveData作用相似,两者可替换使用,区别在于liveData与生命周期相关,通常在viewmodel中使用,需要通过observe()
对变化监听。
和viewmodel使用时,可以把对控件的赋值、状态等在布局中进行处理,耦合度更低。
notificationsviewmodel = new viewmodelProvIDer(this).get(Notificationsviewmodel.class);notificationsviewmodel.getText().observe(getVIEwlifecycleOwner(), new Observer<String>() { @OverrIDe public voID onChanged(@Nullable String s) { //binding.textvIEw.setText("yanghujun"); binding.setModel(notificationsviewmodel); }});
public class Notificationsviewmodel extends viewmodel { public mutablelivedata<String> mText = new mutablelivedata<>(); public Notificationsviewmodel() { mText.setValue("yhj"); } public liveData<String> getText() { return mText; }}
总结 以上是内存溢出为你收集整理的Android JetPack组件(六)DataBinding全部内容,希望文章能够帮你解决Android JetPack组件(六)DataBinding所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)