DataBinding基本使用(3)

咱们什么都不说 先附上知识点

  • 数据绑定的几种方式
  • 点击事件绑定的几种方式
  • 双向绑定数据
  • RecycleView的数据绑定
  • @InverseMethod 标签的使用
    在DataBinding基本使用(2)中已经讲解了数据绑定的几种方式和点击事件绑定的几种方式,可能本人在某些地方理解偏差或错误,往广大读者指出并一起讨论、学习。

双向绑定

请过了摸索和摸坑,总结的来说双向绑定分两种
1.系统自带属性的双向绑定
2.自定义属性的双向绑定

系统自带属性的双向绑定

首先要注意的是,双向绑定用的符号@={} 而不在是@{}
既然是系统自带属性的双向绑定,那么我就就不需要在属性的问题上纠结太久,需要修改的是数据源Bean

public class StudentBean extends BaseObservable {
    private String name = "";
    private int old = 0;
    private boolean studentis = false;
    @Bindable
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(com.example.scs.myapplication.BR.name);
    }
    @Bindable
    public int getOld() {
        return old;
    }
    public void setOld(int old) {
        this.old = old;
        notifyPropertyChanged(com.example.scs.myapplication.BR.old);
    }
    @Bindable
    public boolean isStudentis() {
        return studentis;
    }
    public void setStudentis(boolean studentis) {
        this.studentis = studentis;
        notifyPropertyChanged(com.example.scs.myapplication.BR.studentis);
    }
}

我们可以看到 在set方法上需要加上@Bindable的注解,在get方法里需要调用notifyPropertyChanged(com.example.scs.myapplication.BR.studentis);可以理解为把参数注册到DataBinding中

public class BR {
        public static final int _all = 0;
        public static final int changedata = 1;
        public static final int data = 2;
        public static final int main = 3;
        public static final int name = 4;
        public static final int old = 5;
        public static final int studentis = 6;
}

有的人会说,如果我有还多参数怎么吧?一个个写,一个个改不浪费时间吗?为此
DataBinding提供了响应式对象:
针对8种基本类型的数据结构提供了专门的包装类

  • ObservableBoolean
  • ObservableByte
  • ObservableChar
  • ObservableDouble
  • ObservableFloat
  • ObservableInt
  • ObservableLong
  • ObservableShort
    这样就代替了 繁琐的set/get
    public final ObservableField<String> name = new ObservableField<>();
    public final ObservableInt old = new ObservableInt();
    public final ObservableBoolean studemtis = new ObservableBoolean();

既然我们已经对数据惊喜了双向的处理,接下来就直接应用数据即可
既然要证明是双向的,我们多加了一个EditText去修改TextView的内容,看看对应的data是否改变

  <variable
            name="changedata"
            type="com.example.scs.myapplication.StudentBean"></variable>

        <variable
            name="main"
            type="com.example.scs.myapplication.Main2Activity"></variable>
  
        <TextView
            android:id="@+id/tv_change"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="@={changedata.name}"
            android:textSize="20dp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <EditText
                android:id="@+id/et_change"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_weight="1"
                android:hint="双向绑定--改变view数据" />

            <Button
                android:id="@+id/btn_1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:onClick="@{main.main2click}"
                android:text="修改" />
        </LinearLayout>

这边是java代码

bean = new StudentBean();
bean.setName("双向绑定--Bean(非自定义)");
binding.setChangedata(bean);
binding.setMain(this);
  
public void main2click(View view) {
        switch (view.getId()) {
            case R.id.btn_1://双向绑定数据 需要 @={}
                if (!TextUtils.isEmpty(binding.etChange.getText().toString())) {
                    binding.tvChange.setText(binding.etChange.getText().toString());
                    Toast.makeText(view.getContext(), bean.getName(), Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(view.getContext(), "不能为空", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.btn_2:
                Intent intent = new Intent(Main2Activity.this, Main3Activity.class);
                startActivity(intent);
                break;
        }
    }

大家伙儿,都可以去试一试,你们会发现对应的bean的数据也发送了改变。

bean------->(赋值)TextView
TextView(修改)------->对应的bean的对应的数据修改

自定义属性的双向绑定

一看标题就应该知道,自定义属性,我们需要去处理自定义的数据,和双向的数据。双向的数据的处理方法同上,这里就不在做过多的解释。咱们重点讲解自定义数据如何去实现。
这里我们需要去理解一下几个属性的含义和使用方法

  • InverseBindingMethods
  • InverseBindingMethod
  • InverseBindingAdapter
  • BindingAdapter

BindingAdapter

我们先来讲解一下BindingAdapter,它其实就是用来实现自定义属性所对应的方法

 <variable
            name="tv1data"
            type="String"></variable>

  <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:mytext="@{tv1data}"
            android:textSize="30dp" />

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        binding.setTv1data("tv1_data");//加载数据 way1}

 @BindingAdapter("android:mytext")
    public static void setMytext(TextView textView, String msg) {
        textView.setText(msg + "");
    }

上述代码就实现了,自定义属性的功能。

  • BindingAdapter 标签可以传两个参数

    1.完整的写法 @BindingAdapter(value = {"android:onItemClick", "android:onLoadMore","android:loadMoreEnable"}, requireAll = false) 其中value当然就是自定义的属性的名字,requireAll表示所有属性是否都要用,true表示都要使用,false表示不用
    2.自定义属性有几种表达形式
    a.android:xxxx @BindingAdapter(“android:xxxx”)
    b.app:xxxx @BindingAdapter(“app:xxxx”)
    c.app:xxxx @BindingAdapter(“xxxx”)
    使用b方法会报错,但是不影响编译

  • 我们首先要定义了一个android:mytext的属性,因为是自定义的属性,所以我们需要提供一个对应的set方法,并且该方法能够起到响应的效果的方法。

  • setMytext方法必须传一个对应控件的对象进去。其次我们在自定义的属性里加了个参数,故在方法中也需要添加对应类别的对象进去。

  • 方法必须要用static修饰

以上就是自定义属性的使用,举个例子我们可以对Imageview定义个自定义的属性,传入一个url地址,在对应的方法去给Imageview去设置图片

InverseBindingAdapter

  • InverseBindingAdapter需要传两个参数 attribute:String类型 event: String类型

1.attribute 必填,表示当值发生变化时,要从哪个属性中检索这个变化的值,示例:"android:text"

2.event 非必填;如果填写,则使用填写的内容作为event的值;如果不填,在编译时会根据attribute的属性名再加上后缀“AttrChanged”生成一个新的属性作为event的值,举个例子:attribute属性的值为”android:text”,那么默认会在”android:text”后面追加”AttrChanged”字符串,生成”android:textAttrChanged”字符串作为event的值.

3.event属性的作用: 当View的值发生改变时用来通知dataBinding值已经发生改变了。开发者一般需要使用@BindingAdapter创建对应属性来响应这种改变。
先上XML代码,因为要实现双向,所以加了输入框去修改数据,来提现双向

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="main"
            type="com.example.scs.myapplication.Main3Activity"></variable>
                <variable
                    name="data"
                    type="com.example.scs.myapplication.StudentBean"></variable>
            </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <com.example.scs.myapplication.MyEditText
            android:id="@+id/my_et"
            android:name="@={data.name}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/btn_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{main.onMainClick}"
            android:text="修改edittext" />
        <Button
            android:id="@+id/btn_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{main.onMainClick}"
            android:text="修改bean" />
    </LinearLayout>
</layout>

java代码


public class Main3Activity extends AppCompatActivity {
    ActivityMain3Binding binding;
    StudentBean bean;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main3);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main3);
        binding.setMain(this);
        bean = new StudentBean();
        bean.setName("name");
        binding.setData(bean);
    }

    public void onMainClick(View view) {
        switch (view.getId()) {
            case R.id.btn_1:
                if (!TextUtils.isEmpty(binding.myEt.getText().toString())) {
                    binding.myEt.setName(binding.myEt.getText().toString());
                    Toast.makeText(view.getContext(), bean.getName() + "", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.btn_2:
                bean.setName("修改Bean");
                break;
        }
    }
}

自定义MyEditText

public class MyEditText extends android.support.v7.widget.AppCompatEditText {
    private static String name = "";
    @InverseBindingAdapter(attribute = "android:name", event = "android:nameAttrChanged")
    public static String getName(MyEditText editText) {
        return editText.getText().toString();
    }
    @BindingAdapter(value = "android:name", requireAll = false)
    public static void setName(MyEditText editText, String setname) {
        if (setname == null)
            return;
        if (name.equals(setname))
            return;
        name = setname;
        editText.setText(setname + "");
    }
    @BindingAdapter(value = "android:nameAttrChanged")
    public static void setNameListenering(MyEditText editText, final InverseBindingListener listener) {
        if (listener != null) {
            editText.setOnNameListener(new OnNameListener() {
                @Override
                public void onName() {
                    listener.onChange();
                }
            });
        } else {
            editText.setOnNameListener(null);
        }
    }
    public void setName(String name) {
        if (name != null && !name.equals("")) {
            if (onNameListener != null) onNameListener.onName();
        }
    }
    public MyEditText(Context context) {
        super(context);
    }
    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    private OnNameListener onNameListener;
    public void setOnNameListener(OnNameListener onNameListener) {
        this.onNameListener = onNameListener;
    }
    public interface OnNameListener {
        void onName();
    }
}

我们可以看到 使用InverseBindingAdapter,必须要和BindingAdapter相结合使用。
不说其他,一个自定义的属性,必然要为其提供一个set/get方法。
我们先来看看InverseBindingAdapter标签 对应的方法就是getXXX对应自定义属性
提供完了get方法,还需要提供一个set方法,于是乎用BindingAdapter标签来定义一个set方法

到目前位置,我们似乎还有个变量还没被使用,event对应的属性,之前也说了event的作用就是用来监听自定义属性是否改变。所以我们也需要为event定义一个BindingAdapter属性,setNameListenering方法就是第二个参数就是DataBinding为我们生成的监听.

大家可以看到为什么还要写个OnNameListener接口,因为InverseBindingListener也需要被调用。

InverseBindingMethod与InverseBindingMethods

我们先来看看它们的使用方法
@InverseBindingMethods({
@InverseBindingMethod(type = MyEditText.class,attribute = "android:name",event = "android:nameAttrChanged",method = "android:getName")
})
说白了 InverseBindingMethod与InverseBindingMethod就是对上面自定义的一个总结

  • type 指的是自定义View的类
  • attribute 就是自定义的属性
  • event 上面已经解释,这里就不在多说
  • method 其实就是对应get方法

在回顾上面,是不是有属性的get/set方法?这么一看,有众一目了然的感觉

自定义属性的双向绑定和非自定义属性的双向绑定,就这么讲解完了,若有错误的地方请指出,大家共同进步

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,152评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,657评论 18 139
  • Android DataBinding Data Binding Library 从 2015 Google I/...
    灰灰鸽阅读 2,905评论 0 7
  • 素白 用来形容什么 离乡的愁绪 回家的温暖 还是前路的漫漫 五彩 用来形容什么 缤纷的世界 纸醉金迷 酒池肉林 还...
    栋赵阅读 248评论 4 4
  • 打破沉默的力量 今天是老李叔家嫁女的日子,按照村里习俗,得大摆筵席,一则宴请宾客,热闹庆祝,二则来的宾客往往要...
    齐打开阅读 261评论 1 0