Android官方数据绑定框架DataBinding(三)

11、 Data Binding VS RecyclerView 
有了上面的思路,你们是否是也会在ListView和RecyclerView中使用了?咱们仅以一个RecyclerView来学习一下。 
首先来看看item的布局,java

<layout xmlns:android="http://schemas.android.com/apk/res/android">  
  
    <data>  
        <variable  
            name="stu"  
            type="org.loader.app6.Student" />  
    </data>  
  
    <RelativeLayout  
        android:layout_width="match_parent"  
        android:layout_height="match_parent">  
  
        <TextView  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="@{stu.name}"  
            android:layout_alignParentLeft="true"/>  
  
        <TextView  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="@{String.valueOf(stu.age)}"  
            android:layout_alignParentRight="true"/>  
  
    </RelativeLayout>  
</layout>

能够看到,仍是用了那个Student实体,这样得代码,相信你也已经看烦了吧。 
那咱们来看看activity的。android

private RecyclerView mRecyclerView;  
private ArrayList<Student> mData = new ArrayList<Student>() {  
    {  
        for (int i=0;i<10;i++) add(new Student("loader" + i, 18 + i));  
    }  
};  
  
@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
  
    mRecyclerView = (RecyclerView) findViewById(R.id.recycler);  
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this,  
            LinearLayoutManager.VERTICAL, false));  
    mRecyclerView.setAdapter(new MyAdapter(mData));  
}

这里给RecyclerView设置了一个Adapter,相信最主要的代码就在这个Adapter里。数组

private class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {  
  
    private ArrayList<Student> mData = new ArrayList<>();  
  
    private MyAdapter(ArrayList<Student> data) {  
        mData.addAll(data);  
    }  
  
    @Override  
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {  
        ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater  
                .from(viewGroup.getContext()), R.layout.item, viewGroup, false);  
        ViewHolder holder = new ViewHolder(binding.getRoot());  
        holder.setBinding(binding);  
        return holder;  
    }  
  
    @Override  
    public void onBindViewHolder(ViewHolder viewHolder, int i) {  
        viewHolder.getBinding().setVariable(org.loader.app6.BR.stu, mData.get(i));  
        viewHolder.getBinding().executePendingBindings();  
    }  
  
    @Override  
    public int getItemCount() {  
        return mData.size();  
    }  
  
    class ViewHolder extends RecyclerView.ViewHolder {  
  
        private ViewDataBinding binding;  
  
        public ViewHolder(View itemView) {  
            super(itemView);  
        }  
  
        public void setBinding(ViewDataBinding binding) {  
            this.binding = binding;  
        }  
  
        public ViewDataBinding getBinding() {  
            return this.binding;  
        }  
    }

果真,这个adapter的写法和咱们以前的写法不太同样,首先看看ViewHolder,在这个holder里,咱们保存了一个ViewDataBinding对象,并给它提供了GetterSetter方法, 这个ViewDataBinding是干吗的?咱们稍后去讲。继续看看onCreateViewHolder,在这里面,咱们首先调用DataBindingUtil.inflate方法返回了一个ViewDataBinding的对象,这个ViewDataBinding是个啥?咱们之前没见过啊,这里告诉你们咱们以前返回的那些都是ViewDataBinding的子类!继续看代码,咱们new了一个holder,参数是确定是咱们的item布局了,继续看,接着咱们又把binding设置给了holder,最后返回holder。这时候,咱们的holder里就保存了刚刚返回的ViewDataBinding对象,干吗用呢?继续看onBindViewHolder就知道了。网络

@Override  
public void onBindViewHolder(ViewHolder viewHolder, int i) {  
    viewHolder.getBinding().setVariable(org.loader.app6.BR.stu, mData.get(i));  
    viewHolder.getBinding().executePendingBindings();  
}

只有两行代码,可是都是咱们没有见过的,首先第一行,咱们之前都是使用相似binding.setStu这样方法去设置变量,那这个setVariable呢? 为何没有setStu,这里要记住,ViewDataBinding是咱们以前用的那些binding的父类,只有自动生成的那些子类才会有setXXX方法,那如今咱们须要在ViewDataBinding中设置变量咋办?这个类为咱们提供了setVariable去设置变量,第一个参数是咱们的变量名的引用,第二个是咱们要设置的值。第二行代码,executePendingBindings的做用是干吗的?官方的回答是:
当数据改变时,binding会在下一帧去改变数据,若是咱们须要当即改变,就去调用executePendingBindings方法。app

因此这里的做用就是去让数据的改变当即执行。 
ok,如今看起来,咱们的代码更加简洁了,并且不须要保存控件的实例,是否是很爽? 来看看效果:框架

12、 View with ID 
在使用Data Binding的过程当中,咱们发现并无保存View的实例,可是如今咱们有需求须要这个View的实例咋办?难道走老路findViewById?固然不是啦,当咱们须要某个view的实例时,咱们只要给该view一个id,而后Data Binding框架就会给咱们自动生成该view的实例,放哪了?固然是ViewDataBinding里面。 
上代码:ide

<layout xmlns:android="http://schemas.android.com/apk/res/android">  
    <data class=".Custom">  
        <variable  
            name="str"  
            type="android.databinding.ObservableField<String>" />  
        <variable  
            name="handler"  
            type="org.loader.app7.MainActivity" />  
    </data>  
  
    <TextView  
        android:id="@+id/textView"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="@{str.get}"  
        android:onClick="@{handler.click}"/>  
</layout>

xml中代码没有什么好说的,都是以前的代码,若是在这有点迷糊,建议你仍是回头看看上篇博客。须要注意的是, 
咱们给TextView了一个id-textView。 
activity,布局

public class MainActivity extends AppCompatActivity {  
  
    private org.loader.app7.Custom mBinding;  
    private ObservableField<String> mString;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        mBinding = DataBindingUtil.setContentView(this,  
                R.layout.activity_main);  
        mString = new ObservableField<String>();  
        mString.set("loader");  
        mBinding.setStr(mString);  
        mBinding.setHandler(this);  
    }  
  
    public void click(View view) {  
        mString.set("qibin");  
        mBinding.textView.setTextColor(Color.GREEN);  
    }  
}

经过ViewDataBinding类的实例直接去获取的。学习

只要咱们给了view一个id,那么框架就会在ViewDataBinding中自动帮咱们保存这个view的实例,变量名就是咱们设置的id。this

十3、 自定义setter 
想一想这样的一种情景,一个ImageView须要经过网络去加载图片,那咱们怎么办?看似好像使用DataBinding不行,恩,咱们上面所学到东西确实不可以解决这个问题,可是DataBinding框架给咱们提供了很好的扩展,容许咱们自定义setter,那该怎么作呢?这里就要引出另外一个知识点——BindingAdapter,这是一个注解,参数是一个数组,数组中存放的是咱们自定义的’属性’。接下来就以一个例子学习一下BindingAdapter的使用。

<layout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:app="http://schemas.android.com/apk/res-auto">  
    <data class=".Custom">  
        <variable  
            name="imageUrl"  
            type="String" />  
    </data>  
  
    <ImageView  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        app:image="@{imageUrl}"/>  
</layout>

这里咱们增长了一个命名空间app,而且注意ImageView的app:image属性,这里和咱们自定义view时自定义的属性同样,可是这里并不须要咱们去重写ImageView,这条属性的值是咱们上面定义的String类型的imageUrl,从名称中看到这里咱们可能会塞给他一个url。 
activity,

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        org.loader.app8.Custom binding = DataBindingUtil.setContentView(this,  
                R.layout.activity_main);  
        binding.setImageUrl("http://images.csdn.net/20150810/Blog-Image%E5%89%AF%E6%9C%AC.jpg");  
    }  
}

果真在这里咱们set了一个url,那图片怎么加载呢?这里就要使用到咱们刚才说的BindingAdapter注解了。

public class Utils {  
    @BindingAdapter({"bind:image"})  
    public static void imageLoader(ImageView imageView, String url) {  
        ImageLoaderUtils.getInstance().displayImage(url, imageView);  
    }  
}

咱们定义了一个Utils类,这个类你能够随便起名,该类中只有一个静态的方法imageLoader,该方法有两个参数,一个是须要设置数据的view, 
一个是咱们须要的url。值得注意的是那个BindingAdapter注解,看看他的参数,是一个数组,内容只有一个bind:image,仅仅几行代码,咱们不须要 
手工调用Utils.imageLoader,也不须要知道imageLoader方法定义到哪了,一个网络图片加载就搞定了,是否是很神奇,这里面起关键做用的就是BindingAdapter 
注解了,来看看它的参数怎么定义的吧,难道是乱写?固然不是,这里要遵循必定的规则,

以bind:开头,接着书写你在控件中使用的自定义属性名称。

这里就是image了,不信来看。

<ImageView  
    android:layout_width="match_parent"  
    android:layout_height="wrap_content"  
    app:image="@{imageUrl}"/>

看看运行结果:

十4、 Converters 
Converter是什么呢?举个例子吧:假如你的控件须要一个格式化好的时间,可是你只有一个Date类型额变量咋办?确定有人会说这个简单,转化完成后在设置,恩,这也是一种办法,可是DataBinding还给咱们提供了另一种方式,虽然原理同样,可是这种方式使用的场景更多,那就是——Converter。和上面的BindingAdapter使用方法同样,这也是一个注解。下面仍是以一段代码的形式进行学习。

<layout xmlns:android="http://schemas.android.com/apk/res/android">  
    <data class=".Custom">  
        <variable  
            name="time"  
            type="java.util.Date" />  
    </data>  
  
    <TextView  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="@{time}"/>  
</layout>

看TextView的text属性,咱们须要一个String类型的值,可是这里确给了一个Date类型的,这就须要咱们去定义Converter去转换它, 
activity,

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        org.loader.app9.Custom binding = DataBindingUtil.setContentView(this,  
                R.layout.activity_main);  
        binding.setTime(new Date());  
    }  
}

去给这个Date类型的变量设置值。怎么去定义Converter呢? 看代码:

public class Utils {  
  
    @BindingConversion  
    public static String convertDate(Date date) {  
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
        return sdf.format(date);  
    }  
}

和上面同样,咱们不须要关心这个convertDate在哪一个类中,重要的是他的@BindingConversion注解,这个方法接受一个Date类型的变量,正好咱们的android:text设置的就是一个Date类型的值,在方法内部咱们将这个Date类型的变量转换成String类型的日期而且返回。这样UI上就显示出咱们转化好的字符串。 
看看效果:

好了,到这里DataBinding的知识咱们就算学习完了,在学完以后发现这东西也没什么难度,学会使用就ok了,并且android官网也有很是详细的文档

相关文章
相关标签/搜索