快播根据相关法律法规和政策-asf转换mp4

compoundbutton
2023年4月4日发(作者:手机绑定)

Databinding双向绑定详解

图⽚来⾃必应

Databinding是Google推出的⼀个⽀持View与ViewModel绑定的Library,可以说Databinding建⽴了⼀个UI与数据模型之间的桥

梁,即UI的变化可以通知到ViewModel,ViewModel的变化同样能够通知到UI从⽽使UI发⽣改变,⼤⼤减少了之前View与Model之间

的胶⽔代码,如findViewById,改变及获取TextView的内容还需要调⽤setText()、getText(),获取EditText编辑之后的内容需要调

⽤getText(),⽽有了Databinding的双向绑定,这些重复的⼯作都将被省去。下⾯我们就来看⼀下如何使⽤Databinding来双向绑定

⾸先我们先定⼀个ViewModel,将这个ViewModel的变量content与布局⽂件中的TextView绑定:

classMyViewModel:ViewModel(){

varcontent:ObservableFiled=ObservableFiled()

}

复制代码

官⽅⽀持的双向绑定这⾥的官⽅⽀持指的是Databinding库中已经定义了⼀些View的双向绑定,我们如果要使⽤的话只需要将xml⽂件

中的"@{}"改成"@={}",如下代码

(test_):

<?xmlversion="1.0"encoding="utf-8"?>

xmlns:android="/apk/res/android">

name="model"

type="l"/>

android:layout_width="match_parent"

android:layout_height="match_parent"

>

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@{t}"

/>

复制代码

上述代码中注释部分就是将单向绑定改为双向绑定的代码

官⽅⽀持的双向绑定:

AbsListViewandroid:selectedItemPosition

CalendarViewandroid:date

CompoundButtonandroid:checked

DatePickerandroid:year,android:month,android:day

NumberPickerandroid:value

RadioGroupandroid:checkedButton

RatingBarandroid:rating

SeekBarandroid:progress

TabHostandroid:currentTab

TextViewandroid:text

TimePickerandroid:hour,android:minute

那么双向绑定是怎么实现的呢?⾸先来看⼀下Databinding的源码:

⾸先是我们在编译之后会⽣成⼏个相关⽂件如test_,test_,,BR⽂件等。我们主要

来看⼀下这个⽂件。这个⽂件的主要作⽤是声明xml内的View控件以及声明的ViewModel,如本例中声明了⼀个

TextView:

@NonNull

LayoutmboundView0;

@NonNull

ewmboundView1;

//variables

@Nullable

lmModel;

复制代码

上述代码中mboundView0为根布局、mboundView1为声明了双向绑定的TextView,mModel为与View绑定的ViewModel。(注意:并不

是所有的变量都会被声明,有三种情况:在xml布局中声明id的,将会被定义为静态变量,可以直接通过Databinding对象访问;引⽤了

model数据的;根布局)

接下来当我们定义了双向绑定的时候,会⽣成这样⼀段代码:

eBindingListenermboundView1androidTextAttrChanged=eBindingListener(){

@Override

publicvoidonChange(){

//()

//(()callbackArg_0)

//调⽤定义的TextViewBindingAdapter中的getTextString(TextViewview)⽅法得到mboundView1(即布局中定义的TextView)的值

callbackArg_0=tString(mboundView1);

//localizevariablesforthreadsafety

//model

lmodel=mModel;

//()

//当前View绑定的ViewModel中的content的值

modelContentGet=null;

//model!=null

booleanmodelJavaLangObjectNull=false;

//t

ableField<>modelContent=null;

//t!=null

booleanmodelContentJavaLangObjectNull=false;

modelJavaLangObjectNull=(model)!=(null);

if(modelJavaLangObjectNull){

modelContent=tent();

modelContentJavaLangObjectNull=(modelContent)!=(null);

if(modelContentJavaLangObjectNull){

//将View中的值取出并复制给相应的model中绑定的数据

((()(callbackArg_0)));

}

}

}

};

复制代码

通过上述代码可以看到实现逆向绑定的关键部分,可以看到调⽤InverseBindingListener接⼝的onChange()⽅法就可以将view的值传回

ViewModel。那么它在哪⾥被⽤到呢?看下⾯的代码:

@Override

protectedvoidexecuteBindings(){

...

if((dirtyFlags&0x4L)!=0){

tWatcher(...,mboundView1androidTextAttrChanged);

}

}

复制代码

可以看到在executeBindings这个⽅法中,将InverseBindingListener的值传给setTextWatcher,这个⽅法怎么来的后⾯会提到。

上⾯的代码中提到⼀个TextViewBindingAdapter,通过它的名字就可以看出是绑定View与ViewModel的适配器,那么来看⼀下

TextViewBindingAdapter做了什么(在包rs下):

@BindingAdapter("android:text")

publicstaticvoidsetText(TextViewview,CharSequencetext){

finalCharSequenceoldText=t();

//为防⽌双向绑定⽆限循环调⽤⽐较新旧内容是否相同

if(text==oldText||(text==null&&()==0)){

return;

}

if(textinstanceofSpanned){

if((oldText)){

return;//Nochangeinthespans,sodon'tsetanything.

}

}elseif(!haveContentsChanged(text,oldText)){

return;//Nocontentchanges,sodon'tsetanything.

}

t(text);

}

复制代码

上述代码相信⼤家并不陌⽣,当Databinding在给某⼀个控件的XXX属性赋值的时候,需要去找到相应的setXXX()⽅法,然后将model中

的值给这个View。⽽@BindingAdapter("xxx")正是将这个setXXX()⽅法与xxx属性关联起来,所以上⾯部分的代码作⽤总结起来就是做

了t(text)。(注:可以看到代码中有对新旧内容的⽐较,只有当内容不同的时候才会执⾏下⼀步,这是为了防⽌双向绑定循环

调⽤)

再来看下⼀个⽅法:

@InverseBindingAdapter(attribute="android:text",event="android:textAttrChanged")

publicstaticStringgetTextString(TextViewview){

t().toString();

}

复制代码

这个⽅法在中已经说明了,是双向绑定取值时调⽤的⽅法,@InverseBindingAdapter注解和@BindingAdapter

注解作⽤类似。

再来看下⼀个关键⽅法:

@BindingAdapter(value={"android:beforeTextChanged","android:onTextChanged",

"android:afterTextChanged","android:textAttrChanged"},requireAll=false)

publicstaticvoidsetTextWatcher(TextViewview,finalBeforeTextChangedbefore,

finalOnTextChangedon,finalAfterTextChangedafter,

finalInverseBindingListenertextAttrChanged){

finalTextWatchernewValue;

//listener为空则不作处理

if(before==null&&after==null&&on==null&&textAttrChanged==null){

newValue=null;

}else{

//创建⼀个TextWatcher监听text内容的变化

newValue=newTextWatcher(){

@Override

publicvoidbeforeTextChanged(CharSequences,intstart,intcount,intafter){

if(before!=null){

TextChanged(s,start,count,after);

}

}

@Override

publicvoidonTextChanged(CharSequences,intstart,intbefore,intcount){

if(on!=null){

Changed(s,start,before,count);

}

//如果设定了双向绑定,则InverseBindingListener不为空,可参见之前的对其的赋值。

if(textAttrChanged!=null){

//调⽤onChange()⽅法(实现在中),即将view的值传回给ViewModel

ge();

}

}

@Override

publicvoidafterTextChanged(Editables){

if(after!=null){

extChanged(s);

}

}

};

}

finalTextWatcheroldValue=istener(view,newValue,tcher);

if(oldValue!=null){

TextChangedListener(oldValue);

}

if(newValue!=null){

tChangedListener(newValue);

}

}

复制代码

从上⾯的代码我们可以看到@BindingAdapter绑定了⼏个属性,其中有⼀个叫做"android:textAttrChanged",那么这个属性就代表了当

TextView的text属性变化,xxxAttrChanged。可以想象得到那这个⽅法就是在android:text属性发⽣变化的时候会被调⽤。我们在这个⽅

法中设置了对android:text这个属性内容变化的监听(注意是内容变化,不是属性变化),以后当每次改内容变化的时候,就会调⽤

ge()这个⽅法将TextView的值传回给ViewModel。

上述的是通过官⽅⽀持的源码来看双向绑定,那么我们要⾃定义的View使⽤双向绑定应该怎么做呢?

⾃定义View双向绑定

通过对官⽅源码分析,我们发现要实现双向绑定关键是需要实现Adapter,实现setter,getter,以及Listene⽅法。下⾯就让我们来

实现⼀个⾃定义View的双向绑定吧。对MyModel稍作修改:

classMyModel:ViewModel(){

varcontent:ObservableField=ObservableField()

funonChangeClick(view:View){

if(viewisTestEditView){

Log.v("---TAG---",())

ue("改变后")

Log.v("---TAG---","click:${()}")

}

}

}

复制代码

对test_修改(主要是使⽤⾃定义的View),加了⼀个点击事件,点击之后修改View的内容:

android:layout_width="match_parent"

android:layout_height="50dp"

app:content="@={t}"

android:onClick="@{(view)->geClick(view)}"

/>

复制代码

这⾥我们引⼊⼀个⾃定义View:这是⼀个类似表单的View,左边是个⼩标题,右边可以填⼀些信息等:

classTestEditView:LinearLayout{

privatelateinitvarcontentView:TextView

privatevaronContentedListener:OnContentChangedListener?=null

{

...

}

privatefuninitView(context:Context){

valview=e(context,_edit_layout,this)

contentView=ewById(_content)

}

funsetOnContentListener(

listener:OnContentChangedListener?){

entedListener=listener

}

publicfunsetValue(content:String){

if(y()||(!y()&&content==)){

return

}

=content

onContentedListener?.onContentChanged()

}

fungetContent():String{

ng()

}

//内容改变Listener

interfaceOnContentChangedListener{

funonContentChanged()

}

}

复制代码

省略了部分代码,主要就是定义了⼀个内容改变的Listener。下⾯是TestEditViewAdapter的代码:

//使⽤InverbaseBindingMethod注解,与使⽤@BindingAdapter效果⼀样其中,event、method可声明也可以不声明,不声明的话会默认设置xxxAttrChanged与ge

//@InverseBindingMethods(InverseBindingMethod(type=TestEditView::class,attribute="content",event="contentAttrChanged",method="getContent"))

objectTestEditViewAdapter{

@JvmStatic

@BindingAdapter("app:content")

funsetContent(view:TestEditView,content:String){

if(mpty()&&tent()==content){

return

}

ue(content)

}

@JvmStatic

@InverseBindingAdapter(attribute="app:content",event="contentAttrChanged")

fungetContent(view:TestEditView):String{

tent()

}

@JvmStatic

@BindingAdapter(value="app:contentAttrChanged",requireAll=false)

funsetContentAttrChangedListener(view:TestEditView,bindingListener:InverseBindingListener){

if(bindingListener==null){

ontentListener(null)

}else{

//如果设置了双向绑定则为其添加监听

ontentListener(object:OnContentChangedListener{

overridefunonContentChanged(){

ge()

}

})

}

}

}

复制代码

接下来我们只需要在Fragment中为content赋值,然后运⾏程序,点击TestEditView,就可以看到Log输出了content的初始值与改变后

的值(不贴图展⽰了)。

valbinding=e(inflater!!,container,false)

valmodel=MyModel()

("初始值")

=model

复制代码

总结以上就是Databinding双向绑定的使⽤⽅法,总的来说应该注意两点:

1、修改"@{}"为"@={}"

2、写setXXX(),xxxAttrChanged(),getXXX()⽅法。

更多推荐

compoundbutton