本文默認(rèn)讀者有一定的Android開發(fā)經(jīng)驗(yàn),對Android Annotations和DataBinding技術(shù)也有了簡單的了解。
文章通過三種不同方式代碼的對比,最后總結(jié)說明為什么要使用DataBinding的技術(shù)。
功能
三種不同方式代碼需要實(shí)現(xiàn)的功能是在登錄界面里,通過監(jiān)聽用戶名和密碼輸入框的文本變化,動(dòng)態(tài)控制登錄按鈕點(diǎn)擊狀態(tài)。
第一種:普通實(shí)現(xiàn)
采用普通方式編寫代碼,可以發(fā)現(xiàn)會(huì)有很多的多余地方,大部分都是重復(fù)的工作:
- 實(shí)例化view:
findViewById(...) - 添加文本監(jiān)聽:
addTextChangedListener(...) - 設(shè)置點(diǎn)擊事件:
setOnClickListener(...)
xml文件有兩個(gè)EditText和一個(gè)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);
...
//實(shí)例化view
nameEdit = (EditText) findViewById(R.id.login_name_edit);
pwdEdit = (EditText) findViewById(R.id.login_pwd_edit);
loginBtn = (Button) findViewById(R.id.login_btn);
//添加文本變化監(jiān)聽
OnTextChangeListener textChangeListener = new OnTextChangeListener();
nameEdit.addTextChangedListener(textChangeListener);
pwdEdit.addTextChangedListener(textChangeListener);
//登錄按鈕點(diǎn)擊事件監(jiān)聽
loginBtn.setOnClickListener(v -> Toast.makeText(this, "click login!", Toast.LENGTH_SHORT).show());
updateLoginEnable();
}
/**
* 更新登錄按鈕的狀態(tài)
*/
private void updateLoginEnable() {
loginBtn.setEnabled(!(TextUtils.isEmpty(nameEdit.getText()) || TextUtils.isEmpty(pwdEdit.getText())));
}
/**
* 文本變化監(jiān)聽Listener
*/
private class OnTextChangeListener implements TextWatcher {
...
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//在文本變化結(jié)束后去更新
updateLoginEnable();
}
}
}
第二種:Android Annotations實(shí)現(xiàn)
注解方式編寫代碼,讓你專注于真正重要的地方,使代碼更加精簡:
- 通過注解
@ViewById@Click@AfterTextChange解決很多重復(fù)工作 - 在編譯期通過APT生成一個(gè)新的類,命名規(guī)則是原始類名加下劃線,沒有使用反射,不會(huì)影響程序運(yùn)行時(shí)的效率,但是新的編譯出來的類會(huì)讓增加你的認(rèn)知,用起來稍有不爽。
xml文件有兩個(gè)EditText和一個(gè)Button,比較簡單,同樣也就不貼代碼了,只貼出Activity代碼:
@EActivity(R.layout.login_activity)
public class LoginAnnotationActivity extends AppCompatActivity {
//實(shí)例化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();
}
/**
* 更新登錄按鈕的狀態(tài)
*/
private void updateLoginEnable() {
loginBtn.setEnabled(!(TextUtils.isEmpty(nameEdit.getText()) || TextUtils.isEmpty(pwdEdit.getText())));
}
/**
* 登錄點(diǎn)擊回調(diào)
*/
@Click(R.id.login_btn)
protected void login(View view) {
Toast.makeText(this, "click login!", Toast.LENGTH_SHORT).show();
}
//添加文本變化監(jiān)聽
@AfterTextChange({R.id.login_pwd_edit, R.id.login_name_edit})
protected void afterTextChange(TextView tv, Editable text) {
//在文本變化結(jié)束后去更新
updateLoginEnable();
}
}
第三種:DataBinding實(shí)現(xiàn)
綁定方式:去除了冗余代碼的基礎(chǔ)上對數(shù)據(jù)和UI層進(jìn)行解耦
- 通過
android:text="@={...}"將數(shù)據(jù)雙向綁定到UI中 - 通過
android:enabled="@{...}"控制按鈕狀態(tài) - 通過
android:onClick="@{...}"直接處理用戶操作事件 - 編譯期同過APT生成輔助工具類,實(shí)現(xiàn)數(shù)據(jù)和UI的動(dòng)態(tài)綁定
首先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代碼我們可以看出,數(shù)據(jù)綁定規(guī)則已經(jīng)放在里面了,其實(shí)java代碼的只需要處理業(yè)務(wù)相關(guān)的邏輯就好了,非常的清晰,然后Activity和輔助Helper代碼:
public class LoginActivity extends AppCompatActivity {
//DataBinding自動(dòng)生成的類,命名規(guī)則是取xml文件名加Binding結(jié)尾
LoginActivityBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
//初始化data bind,并設(shè)置Helper實(shí)例
binding = DataBindingUtil.setContentView(this, R.layout.login_activity);
binding.setLoginViewHelper(new LoginViewHelper());
}
}
public class LoginViewHelper {
//監(jiān)聽屬性
public ObservableField<String> name = new ObservableField<>();
public ObservableField<String> pwd = new ObservableField<>();
/**
* 登錄點(diǎn)擊回調(diào)
*/
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));
}
}
總結(jié)
- 普通方式:過多的冗余代碼,所以我們應(yīng)該拋棄普通方式,擁抱新的技術(shù),解放雙手
- 注解方式:通過注解解決絕大多數(shù)的重復(fù)工作,并且沒有使用反射,不影響程序的運(yùn)行效率,只是需要多認(rèn)知一些類,使用稍有不爽。不過在一些Activity跳轉(zhuǎn)廣播接收中,通過注解會(huì)有天然的優(yōu)勢,可以使你的代碼更清晰。
- 綁定方式:數(shù)據(jù)驅(qū)動(dòng):數(shù)據(jù)變化后自動(dòng)更新UI;事件處理:直接找到目標(biāo)實(shí)例處理用戶操作的事件。這樣我們就不需要和UI或者控件打交道,只需要在java代碼中處理業(yè)務(wù)邏輯就好了,非常清晰,其余的統(tǒng)一交給binding庫去完成。降低了代碼耦合度,使得數(shù)據(jù)獨(dú)立于UI,對以后程序的變化和維護(hù)都有積極的影響。長遠(yuǎn)考慮下首選綁定方式.
最后吐槽一下:目前Android的綁定和前端的angularjs相比還有不小的差距,尤其是在雙向綁定這一塊,另外Android studio對DataBinding的報(bào)錯(cuò)和代碼自動(dòng)生成這方面的支持也不太友好。當(dāng)然這只是現(xiàn)狀,會(huì)慢慢變好的。