Habréはすでにこのトピックに関する記事でいっぱいです。それらはすべて、
RecyclerViewでセルを簡単にサイズ変更するためのソリューションを主に提供しています。 今日はもう少し進んで、
DataBindingに匹敵するシンプルさに近づきます。

リストに
DataBindingを使用せず(
良い例 )、昔ながらの方法で実行している場合は、この記事が役立ちます。
はじめに
この記事のコンテキストをすばやく掘り下げるために、以前の記事のユニバーサルアダプターを使用した場合の現在の状態を見てみましょう。
- 簡単リスト管理-RendererRecyclerViewAdapter
- 簡易リスト管理-RendererRecyclerViewAdapter(パート2)
RendererRecyclerViewAdapter v2.xxを使用して最も単純なリストを実装するには、
次のものが必要です。
各セルモデルで空の
ViewModelインターフェイスを実装するには:
public class YourModel implements ViewModel { ... public String getYourText() { ... } }
ViewHolderの古典的な実装を
作成します。
<?xml version="1.0" encoding="utf-8"?> <TextView android:id = "@+id/yourTextView" xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "match_parent" android:layout_height = "50dp" />
public class YourViewHolder extends RecyclerView.ViewHolder { public TextView yourTextView; public RectViewHolder(final View itemView) { super(itemView); yourTextView = (TextView) itemView.findViewById(R.id.yourTextView); } ... }
ViewRendererを実装し
ます 。
public class YourViewRenderer extends ViewRenderer<YourModel, YourViewHolder> { public YourViewRenderer(Class<YourModel> type, Context context) { super(type, context); } public void bindView(YourModel model, YourViewHolder holder) { holder.yourTextView.setText(model.getYourText()); ... } public YourViewHolder createViewHolder(ViewGroup parent) { return new YourViewHolder(inflate(R.layout.your_layout, parent)); } }
アダプターを初期化し、必要なデータをアダプターに渡します。
... RendererRecyclerViewAdapter adapter = new RendererRecyclerViewAdapter(); adapter.registerRenderer(new YourViewRenderer(YourModel.class, getContext())); adapter.setItems(getYourModelList()); ...
DataBindingとその実装の容易さを知っていると、疑問が生じます。主なものはバインディングであるため、なぜ余分なコードがあるのですか。モデルデータをどこからでも逃げられないレイアウトと比較します。
古典的な実装では、
bindView()メソッドを使用しますが、他のすべてはそれを準備するだけです(
ViewHolderの実装と初期化)。
ViewHolderとは何ですか、なぜ必要なのですか?
フラグメント、アクティビティ、リサイクラービューでは、このパターンをよく使用しますが、なぜそれが必要なのでしょうか? それの長所と短所は何ですか?
長所:
- findViewByIdを毎回使用してIDを指定する必要はありません。
- xmlで特定のIDを検索するためにCPU時間を費やす必要がないたび。
- 作成されたフィールドを通してどこからでも要素にアクセスすると便利です。
短所:
- 追加のクラスを作成する必要があります。
- xmlの各IDに類似した名前のフィールドを作成する必要があります。
- IDを変更するときは、ビューアのフィールドの名前を変更する必要があります。
ButterKnifeなどのサードパーティのライブラリは、いくつかの
ことをうまく実行できますが、
RecyclerViewの場合、これはあまり役に立ちません
。ViewHolder自体を
削除することはありません。
DataBindingでは 、このバインディングはxml自体の責任であるため、ユニバーサルビューアーを作成できます。 何ができますか?
デフォルトのViewHolderを作成
createViewHolder()メソッドでスタブとして
RecyclerView.ViewHolderの標準実装を使用する場合、
bindView()で毎回
findViewByIdメソッドを使用する
必要がありますが、プラスを犠牲にして何が起こるか見てみましょう。
クラスは抽象なので、新しいデフォルトビューホルダーの空の実装を追加します。
public class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View itemView) { super(itemView); } }
ViewRender'e
のビューホルダーを
置き換えます。
public class YourViewRenderer extends ViewRenderer<YourModel, ViewHolder> { public YourViewRenderer(Class<YourModel> type, Context context) { super(type, context); } public void bindView(YourModel model, ViewHolder holder) { ((TextView)holder.itemView.findViewById(R.id.yourTextView)).setText(model.getYourText()); } public ViewHolder createViewHolder(ViewGroup parent) { return new ViewHolder(inflate(R.layout.your_layout, parent)); } }
受け取った利点:
- 各セルにViewHolderを実装する必要はありません。
- createViewHolderメソッドの実装は、基本クラスに移動できます。
次に、失われたプラスを分析しましょう。
RecyclerViewのフレームワーク内で
ViewHolderを検討するため、それぞれ
bindView()メソッドでのみアクセスします
。1番目と3番目のポイントはあまり役に立ちません。
findViewByIdを毎回使用してIDを指定する必要はありません。- xmlで特定のIDを検索するためにCPU時間を費やす必要がないたび。
作成されたフィールドを通してどこからでも要素にアクセスすると便利です。
しかし、パフォーマンスを犠牲にすることはできませんので、何かを決めましょう。 ビューアーの実装により、見つかったビューを「キャッシュ」できます。 これをデフォルトのビューホルダーに追加しましょう:
public class ViewHolder extends RecyclerView.ViewHolder { private final SparseArray<View> mCachedViews = new SparseArray<>(); public ViewHolder(View itemView) { super(itemView); } public <T extends View> T find(int ID) { return (T) findViewById(ID); } private View findViewById(int ID) { final View cachedView = mCachedViews.get(ID); if (cachedView != null) { return cachedView; } final View view = itemView.findViewById(ID); mCachedViews.put(ID, view); return view; } }
したがって、
bindView()の最初の呼び出しの後、ビューアーはすべてのビューを認識し、後続の呼び出しはキャッシュされた値を使用します。
次に、ベース
ViewRenderのすべての余分な部分を
取り除き 、レイアウトIDを渡すためのコンストラクターに新しいパラメーターを追加して、何が起こったかを確認します。
public class YourViewRenderer extends ViewRenderer<YourModel, ViewHolder> { public YourViewRenderer(int layoutID, Class<YourModel> type, Context context) { super(layoutID, type, context); } public void bindView(YourModel model, ViewHolder holder) { ((TextView)holder.find(R.id.yourTextView)).setText(model.getYourText()); } }
コードの量に関しては、見た目はずっと良く、コンストラクターは1つだけであり、常に同じです。 1つのメソッドのために、毎回新しい
ViewRendererを作成する必要がありますか? 私はそうは思わない、私たちは委任とコンストラクターの追加パラメーターを介してこの問題を解決し、私たちは見ます:
public class ViewBinder<M extends ViewModel> extends ViewRenderer<M, ViewHolder> { private final Binder mBinder; public ViewBinder(int layoutID, Class<M> type, Context context, Binder<M> binder) { super(layoutID, type, context); mBinder = binder; } public void bindView(M model, ViewHolder holder) { mBinder.bindView(model, holder); } public interface Binder <M> { void bindView(M model, ViewHolder holder); } }
セルの追加は以下に削減されます。
... adapter.registerRenderer(new ViewBinder<>( R.layout.your_layout, YourModel.class, getContext(), (model, holder) -> { ((TextView)holder.find(R.id.yourTextView)).setText(model.getYourText()); } )); ...
このソリューションの利点をリストします。
- 毎回ViewHolderを作成し、ビューの変数を作成する必要はありません。
- 毎回ViewRendererを作成して追加のコードを記述する必要はありません。
- ビューIDを変更するときに名前を変更する必要はありません。
- すべてのビューデータ(layoutID、concreteViewID、cast)は1つの場所にあります。
おわりに
ささいな利点を犠牲にして、新しいセルを追加するためのかなり簡単なソリューションを得ました。これは間違いなく
RecyclerViewでの作業を単純化します。
この記事では、理解のための簡単な例を示しています。現在の実装では次のことが可能です。
ネストされたRecyclerViewを使用する adapter.registerRenderer( new CompositeViewBinder<>( R.layout.nested_recycler_view,
DiffUtilを使用するときにペイロードを操作する adapter.setDiffCallback(new YourDiffCallback()); adapter.registerRenderer(new ViewBinder<>( R.layout.item_layout, YourModel.class, getContext(), (model, holder, payloads) -> { if(payloads.isEmpty()) {
データを読み込むときに進行状況バーを追加する adapter.registerRenderer(new LoadMoreViewBinder(R.layout.item_load_more, getContext())); recyclerView.addOnScrollListener(new YourEndlessScrollListener() { public void onLoadMore() { adapter.showLoadMore();
より詳細な例を
ここで見つけることができます。
世論調査
バインディングデザインは少しいように見えます。
... (model, holder) -> { ((TextView)holder.find(R.id.textView)).setText(model.getText()); ((ImageView)holder.find(R.id.imageView)).setImageResource(model.getImage()); } ...
テストとして、デフォルトの
ViewHolderにいくつかのメソッドを追加しました。
public class ViewHolder extends RecyclerView.ViewHolder { ... public ViewHolder setText(int ID, CharSequence text) { ((TextView)find(ID)).setText(text); return this; } }
結果:
... (model, holder) -> holder .setText(R.id.textView, model.getText()) .setImageResource(R.id.imageView, ...) .setVisibility(...) ...