<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] # 生成綁定類 引入數據綁定庫后,系統會為每個布局文件生成一個綁定類,綁定類會將布局變量與布局中的視圖關聯起來。創建綁定對象時,會為布局變量自動生成setter和getter方法。 ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> // 布局變量 <data class="CustomBinding"> <import type="java.util.List" /> <variable name="stringList" type="List&lt;String>" /> </data> // 布局視圖 <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> //... </androidx.constraintlayout.widget.ConstraintLayout> </layout> ``` 生成綁定類共有以下幾種方式,以Activity為示例: **方式1:** ```java CustomBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); ``` **方式2:** ```java CustomBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_main, null, false); setContentView(binding.getRoot()); ``` **方式3:** ```java View view = getLayoutInflater().inflate(R.layout.activity_main, null, false); CustomBinding binding = DataBindingUtil.bind(view); setContentView(binding.getRoot()); ``` **方式4:** 此方法最終調用的是DataBindingUtil的bind方法。 ```java View view = getLayoutInflater().inflate(R.layout.activity_main, null, false); CustomBinding binding = CustomBinding.bind(view); setContentView(binding.getRoot()); ``` **方式5:** 此方法最終調用的是DataBindingUtil的inflate方法。且此方法已經Deprecated。 ```java CustomBinding binding = CustomBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); ``` 以上5種方法中,方法1-3調用DataBindingUtil的方法創建綁定對象;方法4-5調用綁定類的方法創建綁定對象,方法4-5中綁定類的方法最終調用的也是DataBindingUtil的方法。所以推薦直接調用DataBindingUtil的方法。 ## 源碼分析 下面重點關注方法1和方法2,看看它們的源碼。 **1、DataBindingUtil的setContentView方法** 方法1DataBindingUtil的setContentView方法,僅適用于Activity的綁定,源碼如下: ```java // DataBindingUtil.java public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity, int layoutId) { return setContentView(activity, layoutId, sDefaultComponent); } // DataBindingUtil.java public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity, int layoutId, @Nullable DataBindingComponent bindingComponent) { activity.setContentView(layoutId); View decorView = activity.getWindow().getDecorView(); ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content); return bindToAddedViews(bindingComponent, contentView, 0, layoutId); } ``` DataBindingUtil的setContentView方法會自動為Activity設置ContentView。 重點關注下bindToAddedViews方法,源碼如下: ```java // DataBindingUtil.java private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component, ViewGroup parent, int startChildren, int layoutId) { final int endChildren = parent.getChildCount(); final int childrenAdded = endChildren - startChildren; if (childrenAdded == 1) { final View childView = parent.getChildAt(endChildren - 1); return bind(component, childView, layoutId); } else { final View[] children = new View[childrenAdded]; for (int i = 0; i < childrenAdded; i++) { children[i] = parent.getChildAt(i + startChildren); } return bind(component, children, layoutId); } } ``` bindToAddedViews的第二個參數是parent,第三個參數是startChildren,需要注意,bindToAddedViews方法綁定的是parent父布局內部的子View或子View集合。對于Activity來說,parent就是contentView,子View也就是我們Activity布局文件的根布局。 **2、DataBindingUtil的inflate方法** 方法2DataBindingUtil的inflate方法適用于Activity、Fragment、Adapter等。 ```java // DataBindingUtil.java public static <T extends ViewDataBinding> T inflate(@NonNull LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent, boolean attachToParent) { return inflate(inflater, layoutId, parent, attachToParent, sDefaultComponent); } // DataBindingUtil.java public static <T extends ViewDataBinding> T inflate( @NonNull LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent, boolean attachToParent, @Nullable DataBindingComponent bindingComponent) { final boolean useChildren = parent != null && attachToParent; // 1、startChildren是目標加載View在parent中的index final int startChildren = useChildren ? parent.getChildCount() : 0; // root不空且附加到root時,返回的是root(當然View已經附加到里面了);否則返回的是目標加載View final View view = inflater.inflate(layoutId, parent, attachToParent); if (useChildren) { // 1.1 綁定的是parent父布局內部的子View或子View集合 return bindToAddedViews(bindingComponent, parent, startChildren, layoutId); } else { // 2、直接綁定當前布局文件加載出來的View視圖 return bind(bindingComponent, view, layoutId); } } ``` 1、當parent不為null,且附加到parent時,調用bindToAddedViews方法,傳遞過去的參數是parent和目標View的index。最終綁定parent父布局的子View,也就是我們的目標加載View 2、當parent為null,或不添加到parent時,直接調用bind方法,綁定當前布局文件加載出來的View視圖。 兩種情況的區別在于,綁定出的View是否具有LayoutParams。 # 綁定表達式 ## 表達式語言 ### 引用 **1、對象屬性引用** 表達式可以使用以下格式在類中引用對象的屬性,對于字段、getter 和[`ObservableField`](https://developer.android.com/reference/androidx/databinding/ObservableField)對象都一樣。 ```xml <data> <variable name="user" type="com.markxu.notitest.User" /> </data> android:text="@{user.lastName}" ``` **2、集合引用** ```xml <data> <import type="java.util.List" /> <variable name="stringList" type="List&lt;String>" /> </data> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text='@{stringList.get(0)}' android:textSize="24sp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"/> ``` 注意:`<`必須轉義書寫為`&lt;` **3、資源引用** ```xml android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}" ``` ### 避免 Null 指針異常 生成的數據綁定代碼會自動檢查有沒有`null`值并避免出現 Null 指針異常。例如,在表達式`@{user.name}`中,如果`user`為 Null,則為`user.name`分配默認值`null`。 ### Null合并運算符 如果左邊運算數不是`null`,則 Null 合并運算符 (`??`) 選擇左邊運算數,如果左邊運算數為`null` ,則選擇右邊運算數。 ```xml android:text="@{user.displayName ?? user.lastName}" 等價于 android:text="@{user.displayName != null ? user.displayName : user.lastName}" ``` ## 事件處理 通過數據綁定,可以編寫從視圖分派的表達式處理事件。有方法引用和監聽器綁定兩種機制。 使用方法引用方式進行事件處理時,在編譯時就會實現監聽器。如果方法不存在或簽名不正確,會收到編譯錯誤;使用監聽器綁定方式進行事件處理時,事件發生時才會實現監聽器。 首先熟悉[Lambda表達式的使用](http://www.androidwiki.site/1569131#Lambda_1),理解事件處理會更容易些。 ### 方法引用 當表達式求值結果為方法引用時,數據綁定會將方法引用和所有者對象封裝到監聽器中,并在目標視圖上設置該監聽器。 ```java public class MyHandlers { public void onClickFriend(View view) { ... } } ``` ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="handlers" type="com.example.MyHandlers"/> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}" android:onClick="@{handlers::onClickFriend}"/> </LinearLayout> </layout> ``` **備注** 使用Lambda表達式時,當已經有現成的方法可以完成你想要傳遞到其他代碼的某個動作時,可以直接使用方法引用。 表達式`handlers::onClickFriend`就是一個方法引用,等價于lambda表達式`(v) -> handlers.onClickFriend(v)`,具體可參考[http://www.androidwiki.site/1569131#\_48](http://www.androidwiki.site/1569131#_48) ### 監聽器綁定 事件發生時對lambda表達式進行求值。 ```java public class Presenter { public void onSaveClick(Task task){} } ``` ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="task" type="com.android.example.Task" /> <variable name="presenter" type="com.android.example.Presenter" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{() -> presenter.onSaveClick(task)}" /> </LinearLayout> </layout> ``` 監聽器綁定中,可以忽略方法的所有參數`android:onClick="@{() -> presenter.onSaveClick(task)}"`,也可以命名所有參數`android:onClick="@{(view) -> presenter.onSaveClick(task)}"` **備注** 使用Lambda表達式時,不能忽略方法的參數 # 使用可觀察數據對象 任何 plain-old 對象都可用于數據綁定,但修改對象不會自動使界面更新。通過數據綁定,數據對象可在其數據發生更改時通知其他對象,自動更新界面。 ## 可觀察對象 將我們的對象繼承BaseObservable即可實現可觀察功能。 ```java private static class User extends BaseObservable { private String firstName; private String lastName; @Bindable public String getFirstName() { return this.firstName; } @Bindable public String getLastName() { return this.lastName; } public void setFirstName(String firstName) { this.firstName = firstName; notifyPropertyChanged(BR.firstName); } public void setLastName(String lastName) { this.lastName = lastName; notifyPropertyChanged(BR.lastName); } } ``` 向getter分配`Bindable`,在setter中調用`notifyPropertyChanged()`方法即可。 ## 可觀察字段 除了使用繼承BaseObservable的方式實現可觀察對象,還可以直接使用可觀察字段。 ```java private static class User { public final ObservableField<String> firstName = new ObservableField<>(); public final ObservableField<String> lastName = new ObservableField<>(); public final ObservableInt age = new ObservableInt(); } ``` Android系統提供了如下可觀察字段: * [`ObservableBoolean`](https://developer.android.com/reference/androidx/databinding/ObservableBoolean) * [`ObservableByte`](https://developer.android.com/reference/androidx/databinding/ObservableByte) * [`ObservableChar`](https://developer.android.com/reference/androidx/databinding/ObservableChar) * [`ObservableShort`](https://developer.android.com/reference/androidx/databinding/ObservableShort) * [`ObservableInt`](https://developer.android.com/reference/androidx/databinding/ObservableInt) * [`ObservableLong`](https://developer.android.com/reference/androidx/databinding/ObservableLong) * [`ObservableFloat`](https://developer.android.com/reference/androidx/databinding/ObservableFloat) * [`ObservableDouble`](https://developer.android.com/reference/androidx/databinding/ObservableDouble) * [`ObservableParcelable`](https://developer.android.com/reference/androidx/databinding/ObservableParcelable) * [`ObservableArrayMap`](https://developer.android.com/reference/androidx/databinding/ObservableArrayMap) * [`ObservableArrayList`](https://developer.android.com/reference/androidx/databinding/ObservableArrayList) # 綁定適配器 ## 設置綁定值 綁定適配器的作用是,View的屬性綁定的值發生變化時,告訴數據綁定庫應該調用哪個方法來更新View。 **注意:** View的屬性在初次綁定時,以及綁定值發生變化時,數據綁定庫都會調用View的方法更新UI。 ```java TextView tvName = findViewById(R.id.tv_name); tvName.setText("Mark"); ``` 不使用數據綁定庫時,更新UI控件需要首先通過findViewById方法找到控件,再調用控件的setter方法改變屬性更新UI。如上面例子中,先通過findViewById找到TextView控件,再調用TextView的setText方法,來給TextView的text屬性賦值,以更新TextView所展示的文案。 ```xml <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.name}" android:textSize="24sp" /> ``` 使用數據綁定庫后,只要綁定值`user.name`發生更改,綁定類就會在視圖上調用setter方法(相當于自動調用TextView的setText方法),來更新UI,前面的賦值更新UI操作全部由數據綁定庫幫我們完成了。 綁定值發生變化時,數據綁定庫調用哪個方法來更新控件呢,有如下三種情況: **1、讓數據綁定庫自己決定調用哪個方法** 還以`android:text="@{user.name}"`為例,綁定值發生變更時,數據綁定庫會自動在TextView類中查找`setText`方法,方法參數需要與`user.name`的值一致。在TextView類中找到`setText(CharSequence text)`方法后,自動調用。 **2、指定自定義方法名稱** View的某些特性擁有名稱不符的 setter時,可以指定想讓數據綁定庫調用的方法。 ```java @BindingMethods({ @BindingMethod(type = "android.widget.ImageView", attribute = "android:tint", method = "setImageTintList"), }) ``` 上面示例中,`android:tint`屬性綁定的值變化時,數據綁定庫會去控件類中尋找`setImageTintList(ImageView imageView)`方法并調用,而不是去尋找`setTint()`方法。 **3、提供自定義邏輯** 某些情況下,沒有現成的方法供數據綁定庫去調用,我們可以提供自定義邏輯。 例如,TextView的`android:paddingLeft`特性沒有關聯的 setter,也就是TextView沒有setPaddingLeft方法,但是TextView提供了`setPadding(left, top, right, bottom)`方法。此時可以使用BindingAdapter來創建綁定適配器: ```java @BindingAdapter("android:paddingLeft") public static void setPaddingLeft(View view, int paddingLeft) { view.setPadding(paddingLeft, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); } ``` 參數類型非常重要。第一個參數確定與特性關聯的視圖類型(TextView),第二個參數確定View屬性綁定表達式中接受的類型(`android:paddingLeft`對應的類型)。 即,TextView的`android:paddingLeft`綁定的值發生變更時,數據綁定庫根據BindingAdapter注解找到setPaddingLeft方法并調用,會把待更新的View和變更后的值傳遞過去。 **接收多個特性的適配器:** ```java @BindingAdapter({"imageUrl", "error"}) public static void loadImage(ImageView view, String url, Drawable error) { Picasso.get().load(url).error(error).into(view); } ``` ```xml <ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" /> ``` 如果ImageView對象同時設置了`imageUrl`和`error`,并且`imageUrl`是字符串,`error`是Drawable,則會調用適配器。如果希望在設置了任意特性時調用適配器,則可以將適配器的`requireAll`設置為`false`,如下示例所示: ```java @BindingAdapter(value={"imageUrl", "placeholder"}, requireAll=false) public static void setImageUrl(ImageView imageView, String url, Drawable placeHolder) { if (url == null) { imageView.setImageDrawable(placeholder); } else { MyImageLoader.loadInto(imageView, url, placeholder); } } ``` > 備注: 數據綁定庫在匹配時會忽略自定義命名空間。 ## 對象轉換 **1、自動對象轉換** ```xml <TextView android:text='@{userMap["lastName"]}' android:layout_width="wrap_content" android:layout_height="wrap_content" /> ``` View屬性綁定值返回的為Object時,數據綁定庫會自動進行對象轉換,將Object對象轉換為text屬性對應的setText方法所需要的參數類型,也就是String類型。 **2、自定義轉換** ```xml <View android:background="@{isError ? @color/red : @color/white}" android:layout_width="wrap_content" android:layout_height="wrap_content"/> ``` 某些情況下,View屬性綁定值返回的類型可以自定義轉換。上面例子中,background屬性需要的是Drawable類型的值,但color是整型值,此時可以使用`BindingConversion`注解完成轉換: ```java @BindingConversion public static ColorDrawable convertColorToDrawable(int color) { return new ColorDrawable(color); } ``` 綁定表達式中提供的值類型必須保持一致。您不能在同一個表達式中使用不同的類型,如以下示例所示: ```xml <View android:background="@{isError ? @drawable/error : @color/white}" android:layout_width="wrap_content" android:layout_height="wrap_content"/> ``` 自定義轉換的應用場景:假如某個控件需要一個格式化好的時間,但是目前只有一個`Date`類型額變量,除了轉化完成之后再進行設置時間,現在還可以使用自定義轉換的方式,先設置再轉換。 # 總結 在沒有使用數據綁定庫的時候,更新UI需要先找到控件,再調用控件的方法更改控件屬性來更新UI。引入數據綁定庫后,數據綁定庫直接將控件的屬性值和綁定表達式的值(數據源)進行綁定,當綁定表達式值變更時,數據綁定庫會自動去尋找控件對象的合適的方法,自動調用,來更新UI。 因此本文的主要內容也就是:怎么將視圖和數據源(綁定表達式)進行綁定,綁定表達式的規則,賦予數據源變化時數據綁定庫自動調用控件方法的能力,幫助數據綁定庫選擇應該調用哪個方法,數據源返回對象的轉換。 # 參考文檔 [官方文檔:綁定適配器](https://developer.android.com/topic/libraries/data-binding/binding-adapters#object-conversions) [Android官方數據綁定框架DataBinding(二)](https://blog.csdn.net/qibin0506/article/details/47720125)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看