Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Concat Adapters Droidcon APAC 2020

3d47c78737bf639fc934b232198c8256?s=47 Abhishesh
December 15, 2020

Concat Adapters Droidcon APAC 2020

ConcatAdapter is a new api exposed in recycler view which enables us to sequentially combine multiple adapters.
Learn how to better encapsulate your adapters having different data sources.
Different use cases where concat adapter can be used in an easy & elegant way.

3d47c78737bf639fc934b232198c8256?s=128

Abhishesh

December 15, 2020
Tweet

Transcript

  1. Concat adapters: Easy & elegant way of combining adapters Abhishesh

    Srivastava https://twitter.com/abhishesh_sri https://github.com/abhishesh-srivastava
  2. Sprint 1

  3. Too simple. Isn’t it? class WowAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { …

    override fun getItemViewType(position: Int): Int { if (position == 0) { return VIEW_TYPE_HEADER_1 } return VIEW_TYPE_DATA_SOURCE_1 } … }
  4. Sprint 2

  5. I know my game very well!!! class MessAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()

    { … override fun getItemViewType(position: Int): Int { if (position == 0) { return VIEW_TYPE_HEADER_1 } else if (position < dataSource1.size) { return VIEW_TYPE_DATA_SOURCE_1 } else if (position == dataSource1.size + 1) { return VIEW_TYPE_HEADER_2 } else { return VIEW_TYPE_DATA_SOURCE_2; } } … }
  6. Sprint 3

  7. Hold on hold on. I will figure something out class

    HellAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { … override fun getItemViewType(position: Int): Int { if (position == 0) { return VIEW_TYPE_HEADER_1 } else if (position < dataSource1.size) { return VIEW_TYPE_DATA_SOURCE_1 } else if (position == dataSource1.size + 1) { return VIEW_TYPE_HEADER_3 } else if (position < (dataSource1.size + dataSource3.size)) { return VIEW_TYPE_DATA_SOURCE_3 } else if (position == dataSource1.size + dataSource2.size) { return VIEW_TYPE_HEADER_2 } else { return VIEW_TYPE_DATA_SOURCE_2 } } … }
  8. Refactor? class HellAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { … override fun getItemViewType(position:

    Int): Int { if (position == 0) { return VIEW_TYPE_HEADER_1 } else if (position < dataSource1.size) { return VIEW_TYPE_DATA_SOURCE_1 } else if (position == dataSource1.size + 1) { return VIEW_TYPE_HEADER_3 } else if (position < (dataSource1.size + dataSource3.size)) { return VIEW_TYPE_DATA_SOURCE_3 } else if (position == dataSource1.size + dataSource2.size) { return VIEW_TYPE_HEADER_2 } else { return VIEW_TYPE_DATA_SOURCE_2 } } … }
  9. Problems • Endless If-Else If statement

  10. Problems • Endless If-Else If statement • Maintenance

  11. Problems • Endless If-Else If statement • Maintenance • Breaks

    Single Responsibility
  12. Problems • Endless If-Else If statement • Maintenance • Breaks

    Single Responsibility • Cannot reuse
  13. Solution Let’s have our AHA(Android Has an Abstraction) moment Concat

    adapters to the rescue Alternatives : Epoxy, Bento
  14. Concat adapters • Added in recyclerview 1.2.0-alpha02 as MergeAdapter •

    Renamed to ConcatAdapter in 1.2.0-alpha04 • Presents content of multiple adapters in sequence
  15. Adapter 1 Adapter 3 Adapter 2

  16. Why? • Encapsulate adapters rathers than merging data source and

    adapter into one. • Less error-prone. • Code reusability • Reduced code complexity
  17. Show us some code

  18. Concat adapter way!! val adapter1: AwesomeAdapter = ... val adapter2:

    GreatAdapter = ... val adapter3: SingleResponsibilityAdapter = ... val concatenated: ConcatAdapter = ConcatAdapter(adapter1, adapter2, adapter3); recyclerView.setAdapter(concatenated);
  19. API public boolean addAdapter(int index, @NonNull Adapter<? extends ViewHolder> adapter)

    { } public boolean removeAdapter(@NonNull Adapter<? extends ViewHolder> adapter) { }
  20. API public boolean addAdapter(int index, @NonNull Adapter<? extends ViewHolder> adapter)

    { } Adds an adapter at specified index. Note: It’s not the recyclerview adapter
  21. API public boolean removeAdapter(@NonNull Adapter<? extends ViewHolder> adapter) { }

    Removes an adapters from adapter list
  22. API public static final Config DEFAULT = new Config(true, NO_STABLE_IDS);

    public ConcatAdapter(@NonNull List<? extends Adapter<? extends ViewHolder>> adapters) { this(Config.DEFAULT, adapters); } public ConcatAdapter( @NonNull Config config, @NonNull List<? extends Adapter<? extends ViewHolder>> adapters) { }
  23. ConcatAdapter.Config Config(boolean isolateViewTypes, @NonNull StableIdMode stableIdMode) { }

  24. ConcatAdapter.Config Config(boolean isolateViewTypes, @NonNull StableIdMode stableIdMode) { } Local/global view

    pool
  25. ConcatAdapter.Config Config(boolean isolateViewTypes, @NonNull StableIdMode stableIdMode) { } NO_STABLE_IDS: Default.

    Individual adapters stable id would be ignored. ISOLATED_STABLE_IDS: Individual adapters to have stable ids. SHARED_STABLE_IDS: Child adapters should never report the same stable id.
  26. notifyDataSetChanged() public abstract static class Adapter<VH extends ViewHolder> { ...

    public final void notifyDataSetChanged() { mObservable.notifyChanged(); } public abstract static class AdapterDataObserver { ... @Override public void onChanged(@NonNull NestedAdapterWrapper wrapper) { mConcatAdapter.notifyDataSetChanged(); calculateAndUpdateStateRestorationPolicy(); } … }
  27. Q&A

  28. Thank you!