在昨天講完ObservableField的雙向綁定後,今天要來使用BindingAdapter及InverseBindingAdapter等實作雙向綁定,那麼首先一樣先加入依賴至gradle中:
android {
...
dataBinding {
enabled = true
}
}
首先一樣,先把布局更換成data binding的layout,那麼一樣先將今天設計的布局貼上來,稍後會去設計一個ProgressData的Model。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="ProgressData"
type="com.example.bindingadapter.ProgressData" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(ProgressData.progress_1)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.979"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="246dp"
android:layout_height="wrap_content"
app:progressing="@={ProgressData.progress_1}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.357"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
BindingAdapter可用於自訂一個方法,並且透過可從布局中直接做使用,可以把他理解成setter,而InverseBindingAdapter則是getter,這樣則為雙向綁定,而雙向綁定官方也有釋出一些調配器做使用:
參考:Android Developers
寫法大約是這樣,我建立一個progressing的方法並且給ProgressBar做使用,然後layout在做使用會傳入一個ObservableField的參數,這邊是當數據發生變化時要做什麼,然後要注意這個方法必須是靜態的(static)。
@BindingAdapter(value = "progressing")
public static void setProgressing(ProgressBar progressBar, ObservableField<Integer>){
//要做的事...
}
InverseBindingAdapter綁的attribute必須和BindingAdapter設定的value相同,主要是當視圖屬性發生變化時會調用此,而屬性+AttrChanged這個BindingAdapter在你設定完InverseBindingAdapter要將layout自訂屬性改為雙向時必不可少的方法之一,主要是他不知道屬性何時更改,用這個BindingAdapter可以在裡面加入監聽器等判斷他View的更新。
@InverseBindingAdapter(attribute = "progressing")
public static int getProgressing(ProgressBar progressBar) {
//要做的事...
}
@BindingAdapter(value = "progressingAttrChanged")
public static void setProgressingAttrChanged(ProgressBar progressBar, InverseBindingListener attrChange) {
// Set a listener for click, focus, touch
}
那麼接著來設計需要的Model,一樣會用到昨天寫雙向的ObservableField。
public class ProgressData{
private static int[] progressValue = {0};
public final ObservableField<Integer> progress_1 = new ObservableField<>();
//以此使ProgressBar進行遞增並run至完成
@BindingAdapter(value = "progressing")
public static void setProgressing(ProgressBar progressBar, ObservableField<Integer> progress_1) {
new Thread(new Runnable() {
@Override
public void run() {
while (progressValue[0] <= 100) {
try {
Thread.sleep(500);
progressBar.setProgress(progressValue[0]);//設定progress進度
progress_1.set(progressBar.getProgress());//設定資料
} catch (InterruptedException e) {
e.printStackTrace();
}
progressValue[0] = progressValue[0] + 1;//遞增progress進度
//當進度滿時,更改progressBar背景顏色(因為是在線程更改ui,所以需要包Looper)
if(progressValue[0]==100){
Looper.prepare();
progressBar.setBackgroundColor(Color.argb(25,25,30,10));
Looper.loop();
}
}
}
}).start();
}
//取得progress的自訂getter
@InverseBindingAdapter(attribute = "progressing")
public static int getProgressing(ProgressBar progressBar) {
return progressBar.getProgress();
}
//必加屬性(可用於判斷屬性何時做更改)
@BindingAdapter(value = "progressingAttrChanged")
public static void setProgressingAttrChanged(ProgressBar progressBar, InverseBindingListener attrChange) {
attrChange.onChange();
}
}
設計完後,我便可在layout的元件上加入我自訂的屬性(app:processing):
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="246dp"
android:layout_height="wrap_content"
app:progressing="@={ProgressData.progress_1}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.357"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
最後還需要去給他databinding的variable資料。
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
ProgressData progressData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
progressData= new ProgressData();
binding.setProgressData(progressData);
}
}