簡単リスト管理-RendererRecyclerViewAdapter

最近、リスト用に多くのアダプターを書き直さなければならず、頭を抱えるたびに、ビジネスロジック、ネットワーク要求、アプリケーションルーティングなどが含まれていました。 これをすべて変更することは非常に困難です。



最初は、いつものように、余剰分をすべてアダプターからプレゼンター、フラグメント、その他のクラスに取り出しました。 最終的に、私は結論に達しました、なぜですか:



  1. アダプタに不要なロジックを導入しないように「保護」します。
  2. セルバインディングを再利用します。
  3. いくつかの種類の細胞を扱うためのある種の普遍性を達成する。


あなたがそのような問題に精通しているなら、猫へようこそ。



既製のソリューションの中で、 AdapterDelegatesを見つけましたが、最初の条件には適合しませんでした。



必要条件



最初に、事前に作成された要件をいくつか書きました。





実装



まず最初に、私は常にアダプターで何をするかを見ました。これのために、テスト実装を作成し、使用したメソッドを分析しました。



public class Test extends RecyclerView.Adapter { @Override public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) { } @Override public void onBindViewHolder(final ViewHolder holder, final int position) { } @Override public int getItemCount() { return 0; } public void setItems(@NonNull final ArrayList items) { } }
      
      





4つの方法で何も起こりませんでした。 setItems()メソッドはすぐに明らかになります。モデルのさまざまなリストを受け入れ、空のインターフェイスを作成し、テストアダプターのコードを更新できる必要があります。



 public interface ItemModel { } public class Test extends RecyclerView.Adapter { @NonNull private final ArrayList<ItemModel> mItems = new ArrayList<>(); .... @Override public int getItemCount() { return mItems.size(); } public void setItems(@NonNull final ArrayList<ItemModel> items) { mItems.clear(); mItems.addAll(items); } }
      
      





次に、onCreateViewHolder()およびonBindViewHolder()を使用して何かを考え出す必要があります。



アダプターが異なるビューをバインドできるようにしたい場合、これを誰かに委任するとよいでしょう。 これにより、実装の再利用が可能になります。 1つのタイプのセルのみ、そしてもちろん特定のViewHolderで動作できる抽象クラスを作成します。 これを行うには、ジェネリックを使用してカーストを回避します。 それをViewRendererと呼びましょう-これ以上思い浮かぶことはありません。



 public abstract class ViewRenderer <M extends ItemModel, VH extends RecyclerView.ViewHolder> { public abstract void bindView(@NonNull M model, @NonNull VH holder); @NonNull public abstract VH createViewHolder(@Nullable ViewGroup parent); }
      
      





アダプターで使用してみましょう。 アダプターの名前を意味のあるものに変更し、コードを変更します。



 public class RendererRecyclerViewAdapter extends RecyclerView.Adapter { ... private ViewRenderer mRenderer; @Override public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) { return mRenderer.createViewHolder(parent); } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { mRenderer.bindView(item, holder); } public void registerRenderer(@NonNull final ViewRenderer renderer) { mRenderer = renderer; } ... }
      
      





これまでのところ、すべてがよさそうです。 しかし、アダプターはいくつかのタイプのビューで機能するはずです。 このため、アダプターにはgetItemViewType()メソッドがあり、アダプターでオーバーライドします。



そして、モデル自体のセルタイプを尋ねてみてください-インターフェースにメソッドを追加し、アダプターメソッドを更新します:



 public interface ItemModel { int getType(); } public class RendererRecyclerViewAdapter extends RecyclerView.Adapter { ... @Override public int getItemViewType(final int position) { final ItemModel item = getItem(position); return item.getType(); } private ItemModel getItem(final int position) { return mItems.get(position); } ... }
      
      





同時に、複数のViewRendererのサポートを確定します。



 public class RendererRecyclerViewAdapter extends RecyclerView.Adapter { ... @NonNull private final SparseArray<ViewRenderer> mRenderers = new SparseArray<>(); @Override public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) { final ViewRenderer renderer = mRenderers.get(viewType); if (renderer != null) { return renderer.createViewHolder(parent); } throw new RuntimeException("Not supported Item View Type: " + viewType); } public void registerRenderer(@NonNull final ViewRenderer renderer) { final int type = renderer.getType(); if (mRenderers.get(type) == null) { mRenderers.put(type, renderer); } else { throw new RuntimeException("ViewRenderer already exist with this type: " + type); } } @SuppressWarnings("unchecked") @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { final ItemModel item = getItem(position); final ViewRenderer renderer = mRenderers.get(item.getType()); if (renderer != null) { renderer.bindView(item, holder); } else { throw new RuntimeException("Not supported View Holder: " + holder); } } ... }
      
      





レンダラーにgotType()メソッドがあることがわかりますが、これは特定のビューに必要なレンダラーを見つけるために必要です。



アダプタの準備ができました。



特定のクラスItemModel、ViewHolder、ViewRendererを実装します。



いくつかのモデル
 public class SomeModel implements ItemModel { public static final int TYPE = 0; @NonNull private final String mTitle; public SomeModel(@NonNull final String title) { mTitle = title; } @Override public int getType() { return TYPE; } @NonNull public String getTitle() { return mTitle; } ... }
      
      







視聴者
 public class SomeViewHolder extends RecyclerView.ViewHolder { public final TextView mTitle; public SomeViewHolder(final View itemView) { super(itemView); mTitle = (TextView) itemView.findViewById(R.id.title); ... } }
      
      







サムビューレンダラー
 public class SomeViewRenderer extends ViewRenderer<SomeModel, SomeViewHolder> { public SomeViewRenderer(final int type, final Context context) { super(type, context); } @Override public void bindView(@NonNull final SomeModel model, @NonNull final SomeViewHolder holder) { ... } @NonNull @Override public SomeViewHolder createViewHolder(@Nullable final ViewGroup parent) { return new SomeViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.some_item, parent, false)); } }
      
      







ViewRenderにはコンストラクタと2つのパラメータがあります-ViewRenderer(int viewType、Context context)、それが必要であると私は説明する必要はないと思います。



これで、RecyclerViewにアダプターを導入できます。



 public class SomeActivity extends AppCompatActivity { private RendererRecyclerViewAdapter mRecyclerViewAdapter; private RecyclerView mRecyclerView; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mRecyclerViewAdapter = new RendererRecyclerViewAdapter(); mRecyclerViewAdapter.registerRenderer(new SomeViewRenderer(SomeModel.TYPE, this)); // mRecyclerViewAdapter.registerRenderer(...); mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mRecyclerView.setAdapter(mRecyclerViewAdapter); mRecyclerViewAdapter.setItems(getItems()); mRecyclerViewAdapter.notifyDataSetChanged(); } ... }
      
      





おわりに



かなり少ない電力で、いくつかのタイプのセルで簡単に使用できるアダプターの作業バージョンが得られました。これは、特定のタイプのセルにViewRendererを実装し、アダプターに登録するだけで十分です。



現時点では、この実装はすでにいくつかの大規模プロジェクトで実証済みです。



例とソースはこちらから入手できます



UPD: 簡易リスト管理-RendererRecyclerViewAdapter(パート2)



All Articles