您的位置:首页 > 移动开发 > Android开发

Android Data Binding 高级用法

2016-10-29 14:45 381 查看
承接上一篇博客,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>
添加动画:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: