AdapterDelegates

repository·master·Indexed 25 days ago

https://github.com/sockeqwe/adapterdelegates

A library for building RecyclerView Adapters by composing reusable components (delegates) instead of using monolithic inheritance hierarchies. It supports Java and Kotlin, providing a Kotlin DSL for convenient delegate creation, ViewBinding integration, and DiffUtil support via AsyncListDifferDelegationAdapter. The library includes specialized components like ListDelegationAdapter, AbsListItemAdapterDelegate for reduced casting, and pagination support through the adapterdelegates4-pagination artifact.

Tokens
3.4K
Snippets
11
Records
13
Agent score
34%

What's inside AdapterDelegates

  1. Install Kotlin DSL dependencies

    master

    Kotlin users can use a DSL to write delegates more conveniently. Choose the artifact that matches your preferred view handling method:

    • Standard DSL: com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2
    • Kotlin Android Extensions (Synthetic properties): com.hannesdorfmann:adapterdelegates4-kotlin-dsl-layoutcontainer:4.3.2
    • ViewBinding: com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2
  2. Reduce casting boilerplate with AbsListItemAdapterDelegate

    master

    In Java, use AbsListItemAdapterDelegate<I, T, VH> to avoid manual casting of items and ViewHolders. Specify the item type (I), the list type (T), and the ViewHolder type (VH).

    public class CatListItemAdapterDelegate extends AbsListItemAdapterDelegate<Cat, Animal, CatViewHolder> {
      private LayoutInflater inflater;
    
      public CatListItemAdapterDelegate(Activity activity) {
        inflater = activity.getLayoutInflater();
      }
    
      @Override public boolean isForViewType(Animal item, List<Animal> items, int position) {
        return item instanceof Cat;
      }
    
      @Override public CatViewHolder onCreateViewHolder(ViewGroup parent) {
        return new CatViewHolder(inflater.inflate(R.layout.item_cat, parent, false));
      }
    
      @Override public void onBindViewHolder(Cat item, CatViewHolder vh, @Nullable List<Object> payloads) {
        // 'item' is already cast to Cat
        vh.name.setText(item.getName());
      }
    
      static class CatViewHolder extends RecyclerView.ViewHolder {
        public TextView name;
        public CatViewHolder(View itemView) { super(itemView); name = (TextView) itemView.findViewById(R.id.name); }
      }
    }
  3. Reduce Java boilerplate with ListDelegationAdapter

    master

    Instead of manually implementing all RecyclerView.Adapter methods, extend ListDelegationAdapter<T> (for java.util.List<?>) or AbsDelegationAdapter (for general data sources). This handles the AdapterDelegatesManager wiring for you.

    public class AnimalAdapter extends ListDelegationAdapter<List<Animal>> {
      public AnimalAdapter(Activity activity, List<Animal> items) {
        delegatesManager.addDelegate(new CatAdapterDelegate(activity))
                        .addDelegate(new DogAdapterDelegate(activity));
        setItems(items);
      }
    }
  4. Implement AdapterDelegates in Java

    master

    In Java, extend AdapterDelegate<T> and implement the lifecycle methods. You then use an AdapterDelegatesManager to bridge the RecyclerView.Adapter and your delegates.

    // 1. Define the Delegate
    public class CatAdapterDelegate extends AdapterDelegate<List<Animal>> {
      private LayoutInflater inflater;
    
      public CatAdapterDelegate(Activity activity) {
        inflater = activity.getLayoutInflater();
      }
    
      @Override public boolean isForViewType(@NonNull List<Animal> items, int position) {
        return items.get(position) instanceof Cat;
      }
    
      @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
        return new CatViewHolder(inflater.inflate(R.layout.item_cat, parent, false));
      }
    
      @Override public void onBindViewHolder(@NonNull List<Animal> items, int position, @NonNull RecyclerView.ViewHolder holder, @Nullable List<Object> payloads) {
        CatViewHolder vh = (CatViewHolder) holder;
        Cat cat = (Cat) items.get(position);
        vh.name.setText(cat.getName());
      }
    
      static class CatViewHolder extends RecyclerView.ViewHolder {
        public TextView name;
        public CatViewHolder(View itemView) { super(itemView); name = (TextView) itemView.findViewById(R.id.name); }
      }
    }
    
    // 2. Use Manager in the Adapter
    public class AnimalAdapter extends RecyclerView.Adapter {
      private AdapterDelegatesManager<List<Animal>> delegatesManager;
      private List<Animal> items;
    
      public AnimalAdapter(Activity activity, List<Animal> items) {
        this.items = items;
        delegatesManager = new AdapterDelegatesManager<>();
        delegatesManager.addDelegate(new CatAdapterDelegate(activity))
                        .addDelegate(new DogAdapterDelegate(activity));
      }
    
      @Override public int getItemViewType(int position) { return delegatesManager.getItemViewType(items, position); }
      @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return delegatesManager.onCreateViewHolder(parent, viewType); }
      @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { delegatesManager.onBindViewHolder(items, position, holder); }
      @Override public int getItemCount() { return items.size(); }
    }
  5. Migrate from Version 3.x to 4.0

    master

    Migration from AdapterDelegates3 to AdapterDelegates4 involves moving from Android Support libraries to AndroidX.

    1. Replace package names: Change com.hannesdorfmann.adapterdelegates3 to com.hannesdorfmann.adapterdelegates4.
    2. Update RecyclerView: Replace android.support.v7.widget.RecyclerView with androidx.recyclerview.widget.RecyclerView.
    3. Update Annotations: Replace android.support.annotation.NonNull with androidx.annotation.NonNull.
  6. Use the Kotlin DSL to create an AdapterDelegate

    master

    Use the adapterDelegate function to define a delegate. The initializer block is called once in onCreateViewHolder (use it for findViewById and click listeners). The bind block is called during onBindViewHolder (use it to set data from the item).

    Note on item access: The item is set lazily in onBindViewHolder. Only use it for deferred calls like click listeners.

    fun catAdapterDelegate(itemClickedListener : (Cat) -> Unit) = adapterDelegate<Cat, Animal>(R.layout.item_cat) {
    
        // Initializer block (onCreateViewHolder)
        val name : TextView = findViewById(R.id.name)
        name.setClickListener { itemClickedListener(item) } 
    
        bind { diffPayloads -> 
            // Bind block (onBindViewHolder)
            name.text = item.name 
        }
    }
  7. Use Kotlin DSL with ViewBinding

    master

    When using ViewBinding, use adapterDelegateViewBinding. You must provide a lambda to inflate the binding class.

    fun cat2AdapterDelegate(itemClickedListener : (Cat) -> Unit) = adapterDelegateViewBinding<Cat, DisplayableItem, ItemCatBinding>(
        { layoutInflater, root -> ItemCatBinding.inflate(layoutInflater, root, false) }
    ) {
        binding.name.setOnClickListener {
            itemClickedListener(item)
        }
        bind {
            binding.name.text = item.name
        }
    }
  8. Customize item matching with the `on` parameter

    master

    By default, a delegate handles an item if item instanceof Type. You can override this behavior using the on lambda, which returns true if the delegate should handle the item at a specific position.

    adapterDelegate<Cat, Animal> (
        layout = R.layout.item_cat,
        on = { item: Animal, items: List, position: Int ->
            if (item is Cat && position == 0)
                true
            else
                false
        }
    ){
        ...
        bind { ... }
    }
  9. Use AsyncListDifferDelegationAdapter for DiffUtil support

    master

    If you want to use ListAdapter functionality (background thread diffing and automatic animations) with AdapterDelegates, use AsyncListDifferDelegationAdapter. This class acts as an equivalent to ListAdapter but allows you to compose your adapter using multiple AdapterDelegate instances.

    public class DiffAdapter extends AsyncListDifferDelegationAdapter<Animal> {
        public DiffAdapter() {
            super(DIFF_CALLBACK) // Your diff callback for diff utils
            delegatesManager
                .addDelegate(new DogAdapterDelegate());
                .addDelegate(new CatAdapterDelegate());
        }
    }