簡化你的列表Adapter

陳明明發表於2017-12-14

BindingCollectionAdapter

最近看到了這個基於databing的開源庫,簡單翻譯了一下。。。 GitHub地址 #一 依賴安裝

compile 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter:2.2.0' compile 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter-recyclerview:2.2.0'

gradle 版本需要在2.3.0以上

#二 使用 ##2.1 單檢視 你需要提供items(資料list)和ItemBinding 去繫結資料,應使用ObserableList去自動更新View,當然,如果你不需要該功能也可以使用其他型別的List。 示例ViewModel:

public class ViewModel {
  public final ObservableList<String> items = new ObservableArrayList<>();
  public final ItemBinding<String> itemBinding = ItemBinding.of(BR.item, R.layout.item);
}
複製程式碼

然後佈局繫結viewModel的items和itemBinding,如果使用RecyclerView需要繫結一個layoutManager(看示例)

<!-- 主佈局 layout.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
      <import type="com.example.R" />
      <import type="me.tatarka.bindingcollectionadapter2.LayoutManagers" />
      <variable name="viewModel" type="com.example.ViewModel"/>
    </data>

    <ListView
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:items="@{viewModel.items}"
      app:itemBinding="@{viewModel.itemBinding}"/>

    <android.support.v7.widget.RecyclerView
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:layoutManager="@{LayoutManagers.linear()}"
      app:items="@{viewModel.items}"
      app:itemBinding="@{viewModel.itemBinding}"/>

    <android.support.v4.view.ViewPager
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:items="@{viewModel.items}"
      app:itemBinding="@{viewModel.itemBinding}"/>

    <Spinner
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:items="@{viewModel.items}"
      app:itemBinding="@{viewModel.itemBinding}"
      app:itemDropDownLayout="@{R.layout.item_dropdown}"/>
</layout>
複製程式碼

集合中的item將會通過ItemBinding繫結到子佈局中

<!-- 子佈局 item.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
      <variable name="item" type="String"/>
    </data>

    <TextView
      android:id="@+id/text"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="@{item}"/>
</layout>
複製程式碼

2.2 多檢視

你可以重寫onItemBind實現多檢視佈局,仍使用app:itemBinding繫結資料。

public final OnItemBind<String> onItemBind = new OnItemBind<String>() {
  @Override
  public void onItemBind(ItemBinding itemBinding, int position, String item) {
    itemBinding.set(BR.item, position == 0 ? R.layout.item_header : R.layout.item);
  }
};
複製程式碼

如果使用的是ListView,必須使用app:itemTypeCount="@{2}來指定檢視型別數量。 注意:如果你不做任何複雜的資料,onItemBind仍會被多次呼叫,如果你不需要繫結資料,應當使用ItemBinding.VAR_NONE 作為variable的id

2.3 繫結其他變數

可以使用itemBinding.bindExtra(BR.extra, value)來為列表的子佈局新增額外的變數,示例為佈局額外繫結一個點選事件

public interface OnItemClickListener {
    void onItemClick(String item);
}

OnItemClickListener listener = ...;
ItemBinding<Item> itemBinding = ItemBinding.<Item>of(BR.item, R.layout.item)
    .bindExtra(BR.listener, listener);
複製程式碼
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
      <variable name="item" type="String"/>
      <variable name="listener" type="OnItemClickListener"/>
    </data>

    <TextView
      android:id="@+id/text"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:onClick="@{() -> listener.onItemClick(item)}"
      android:text="@{item}"/>
</layout>
複製程式碼

2.4 額外的Adapter配置

2.4.1 ListView

通過回撥為每一個子佈局新增id

adapter.setItemIds(new BindingListViewAdapter.ItemIds<T>() {
  @Override
  public long getItemId(int position, T item) {
    return // Calculate item id.
  }
});
複製程式碼

或者在佈局中通過app:itemIds="@{itemIds}"來繫結。通過設定這個會使hasStableIds 返回true,從而加大記憶體消耗, 可以通過回撥來決定是否啟用

adapter.setItemEnabled(new BindingListViewAdapter.ItemEnabled<T>() {
  @Override
  public boolean isEnabled(int position, T item) {
    return // Calculate if item is enabled.
  }
});
複製程式碼

或者通過app:itemEnabled="@{itemEnabled}"在佈局檔案中繫結

2.4.2 ViewPager

通過回撥為子頁面新增標題

adapter.setPageTitles(new PageTitles<T>() {
  @Override
  public CharSequence getPageTitle(int position, T item) {
    return "Page Title";
  }
});
複製程式碼

或者通過 app:pageTitles="@{pageTitles}"為ViewPage繫結標題

2.4.3 RecyclerView

自定義view holders

adapter.setViewHolderFactory(new ViewHolderFactory() {
  @Override
  public RecyclerView.ViewHolder createViewHolder(ViewDataBinding binding) {
    return new MyCustomViewHolder(binding.getRoot());
  }
});
複製程式碼

或者通過app:viewHolder="@{viewHolderFactory}" 繫結

2.5 直接操作View

如果你需要直接操作View,你可以通過自定義Adapter來實現

public class MyRecyclerViewAdapter<T> extends BindingRecyclerViewAdapter<T> {

  @Override
  public ViewDataBinding onCreateBinding(LayoutInflater inflater, @LayoutRes int layoutId, ViewGroup viewGroup) {
    ViewDataBinding binding = super.onCreateBinding(inflater, layoutId, viewGroup);
    Log.d(TAG, "created binding: " + binding);
    return binding;
  }

  @Override
  public void onBindBinding(ViewDataBinding binding, int bindingVariable, @LayoutRes int layoutId, int position, T item) {
    super.onBindBinding(binding, bindingVariable, layoutId, position, item);
    Log.d(TAG, "bound binding: " + binding + " at position: " + position);
  }
}
複製程式碼

佈局檔案中繫結adapter

<android.support.v7.widget.RecyclerView
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  app:layoutManager="@{LayoutManagers.linear()}"
  app:items="@{viewModel.items}"
  app:itemBinding="@{viewModel.itemBinding}"
  app:adapter="@{viewModel.adapter}"/>
複製程式碼

###2.6 OnItemBind 助手 這兒有一些OnItemBind的常見的實現

itemBind = new OnItemBindClass<>()
  .map(String.class, BR.name, R.layout.item_name)
  .map(Footer.class, ItemBinding.VAR_NONE, R.layout.item_footer)
  .map(Item.class, new OnItemBind<Item>() {
                       @Override
                       public void onItemBind(ItemBinding itemBinding, int position, Item item) {
                         itemBinding.clearExtras()
                                    .set(BR.item, position == 0 ? R.layout.item_header : R.layout.item)
                                    .bindExtra(BR.extra, (list.size() - 1) == position);
                       }
                     })
  .map(Object.class, ItemBinding.VAR_NONE, R.layout.item_other);
複製程式碼

OnItemBindModel 是子佈局的binding

itemBind = new OnItemBindModel<Model>();

public class Model implements ItemBindingModel {
  @Override
  public void onItemBind(ItemBinding itemBinding) {
    itemBinding.set(BR.name, R.layout.item_name);
  }
}
複製程式碼

###2.7 MergeObservableList 用於融合兩個列表

ObservableList<String> data = new ObservableArrayList<>();
MergeObservableList<String> list = new MergeObservableList<>()
  .insertItem("Header")
  .insertList(data)
  .insertItem("Footer");

data.addAll(Arrays.asList("One", "Two"));
// list => ["Header", "One", "Two", "Footer"]
data.remove("One");
// list => ["Header", "Two", "Footer"]
複製程式碼

###2.8 DiffObservableList 用於列表的更新

DiffObservableList<Item> list = new DiffObservableList(new DiffObservableList.Callback<Item>() {
    @Override
    public boolean areItemsTheSame(Item oldItem, Item newItem) {
        return oldItem.id.equals(newItem.id);
    }

    @Override
    public boolean areContentsTheSame(Item oldItem, Item newItem) {
        return oldItem.value.equals(newItem.value);
    }
});

list.update(Arrays.asList(new Item("1", "a"), new Item("2", "b1")));
list.update(Arrays.asList(new Item("2", "b2"), new Item("3", "c"), new Item("4", "d"));
複製程式碼

執行緒切換

DiffObservableList<Item> list = new DiffObservableList(...);

// On background thread:
DiffUtil.DiffResult diffResult = list.calculateDiff(newItems);

// On main thread:
list.update(newItems, diffResult);
複製程式碼

相關文章