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泛型的时候&lt;表示文本泛型的开始最后还要加一个>表示泛型定义的结束,千万别忘了最后这个>要不然会报错的        -->        <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&lt;String>"/>        <variable            name="map"            type="ObservableArrayMap&lt;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>

0 0
原创粉丝点击