DataBinding学习笔记
来源:互联网 发布:golang教程 pdf 编辑:程序博客网 时间:2024/05/17 05:07
1.介绍
DataBinding使数据跟布局变成了一种可能,是MVVM思想的一部分,免去了我们一直在findViewById然后设置等,这是它的优点,既然是学习它,缺点我就不说了,毕竟每个东西的出现都会有两面,我们只要在合适的地方使用就会避免掉它的缺点,从而彰显它的优点。
任何一个名字为aa_bc.xml布局只要是根节点标签是<layout>,只要这个布局被加载inflate,DataBinding工具都会帮我们生成一个相应的AaBcBinding,都是ViewDataBinding的子类,
可以通过DataBindingUtils.bind(view)或者DataBindingUtils.setContentView(actvity,View)或者其它方法得到。
一旦我们调用了DataBindingUtils.setContentView(actvity,View)这个方法或者是DataBindingUtils.bind(view)方法,加载的布局文件,使用布局文件的类,就会通过生成Binding相关联,binding可以直接通过布局中每个控件的id找到这个控件,然后做相关操作。
有时候我们自定义一个布局的时候,往往需要自定义自己的属性,这就需要导入:app标签,然后再style.xml<declare-stylable> </declare-stylable> 中声明自己的<attr/>,然后我们在自定义的布局的构造器中,用相应的方法得到自己设置的属性,然后设置给相应的控件,操作很繁琐吧,如果我们使用DataBinding就会非常简单,我们只需要导入:app标签然后直接声明属性给自己定义布局,比如app:tvName="String",然后我们在自定义布局中实现对应的setTvName(String str)方法就可以把str设置给我们想给的控件,是不是非常方便,从此不再需要写繁琐的<declaer-stylable>标签。
原理分析参考此文章,写的不错:http://www.jianshu.com/p/b1df61a4df77
2.构建
构建请参照github介绍https://github.com/XinRan5312/MasteringAndroidDataBinding
3.使用代码演示
如下是布局文件,如果使用Databinding最外层布局只能是layout另外还有data标签负责填写我们的数据定义和配置
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <!--用到什么类就要事先用import导包,就跟在类中写代码一样,比如下面我们用到Student和View类,所以 我们要首先导包,DataBinding用variable来定义变量 属性包裹变量名字和其类型,比如下面的stu和isQx变量 ,另外这里定义的变量就成为使用类的Binding类的变量,我们可以在使用类里通过binding.setXX来给他们赋值,比如 ActivityAsimpleBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_asimple); Student stu=new Student(null,"Mack",18); binding.setStu(stu); binding.setIsQx(true); 从而达到使用类和对应布局数据的绑定 另外: 1,@{这里其实就是书写java代码的地方} 2,databinding语法中不但有?:三目运算,还有??两目预算@{stu.name??stu.firstName},如果 stu.name非null就是stu.name,否则就是stu.firstName --> <import type="com.xinran.qxdatabinding.beans.Student"></import> <import type="android.view.View"></import> <variable name="stu" type="Student"></variable> <variable name="isQx" type="boolean"></variable> </data><LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="20dp" android:layout_marginRight="20dp"> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{stu.name??stu.firstName}"/> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="成年" android:visibility="@{stu.age>18 ? View.VISIBLE : View.GONE}"/> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="上大学了" android:visibility="@{isQx ? View.VISIBLE : View.GONE}"/></LinearLayout></layout>
下面是使用布局的Activity的初始化代码,这是一个很简单使用例子
public class ASimpleActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityAsimpleBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_asimple); Student stu=new Student(null,"Mack",18); binding.setStu(stu); binding.setIsQx(true); }}
上面的例子是一个DataBinding自动生成Binding类,生成规则是布局名字根据驼峰,如果要自定义呢,也很简单只需要在data跟标签加class属性就好,代码如下:
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data class=".CustomBinding"> <variable name="name" type="String"></variable> <variable name="age" type="String"></variable> <!-- 我们自定义一个Binding的时候,就是不需要按照我们布局名字让DataBinding帮我们生成一个Binding时,我们 只需在data根标签添加class属性就好,用.开头后面跟自定义的类名字,比如 <data class=".CustomBinding"> 另外:定义的变量类型一定要和真实需要类型相同,否则DataBinding回报notFind异常,比如TextView的text需要的是 String类型的数据,如果我们非要把age变量定义成int类型,就会报错 --> </data><LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="20dp" android:layout_marginRight="20dp"> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{name}"/> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{age}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="toChange" android:text="更改"/></LinearLayout></layout>
使用布局的类的代码:
public class CustomBindingActvity extends BaseActivity { private CustomBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding= DataBindingUtil.setContentView(this, R.layout.activity_custom); binding.setName("Join"); binding.setAge("20"); } public void toChange(View view){ binding.setName("Mack"); binding.setAge("16"); }}
处理Include标签include的公共布局,而且Include的布局中也定义了DataBinding变量,并使用,核心知识点在根布局layout中导入:bind标签,如下布局代码:
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <!-- 在布局中include公共布局由于inclue布局中也有自己定义的DataBinding变量,该怎么关联上呢,还好DataBinding 为我们提供了bind标签,我们只需导入xmlns:bind="http://schemas.android.com/apk/res-auto",然后我们就可以用 bind:Include不居中的变量名字=给他们赋的值,比如 <include layout="@layout/layout_title_bar" bind:stu="@{student}" bind:isQx="@{isOk}"/> 就像我们引用android:标签赋值一样 --> <import type="com.xinran.qxdatabinding.beans.Student"></import> <variable name="student" type="Student"></variable> <variable name="name" type="String"></variable> <variable name="isOk" type="boolean"></variable> </data><LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:orientation="vertical"> <include layout="@layout/layout_title_bar" bind:stu="@{student}" bind:isQx="@{isOk}"/> <include layout="@layout/layout_asimple" bind:discript="@{name}" /> <include android:id="@+id/input" layout="@layout/layout_edit"/></LinearLayout></layout>
使用Include布局类使用代码如下:
public class IncludeLayoutActvity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final ActivityIncludeLayoutBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_include_layout); binding.setName("王大拿"); binding.setIsOk(true); binding.setStudent(new Student("jack", "Mack", 19)); //其中input是布局中的id,evInputName是id是input的布局中的id为ev_input_name的控件id //本例中input是布局layout_edit.xml的id,evInputName是layout_edit.xml布局中一个EditText的id binding.input.evInputName.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void afterTextChanged(Editable editable) { binding.setName(editable.toString()); } }); }}
对资源文件的使用,布局文件如下:
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <!-- DataBinding语法对资源文件的使用其实就是把,原来的代码放入到@{}中就可以了,比如: @{@plurals/banana(bananaCount)}//对数组的选择使用 @{@string/nameFormat(firstName,lastName)}//对string的使用 @{isBig?@dimen/text_big:@dimen/text_small}//对dimen数值的引用 --> <variable name="firstName" type="String"></variable> <variable name="lastName" type="String"></variable> <variable name="orangeCount" type="int"></variable> <variable name="bananaCount" type="int"></variable> <variable name="isBig" type="boolean"></variable> </data><LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="20dp" android:layout_marginRight="20dp"> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{@string/nameFormat(firstName,lastName)}"/> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{@plurals/banana(bananaCount)}" /> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{@plurals/orange(orangeCount,orangeCount)}" android:textSize="@{isBig?@dimen/text_big:@dimen/text_small}" /></LinearLayout></layout>
DataBinding的双向绑定的使用,代码如下:
/** * DataBinding本来只支持单向绑定,如果需要双向绑定,就要用系统各种相应的ObsevableXXX类,比如ObservableArrayList * ObservableInt,ObservableField<T>等 * * 或者自己自定义 * 一个类继承BaseObservable,在每个属性的set方法里调用 notifyPropertyChanged(BR.属性名字); * 比如ObservableStudent * * 或者是自己的实体类中的属性都用ObservableXX来定义,比如ObservableStudentOther */public class ObservableActvity extends BaseActivity{ private ObservableArrayList<String> list=new ObservableArrayList<>(); private ObservableArrayMap<String,String> map=new ObservableArrayMap<>(); private ObservableStudent observableStudent=new ObservableStudent(); private ObservableStudentOther observableStudentOther=new ObservableStudentOther(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityObservableBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_observable); initData(); binding.setList(list); binding.setMap(map); binding.setStuOne(observableStudent); binding.setStuoTo(observableStudentOther); } public void changeName(View view){ changeData(); } public void initData(){ list.add("mack"); list.add("jack"); map.put("1", "fhkg"); map.put("2", "mjs"); observableStudent.setName("Join"); observableStudent.setAge("18"); observableStudentOther.name.set("Frank"); observableStudentOther.age.set("25"); } public void changeData(){ list.add("mack2"); list.add("jack2"); map.put("1", "fhkg2"); map.put("2", "mjs2"); observableStudent.setName("Join2"); observableStudent.setAge("18"); observableStudentOther.name.set("Frank2"); observableStudentOther.age.set("25"); }}
ObservableStudent的定义,继承BaseObservable在setXX方法里调用notifyPropertyChanged(BR.XX):
public class ObservableStudent extends BaseObservable { private String name; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } public String getAge() { return age; } public void setAge(String age) { this.age = age; notifyPropertyChanged(BR.age); }}
ObservableStudentOther直接利用ObservableXX来定义属性,就不需要继承BaseObservable了代码如下:
public class ObservableStudentOther { public final ObservableField<String> name=new ObservableField<>(); public final ObservableField<String> age=new ObservableField<>();}但是使用赋值的时候需要:ObservableStudentOther obj=new ObservableStudentOther(); obj.name.set(Value)
Observable双向绑定的布局代码:
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <!-- 在定义List或者Map泛型的时候<表示文本泛型的开始最后还要加一个>表示泛型定义的结束,千万别忘了最后这个>要不然会报错的 --> <import type="android.databinding.ObservableArrayList"></import> <import type="android.databinding.ObservableArrayMap"></import> <variable name="stuOne" type="com.xinran.qxdatabinding.beans.ObservableStudent"></variable> <variable name="stuoTo" type="com.xinran.qxdatabinding.beans.ObservableStudentOther"></variable> <variable name="list" type="ObservableArrayList<String>"/> <variable name="map" type="ObservableArrayMap<String, String>"/> </data><LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:orientation="vertical"> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{@string/nameFormatWithAge(stuOne.name,stuOne.age,18)}"/> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{@string/nameFormatWithAge(stuoTo.name,stuoTo.age,28)}"/> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{list[1]}"/> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="@{map['1']}"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="changeName" android:text="Change"/></LinearLayout></layout>
不要<data>节点直接给每个view定义一个id,DataBinding会帮我们根据id的名字生成一一对应的绑定的属性名字,我们根据这个属性名字也能找到对应view,代码如下:
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <!-- <data> 如果每个view布局的时候设置了id,我们通过DataBindingUtil.setContentView(Actvity,布局Id)的时候 已经把Actvity跟它使用使用的布局关联起来,我们直接利用binding.id名字就可以直接找到对应的布局控件 注:名字生成规则:根据布局中的id命名的下划线为节点,自动生成驼峰式的名字,如本例tv_name对应tvName: ActivityViewWithIdBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_view_with_id); binding.tvName.setText("Mack"); binding.tvAge.setText("22"); binding.tvName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { binding.tvAge.setText("点击了TvName"); } }); </data> --> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="20dp" android:layout_marginRight="20dp"> <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_age" android:layout_width="wrap_content" android:layout_below="@+id/tv_name" android:layout_height="wrap_content" /> </RelativeLayout></layout>
public class ViewWithIdActvity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //DataBindingUtil.setContentView(this, R.layout.activity_view_with_id)这句代码把布局跟Actvity和DataBinding //给关联上了,如想知道具体细节可以看DataBindingUtil.setContentView()源码细节 final ActivityViewWithIdBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_view_with_id); //DataBinding会根据布局每个控件中的id名字自动生成对应的属性名字,并且一一对应,所以我们可以直接引用 binding.tvName.setText("Mack"); binding.tvAge.setText("22"); binding.tvName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { binding.tvAge.setText("点击了TvName"); } }); }}
Databinding延迟加载并且只能inflate一次的ViewStub的使用:
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > <Button android:text="Inflate the ViewStub" android:onClick="inflateViewStub" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ViewStub android:id="@+id/view_stub" android:layout="@layout/view_stub" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout></layout>
public class ViewStubActivity extends BaseActivity { private ActivityViewStubBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub); mBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() { @Override public void onInflate(ViewStub stub, View inflated) { ViewStubBinding binding = DataBindingUtil.bind(inflated); User user = new User("liang", "fei"); binding.setUser(user); } }); } /** * Don't panic for red error reporting. Just ignore it and run the app. Surprise never ends. */ public void inflateViewStub(View view) { if (!mBinding.viewStub.isInflated()) { mBinding.viewStub.getViewStub().inflate(); } }}
Dynamic动态绑定view,代码使用RecycerView来掩饰,因为它涉及到Adapter中绑定数据,代码如下:
public class StudentAdapter extends RecyclerView.Adapter<StudentHolder> { private List<Student> list; public StudentAdapter(List<Student> list){ this.list=list; } @Override public StudentHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View view= LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_student_adapter,viewGroup,false); return new StudentHolder(view); } @Override public void onBindViewHolder(StudentHolder studentHolder, int i) { //因为在StudentHolder中已经把itemVie布局跟binding绑定关联,所以这里设置的数据可以到布局中 studentHolder.bindData(list.get(i)); } @Override public int getItemCount() { return list.size(); }}
public class StudentHolder extends RecyclerView.ViewHolder{ private ItemStudentAdapterBinding binding; public StudentHolder(View itemView) { super(itemView); //在ViewHolder中使itemView跟相应的binding进行关联 binding= DataBindingUtil.bind(itemView); } public void bindData(Student stu){ //关联后就可以为他设值 binding.setStu(stu); }}
public class DynamicActvity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityDynamicBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_dynamic); //根据布局控件的id直接通过binding的点语法,得到相应的控件(因为DataBinding根据布局id生成了final属性变量) binding.recycerView.setLayoutManager(new LinearLayoutManager(this));// binding.recycerView.setOnScrollChangeListener(new View.OnScrollChangeListener() {// @Override// public void onScrollChange(View view, int i, int i1, int i2, int i3) {// Log.e("eeee:", "onScroll");// }// }); binding.recycerView.setAdapter(new StudentAdapter(mockData())); } private List<Student> mockData(){ List<Student> list=new ArrayList<>(); Student stu=null; for(int i=0;i<10;i++){ stu=new Student("Yang"+i,"mack"+i,i); list.add(stu); } return list; }}
使用DataBinding自定义布局,自定义属性时不用在style.xml声明<declear-stylable>标签声明自己想要的各种<attr/>,代码如下:
public class TabLayout extends LinearLayout { private float textSize; private TextView tv1; private TextView tv2; private ImageView imageView; public TabLayout(Context context) { this(context, null); } public TabLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TabLayout); textSize = typedArray.getDimension(R.styleable.TabLayout_tvSize, 12f); if (typedArray != null) { typedArray.recycle(); } init(); } private void init() { inflate(this.getContext(), R.layout.layout_custom, this); tv1 = (TextView) findViewById(R.id.first); tv2 = (TextView) findViewById(R.id.second); tv1.setTextSize(textSize); } //set方法名字的后缀名字 布局中app:后面的属性名字一致,DataBinding就会自动根据 //app:后面的属性名字找自定义布局中对应的方法调用的,并把值传过来,就不用声明declare-stylable属性了 //效果是一样的 public void setTvFirst(String firstTabName) { tv1.setText(firstTabName); } public void setTvColor(int color){ tv1.setTextColor(color); } public void setTvSecond(String firstTabName) { tv2.setText(firstTabName); } public void setImgIcon(int resid){ imageView.setImageResource(resid); }}
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"><!-- //在TabLayout类中声明app:后属性名字相同的后缀set方法就好 比如app:tvFirst就对应setTvFirst(String str)这个set方法--><com.xinran.qxdatabinding.views.TabLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" app:tvFirst="@{@string/firstName}" app:tvSecond="@{@string/lastName}" app:tvSize="@dimen/text_big" app:tvColor="@{@color/red_1}"></com.xinran.qxdatabinding.views.TabLayout></layout>
使用<declear-stylable>标签自定义的tvSize属性:
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> </style><declare-styleable name="TabLayout"> <attr name="tvSize" format="dimension"></attr></declare-styleable></resources>
- Android DataBinding 学习笔记
- 学习笔记--databinding
- DataBinding学习笔记
- Android之Databinding学习笔记
- DataBinding学习笔记(一)源码分析
- Android学习笔记之--------DataBinding使用一
- Kotlin学习笔记——使用databinding
- Android 官方框架DataBinding学习笔记
- DataBinding 学习
- Android学习笔记之MVVM----DataBinding(数据双向绑定)
- DataBinding使用笔记一
- DataBinding的一些笔记
- Android databinding笔记
- DataBinding笔记一
- Android DataBinding 入门笔记
- DataBinding基本功能使用笔记
- DataBinding学习(一)
- DataBinding学习(二)
- Effective C++
- UEFI引导系统
- MFC dll 类型是 共享 规则 导出对话框函数使用出错解决办法
- java程序设计语言采用的是按值传递的调用方法。
- Runtime之关联对象简述
- DataBinding学习笔记
- Android项目快速编译之Freeline-Android的配置与集成
- C++必知必会
- Jmeter:jp@gc - PerfMon Metrics Collector指标说明
- ORACLE基本数据类型总结
- php调用C语言生成的so文件
- git遇到的诡异错误: Failed connect to github.com:443
- 为什么每个程序员都应该学习C语言?
- 认识Html标签