Android Data Binding 高级用法

来源:互联网 发布:世界十大灾难片 知乎 编辑:程序博客网 时间:2024/06/05 15:01

承接上一篇博客,Android Data Binding入门,这篇博客来看看Data Binding的高级用法。

1. 列表绑定

在Android中,列表是展示内容的最好方式,比如ListView、GridView、RecyclerView。前面我也写了一篇博客,介绍了RecyclerView的用法,请参考Android RecyclerViews实现下拉列表功能。这里用DataBinding绑定RecyclerView来实现一个列表,具体细节不在赘述,方法如下:

ListActivity.java

package com.jackie.sample.databinding;import android.databinding.DataBindingUtil;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.LinearLayoutManager;import android.view.View;import android.widget.Toast;import com.jackie.sample.databinding.databinding.ActivityListBinding;import java.util.ArrayList;import java.util.List;/** * Created by Administrator on 2016/10/29. */public class ListActivity extends AppCompatActivity {    private ActivityListBinding mBinding;    private EmployeeAdapter mAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_list);        mBinding.setPresenter(new Presenter());        mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(this));        mAdapter = new EmployeeAdapter(this);        mBinding.recyclerView.setAdapter(mAdapter);        mAdapter.setListener(new EmployeeAdapter.OnItemClickListener() {            @Override            public void onItemClick(Employee employee) {                Toast.makeText(ListActivity.this, employee.getFirstName(), Toast.LENGTH_SHORT).show();            }        });        List<Employee> employeeList = new ArrayList<>();        employeeList.add(new Employee("Cheng1", "Jackie1", false));        employeeList.add(new Employee("Cheng2", "Jackie2", false));        employeeList.add(new Employee("Cheng3", "Jackie3", true));        employeeList.add(new Employee("Cheng4", "Jackie4", false));        mAdapter.addAll(employeeList);    }    public class Presenter {        public void onClickAddItem(View view) {            mAdapter.add(new Employee("Huang", "Ashia", false));        }        public void onClickRemoveItem(View view) {            mAdapter.remove();        }    }}
EmployeeAdapter.java
package com.jackie.sample.databinding;import android.content.Context;import android.databinding.DataBindingUtil;import android.databinding.ViewDataBinding;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import java.util.ArrayList;import java.util.List;import java.util.Random;/** * Created by Administrator on 2016/10/29. */public class EmployeeAdapter extends RecyclerView.Adapter<BindingViewHolder> {    private LayoutInflater mInflater;    private OnItemClickListener mListener;    private List<Employee> mEmployeeList;    public static final int ITEM_VIEW_TYPE_ON = 1;    public static final int ITEM_VIEW_TYPE_OFF = 2;    public void setListener(OnItemClickListener listener) {        this.mListener = listener;    }    public interface OnItemClickListener {        void onItemClick(Employee employee);    }    public EmployeeAdapter(Context context) {        mInflater = LayoutInflater.from(context);        mEmployeeList = new ArrayList<>();    }    @Override    public int getItemViewType(int position) {        Employee employee = mEmployeeList.get(position);        if (employee.isFired()) {            return ITEM_VIEW_TYPE_OFF;        } else {            return ITEM_VIEW_TYPE_ON;        }    }    @Override    public BindingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        ViewDataBinding binding;        if (viewType == ITEM_VIEW_TYPE_ON) {            binding = DataBindingUtil.inflate(mInflater, R.layout.item_employee_on, parent, false);        } else {            binding = DataBindingUtil.inflate(mInflater, R.layout.item_employee_off, parent, false);        }        return new BindingViewHolder(binding);    }    @Override    public void onBindViewHolder(BindingViewHolder holder, int position) {        final Employee employee = mEmployeeList.get(position);        holder.getBinding().setVariable(com.jackie.sample.databinding.BR.item_employee, employee);        holder.getBinding().executePendingBindings();        holder.itemView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                if (mListener != null) {                    mListener.onItemClick(employee);                }            }        });    }    @Override    public int getItemCount() {        return mEmployeeList.size();    }    public void addAll(List<Employee> employeeList) {        mEmployeeList.addAll(employeeList);    }    public void add(Employee employee) {        int position = new Random().nextInt(mEmployeeList.size()) + 1;        mEmployeeList.add(position, employee);        notifyItemInserted(position);    }    public void remove() {        if (mEmployeeList.size() == 0) {            return;        }        int position = new Random().nextInt(mEmployeeList.size());        mEmployeeList.remove(position);        notifyItemRemoved(position);    }}
BindingViewHolder.java
package com.jackie.sample.databinding;import android.databinding.ViewDataBinding;import android.support.v7.widget.RecyclerView;/** * Created by Administrator on 2016/10/29. */public class BindingViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {    private T mBinding;    public BindingViewHolder(T binding) {        super(binding.getRoot());        mBinding = binding;    }    public T getBinding() {        return mBinding;    }}

activity_list.xml

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools">    <!-- 绑定 -->    <data>        <variable            name="presenter"            type="com.jackie.sample.databinding.ListActivity.Presenter">        </variable>    </data>    <LinearLayout        android:id="@+id/activity_list"        android:layout_width="match_parent"        android:layout_height="wrap_content"        tools:context="com.jackie.sample.databinding.ListActivity"        android:orientation="vertical">        <Button            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:onClick="@{ presenter.onClickAddItem }"            android:text="ADD"/>        <Button            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:onClick="@{ presenter.onClickRemoveItem }"            android:text="REMOVE"/>        <android.support.v7.widget.RecyclerView            android:id="@+id/recycler_view"            android:layout_width="match_parent"            android:layout_height="wrap_content"/>    </LinearLayout></layout>

效果如下:


2.自定义属性

默认的android命名空间下,我们会发现并不是所有的属性都能直接通过data binding进行设置,比如margin,padding,还有自定义View的各种属性。遇到这些属性,我们就需要自己去定义它们的绑定方法。
Setter
就像Data Binding会自动去查找get方法一下,在遇到属性绑定的时候,它也会去自动寻找对应的set方法。拿DrawerLayout举一个例子:
</android.support.v4.widget.drawerlayout>
如此,通过使用app命名空间,data binding就会去根据属性名字找对应的set方法,scrimColor -> setScrimColor:

public void setScrimColor(@ColorInt int color) {    mScrimColor = color;    invalidate();}

如果找不到的话,就会在编译期报错。
利用这种特性,对一些第三方的自定义View,我们就可以继承它,来加上我们的set函数,以对其使用data binding。

比如Fresco的SimpleDraweeView,我们想要直接在xml指定url,就可以加上:

public void setUrl(String url) {    view.setImageURI(TextUtils.isEmpty(url) ? null : Uri.parse(url));}

这般,就能直接在xml中去绑定图片的url。这样是不是会比较麻烦呢,而且有一些系统的View,难道还要继承它们然后用自己实现的类?其实不然,我们还有其他方法可以做到自定义属性绑定。
BindingMethods
如果View本身就支持这种属性的set,只是xml中的属性名字和java代码中的方法名不相同呢?难道就为了这个,我们还得去继承View,使代码产生冗余?
当然没有这么笨,这时候我们可以使用BindingMethods注释。
android:tint是给ImageView加上着色的属性,可以在不换图的前提下改变图标的颜色。如果我们直接对android:tint使用data binding,由于会去查找setTint方法,而该方法不存在,则会编译出错。而实际对应的方法,应该是setImageTintList。

这时候我们就可以使用BindingMethod指定属性的绑定方法:

@BindingMethods({@BindingMethod(type = “android.widget.ImageView”,                     attribute = “android:tint”,                      method = “setImageTintList”),})

我们也可以称BindingMethod为Setter重命名。
BindingAdapter
如果没有对应的set方法,或者方法签名不同怎么办?BindingAdapter注释可以帮我们来做这个。
比如View的android:paddingLeft属性,是没有对应的直接进行设置的方法的,只有setPadding(left, top, right, bottom),而我们又不可能为了使用Data Binding去继承修改这种基础的View(即便修改了,还有一堆继承它的View呢)。又比如那些margin,需要修改必须拿到LayoutParams,这些都无法通过简单的set方法去做。

这时候我们可以使用BindingAdapter定义一个静态方法:

@BindingAdapter("android:paddingLeft")public static void setPaddingLeft(View view, int padding) {    view.setPadding(padding,                    view.getPaddingTop(),                    view.getPaddingRight(),                    view.getPaddingBottom());}

事实上这个Adapter已经由Data Binding实现好了,可以在android.databinding.adapters.ViewBindingAdapter看到有很多定义好的适配器,还有BindingMethod。如果需要自己再写点什么,仿照这些来写就好了。

我们还可以进行多属性绑定,比如:

@BindingAdapter({"bind:imageUrl", "bind:error"})public static void loadImage(ImageView view, String url, Drawable error) {   Picasso.with(view.getContext()).load(url).error(error).into(view);}

来使用Picasso读取图片到ImageView。
BindingConversion
有时候我们想在xml中绑定的属性,未必是最后的set方法需要的,比如我们想用color(int),但是view需要Drawable,比如我们想用String,而view需要的是Url。这时候我们就可以使用BindingConversion:

<view :="" android:background="“@{isError" android:layout_height="“wrap_content”/" android:layout_width="“wrap_content”" color="" red=""></view>@BindingConversion    public static ColorDrawable convertColorToDrawable(int color) {        return new ColorDrawable(color);
3.双向绑定

自定义Listener过去,我们需要自己定义Listener来做双向绑定:

<edittext android:aftertextchanged="“@{callback.change}”/" android:text="“@{user.name}”"></edittext>public void change(Editable s) {    final String text = s.toString();    if (!text.equals(name.get()) {        name.set(text);    }}

需要自己绑定afterTextChanged方法,然后检测text是否有改变,有改变则去修改observable。
新方式 - @=

现在可以直接使用@=(而不是@)来进行双向绑定了,使用起来十分简单:

<pre name="code" class="html"><edittext android:inputtype="textNoSuggestions" android:layout_height="wrap_content" android:layout_width="match_parent" android:text="@={model.name}"></edittext>

这样,我们对这个EditText的输入,就会自动set到对应model的name字段上。

实现如下:

activity_two_way.xml

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools">    <data>        <variable            name="model"            type="com.jackie.sample.databinding.FormModel"/>    </data>    <LinearLayout        android:id="@+id/activity_two_way"        android:layout_width="match_parent"        android:layout_height="match_parent"        tools:context="com.jackie.sample.databinding.TwoWayActivity"        android:orientation="vertical">        <EditText            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:inputType="textNoSuggestions"            android:text="@={model.username}"/>        <EditText            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:inputType="textPassword"            android:text="@={model.password}"/>        <Button            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:text="@{model.username}"/>    </LinearLayout></layout>

TwoWayActivity.java

package com.jackie.sample.databinding;import android.databinding.DataBindingUtil;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import com.jackie.sample.databinding.databinding.ActivityTwoWayBinding;/** * Created by Administrator on 2016/10/29. */public class TwoWayActivity extends AppCompatActivity {    private ActivityTwoWayBinding mBinding;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_two_way);        mBinding.setModel(new FormModel("jackie.cheng", "123456"));    }}

FormModel.java

package com.jackie.sample.databinding;import android.databinding.BaseObservable;import android.databinding.Bindable;/** * Created by Administrator on 2016/10/29. */public class FormModel extends BaseObservable {    private String username;    private String password;    @Bindable    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;        notifyPropertyChanged(com.jackie.sample.databinding.BR.username);    }    @Bindable    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;        notifyPropertyChanged(com.jackie.sample.databinding.BR.password);    }    public FormModel(String username, String password) {        this.username = username;        this.password = password;    }}

效果如下:


3.Lambda表达式

在入门篇中有提到,可以参考。

4.动画

activity_animation.xml

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools">    <data>        <import type="android.view.View"/>        <variable            name="presenter"            type="com.jackie.sample.databinding.AnimationActivity.Presenter"/>        <variable            name="showImage"            type="boolean"/>    </data>    <LinearLayout        android:id="@+id/activity_animation"        android:layout_width="match_parent"        android:layout_height="wrap_content"        tools:context="com.jackie.sample.databinding.AnimationActivity"        android:orientation="vertical">        <ImageView            android:layout_width="100dp"            android:layout_height="100dp"            android:visibility="@{showImage ? View.VISIBLE : View.GONE}"            android:src="@mipmap/ic_launcher"/>        <CheckBox            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:onCheckedChanged="@{presenter.onCheckedChanged}"            android:text="显示图片"/>    </LinearLayout></layout>
添加动画:


0 0
原创粉丝点击