本文的目标是阐述BindingAdapter使用方法并以点击事件为例
在介绍它之前,我们先讲一下,布局文件中的系统属性android:onClick="" ,不知道你有没有用过,其中双引号中间就是你要触发的事件名称。不需要用findViewById获取对应控件,然后再监听click事件,若是你用过请略过,若是没有过,我们看看怎么用的。
举个例子: 在布局文件中设置android:onClick=“clickEvent”
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="clickEvent" android:src="@drawable/tes" />在activity中新建一个clickEvent函数:
public void clickEvent(View v) { Toast.makeText(this, "test", Toast.LENGTH_LONG).show(); }这样就直接可以监听ImageView的单击事件,这样是不是很方便,为啥要介绍这个呢,是因为本文BindingAdapter也要采用同样的方式来实现,只是我们不采用系统的定义的属性,而是自己定义属性。
首先要讲一下attrs.xml,它是res/values目录下的一个文件,为什么讲它?因为它跟bindingadapter紧密相连。
自定义属性 自定义性有两个部分组成,第一,属性名,也就是名字,第二,属性值,也就是属性格式format。看下面的一段代码: <?xml version="1.0" encoding="utf-8"?> <resources> <attr name="backColor" format="color"> <enum name="redColor" value="1" /> <declare-styleable name="MyView"> <attr name="backColor"/> </declare-styleable> </resources>以attr开头的代表着具体某个属性。
以declare-styleable代表的某个组。例如我想为一个view自定义一个属性,假设这个view为MyView,则MyView下面定义所有属性都属于MyView组,凡是继承它的都可以使用。当然也可以为系统控件加上自定义属性。
系统属性和自定义属性是有区别的,属性名前面有“android:”都是系统属性。自定属性名没有没有什么好说的,尽量不要和系统定义的属性名雷同。
若是和系统定义的属性一样,不设置format的话,就沿用系统的。若是设置的话,就是对系统属性的重定义,但是有些系统属性格式是无法修改的。
简单介绍一下属性的format:
reference 引用 color 颜色 boolean 布尔 integer 整型 dimension float 浮点型 string 字符型 fraction 百分比 enum 枚举型 flag
自定义属性搞定了,其实没什么难度,那么接下来,我们要看看如何自定义属性和BindingAdapter建立连接的呢。其实也很简单,还是以一个例子来说明
首先,自定义属性,我们给系统的控制View自定义onClickCommand属性,只要属于View这个组的都可以使用这个属性。 <resources> <declare-styleable name="View"> <attr name="onClickCommand" format="reference" /> </declare-styleable> </resources>这个属性name,就是BindingAdapter的关键。
其次,BindingAdapter它的一般格式为:
@BindingAdapter(value={“属性名1”,“属性名2”}) public static void 函数名(参数){ //函数内容 }
具体的实例如下:新建一个文件为ViewAdapter.java,并类中添加如下代码
@BindingAdapter(value = {"onClickCommand"}, requireAll = false) public static void click(View view, final ClickCallBack bindingCommand) { view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (bindingCommand != null) { bindingCommand.clickEvent(); Toast.makeText(view.getContext(), "it works", Toast.LENGTH_LONG).show(); } else { Toast.makeText(view.getContext(), "binding command is null", Toast.LENGTH_LONG).show(); } } }); }BindingAdapter中value要和attrs.xml中自定义属性保持一致,否则绑定不成功。
绑定的函数一定要是static的,函数名没有具体的要求,函数里面的第一个参数为要绑定对象,我们绑定的为View,那就是View;
后面的参数就是各个属性,在布局文件中一定要按顺序进行,否则可能会报错。
还需要在布局文件中应用这个属性
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/tes" binding:onClickCommand="@{viewmodel.mBindingCommand}" />到目前为止,代码层面是没有问题,但是还不能用,因为还要binding。
ActivityMainBinding binding = DataBindingUtil.setContentView(this, getLayoutResId());其中ActivityMainBinding是系统自动生成的,这一步实现布局文件和属性之间进行绑定,也就是可以实现单击事件了。
最后,我们不仅要是实现事件绑定还能在ViewModel中回调,因为需要对事件进行业务的处理
在ViewModel中,需要具体实现BindingAdapter的回到,如下:
public ClickCallBack mBindingCommand = new ClickCallBack() { @Override public void clickEvent() { Log.e("tag", "click"); } };只需要在布局文件中绑定这个回调即可生效。
但是你发现回调对象为空,因为还需要设置
binding.setVariable(BR.viewmodel, mViewModel);这样才是完整的BindingAdapter过程。
值得注意的是binding.setVariable(BR.viewmodel, mViewModel);必须在创建ViewModel之后,否则你绑定回调参数为空,因为ViewModel没有创建为空,可以看自动生成的代码,自动生成的代码也可调试。
若是你想统一对ImageView都用Glide进行加载图片,你就可以通过这个中方式实现
@BindingAdapter({"imageUrl"}) public static void loadImage(ImageView view, String u) { RequestOptions options = new RequestOptions() .centerCrop() .placeholder(R.mipmap.ic_launcher_round) .error(R.mipmap.ic_launcher) .priority(Priority.HIGH) .diskCacheStrategy(DiskCacheStrategy.NONE); Glide.with(view.getContext()).applyDefaultRequestOptions(options).load(u).transition(new DrawableTransitionOptions().crossFade(1000)).into(view); }代码量会很少,效率会很高。