ComponentsRecyclerAdapter
RecyclerView
で複数の view type や複雑なデータ構造を扱う
RecyclerView
を使う際に便利な自作のライブラリを紹介RecyclerView
使ってますよね?A flexible view for providing a limited window into a large data set.(
RecyclerView
のドキュメントより)
RecyclerView.Adapter
のサブクラスAdapters provide a binding from an app-specific data set to views that are displayed within a RecyclerView
.
(RecyclerView.Adapter
のドキュメントより)
getItemViewType(int)
)ViewHolder
を生成
(onCreateViewHolder(ViewGroup, int)
)onBindViewHolder(VH, int, List<Object>)
)単純なデータセットを 1 個や 2 個程度の view type で扱う分には普通に
RecyclerView.Adapter
のサブクラスを定義してやればよい。 (困ることはない。)
だがしかし……!!
ViewHolder
、binding 間の関係の記述が散らばるgetItemViewType
メソッドonCreateViewHolder
メソッドonBindViewHolder
メソッドComponentsRecyclerAdapter
ViewHolder
生成処理の関係は ViewHolderFactoryRegistry
に宣言的に記述Component
で記述Component
の役割build.gradle
に依存記述repositories {
jcenter()
}
dependencies {
compile 'info.vividcode.android:components-recycler-adapter:0.1.0'
compile 'com.android.support:recyclerview-v7:23.1.1'
}
ViewHolder
(+ Factory) 定義レイアウトファイルと ViewHolder
のクラスを一対一で対応させるのがおすすめ
public class ContentViewHolder extends RecyclerView.ViewHolder {
// こういう Factory も必要。 (この例では lambda 式を使用。)
public static final ViewHolderFactory<ContentViewHolder> FACTORY =
(parent) -> {
TextView v = (TextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_content, parent, false);
return new ContentViewHolder(v);
};
public final TextView textView;
public ContentViewHolder(TextView itemView) {
super(itemView);
textView = itemView;
}
}
Binder
定義特定の ViewHolder
と特定の型の項目の binding 処理。
public class StringContentBinder
implements Binder<ContentViewHolder, String> {
// インスタンスを static フィールドで保持しておく必要はない。
// (後で使うのでここでは定義してある。)
public static final StringContentBinder INSTANCE =
new StringContentBinder();
@Override
public void bindViewHolder(
ContentViewHolder h, Component<String> c, int posInComponent) {
h.textView.setText(c.getItem(posInComponent);
}
}
Adapter は ComponentsRecyclerAdapter
を継承して、以下の内容を実装する。
public class MyAdapter extends ComponentsRecyclerAdapter {
// View type と ViewHolderFactory の関連付け
// (ViewHolderFactoryRegistry)
// コンポーネントの定義
// コンストラクタで ViewHolderFactoryRegistry とコンポーネントを登録
}
ViewHolderFactory
の関連付けpublic class MyAdapter extends ComponentsRecyclerAdapter {
public static final ViewTypes VIEW_TYPES = new ViewTypes();
public static class ViewTypes extends ViewHolderFactoryRegistry {
// ContentViewHolder を生成する Factory を登録し、
// その view type をフィールド変数に保持
public final ViewType<ContentViewHolder> content =
register(ContentViewHolder.FACTORY);
// ...
// (同じようにして複数の Factory を登録して view type を変数に保持する)
}
宣言的に結び付きを記述。
public class MyAdapter extends ComponentsRecyclerAdapter {
// ...
// 文字列のリストを参照するコンポーネント。
private final ListReferenceComponent<String> mStringListComponent =
ListReferenceComponent.create(
// データセット内の各項目の view type と binder を指定。
// (ここでは、全ての項目について view type を `content` に、
// binder を `StringContentBinder` にしている。)
new FixedViewTypeBinderPairProvider<>(
VIEW_TYPES.content,
StringContentBinder.INSTANCE)
);
// ...
// (表示用に複雑なデータ構造が必要な場合は、同様にして
// コンポーネントを定義していく。)
各項目の view type と binder の指定はここで。
public class MyAdapter extends ComponentsRecyclerAdapter {
// ...
public MyAdapter(List<String> list) {
// スーパークラスのコンストラクタに ViewHolderFactoryRegistry を渡す。
super(VIEW_TYPES);
// コンポーネントにリストをセット。
mStringListComponent.setList(list);
// ルートコンポーネントとして文字列のリストを参照する
// コンポーネントをセット。
setComponent(mStringListComponent);
// ...
// (複雑なデータ構造が必要ならここで作る。)
}