本文默认读者有一定的Android开发经验,对Android Annotations和DataBinding技术也有了简单的了解。
文章通过三种不同方式代码的对比,最后总结说明为什么要使用DataBinding的技术。
功能
三种不同方式代码需要实现的功能是在登录界面里,通过监听用户名和密码输入框的文本变化,动态控制登录按钮点击状态。
第一种:普通实现
采用普通方式编写代码,可以发现会有很多的多余地方,大部分都是重复的工作:
- 实例化view:
findViewById(...)
- 添加文本监听:
addTextChangedListener(...)
- 设置点击事件:
setOnClickListener(...)
xml文件有两个EditText
和一个Button
,比较简单,这里就不贴代码了,只贴出Activity代码:
public class LoginNormalActivity extends AppCompatActivity {
private EditText nameEdit;
private EditText pwdEdit;
private Button loginBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
//实例化view
nameEdit = (EditText) findViewById(R.id.login_name_edit);
pwdEdit = (EditText) findViewById(R.id.login_pwd_edit);
loginBtn = (Button) findViewById(R.id.login_btn);
//添加文本变化监听
OnTextChangeListener textChangeListener = new OnTextChangeListener();
nameEdit.addTextChangedListener(textChangeListener);
pwdEdit.addTextChangedListener(textChangeListener);
//登录按钮点击事件监听
loginBtn.setOnClickListener(v -> Toast.makeText(this, "click login!", Toast.LENGTH_SHORT).show());
updateLoginEnable();
}
/**
* 更新登录按钮的状态
*/
private void updateLoginEnable() {
loginBtn.setEnabled(!(TextUtils.isEmpty(nameEdit.getText()) || TextUtils.isEmpty(pwdEdit.getText())));
}
/**
* 文本变化监听Listener
*/
private class OnTextChangeListener implements TextWatcher {
...
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//在文本变化结束后去更新
updateLoginEnable();
}
}
}
第二种:Android Annotations实现
注解方式编写代码,让你专注于真正重要的地方,使代码更加精简:
- 通过注解
@ViewById
@Click
@AfterTextChange
解决很多重复工作 - 在编译期通过APT生成一个新的类,命名规则是原始类名加下划线,没有使用反射,不会影响程序运行时的效率,但是新的编译出来的类会让增加你的认知,用起来稍有不爽。
xml文件有两个EditText
和一个Button
,比较简单,同样也就不贴代码了,只贴出Activity代码:
@EActivity(R.layout.login_activity)
public class LoginAnnotationActivity extends AppCompatActivity {
//实例化view
@ViewById(R.id.login_name_edit)
protected EditText nameEdit;
@ViewById(R.id.login_pwd_edit)
protected EditText pwdEdit;
@ViewById(R.id.login_btn)
protected Button loginBtn;
@AfterViews
protected void initView() {
updateLoginEnable();
}
/**
* 更新登录按钮的状态
*/
private void updateLoginEnable() {
loginBtn.setEnabled(!(TextUtils.isEmpty(nameEdit.getText()) || TextUtils.isEmpty(pwdEdit.getText())));
}
/**
* 登录点击回调
*/
@Click(R.id.login_btn)
protected void login(View view) {
Toast.makeText(this, "click login!", Toast.LENGTH_SHORT).show();
}
//添加文本变化监听
@AfterTextChange({R.id.login_pwd_edit, R.id.login_name_edit})
protected void afterTextChange(TextView tv, Editable text) {
//在文本变化结束后去更新
updateLoginEnable();
}
}
第三种:DataBinding实现
绑定方式:去除了冗余代码的基础上对数据和UI层进行解耦
- 通过
android:text="@={...}"
将数据双向绑定到UI中 - 通过
android:enabled="@{...}"
控制按钮状态 - 通过
android:onClick="@{...}"
直接处理用户操作事件 - 编译期同过APT生成辅助工具类,实现数据和UI的动态绑定
首先xml文件代码:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="loginViewHelper"
type="com.free.fastmvpdemo.login.LoginViewHelper" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:hint="@string/account_hint"
android:text="@={loginViewHelper.name}" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:hint="@string/pwd_hint"
android:text="@={loginViewHelper.pwd}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="@{loginViewHelper.canLogin(loginViewHelper.name,loginViewHelper.pwd)}"
android:onClick="@{loginViewHelper.login}"
android:text="@string/login" />
</LinearLayout>
</layout>
上面xml代码我们可以看出,数据绑定规则已经放在里面了,其实java代码的只需要处理业务相关的逻辑就好了,非常的清晰,然后Activity和辅助Helper代码:
public class LoginActivity extends AppCompatActivity {
//DataBinding自动生成的类,命名规则是取xml文件名加Binding结尾
LoginActivityBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
//初始化data bind,并设置Helper实例
binding = DataBindingUtil.setContentView(this, R.layout.login_activity);
binding.setLoginViewHelper(new LoginViewHelper());
}
}
public class LoginViewHelper {
//监听属性
public ObservableField<String> name = new ObservableField<>();
public ObservableField<String> pwd = new ObservableField<>();
/**
* 登录点击回调
*/
public void login(View view) {
Toast.makeText(view.getContext(), "click login!", Toast.LENGTH_SHORT).show();
}
/**
* 是否可以登录
*/
public boolean canLogin(String name, String pwd) {
return !(TextUtils.isEmpty(name) || TextUtils.isEmpty(pwd));
}
}
总结
- 普通方式:过多的冗余代码,所以我们应该抛弃普通方式,拥抱新的技术,解放双手
- 注解方式:通过注解解决绝大多数的重复工作,并且没有使用反射,不影响程序的运行效率,只是需要多认知一些类,使用稍有不爽。不过在一些Activity跳转广播接收中,通过注解会有天然的优势,可以使你的代码更清晰。
- 绑定方式:数据驱动:数据变化后自动更新UI;事件处理:直接找到目标实例处理用户操作的事件。这样我们就不需要和UI或者控件打交道,只需要在java代码中处理业务逻辑就好了,非常清晰,其余的统一交给binding库去完成。降低了代码耦合度,使得数据独立于UI,对以后程序的变化和维护都有积极的影响。长远考虑下首选绑定方式.
最后吐槽一下:目前Android的绑定和前端的angularjs相比还有不小的差距,尤其是在双向绑定这一块,另外Android studio对DataBinding的报错和代码自动生成这方面的支持也不太友好。当然这只是现状,会慢慢变好的。