LastAdapter Documentation

repository·master·Indexed 20 days ago

https://github.com/nitrico/lastadapter

A lightweight Android library that eliminates the need to write RecyclerView Adapters and ViewHolders by leveraging Android Data Binding. It provides a reflection-free way to bind model lists to RecyclerViews, supporting multiple view types via class mapping, LayoutHandler, or TypeHandler, and automatic UI updates through ObservableLists.

Tokens
2.2K
Snippets
9
Records
11
Agent score
73%

What's inside LastAdapter

  1. Install LastAdapter via Gradle

    master

    To use LastAdapter, you must enable Android Data Binding in your build.gradle file and add the dependency. If you are using Kotlin, you must also apply the kotlin-kapt plugin and include the data-binding compiler via kapt.

    // apply plugin: 'kotlin-kapt' // this line only for Kotlin projects
    
    android {
        ...
        dataBinding.enabled true 
    }
    
    dependencies {
        compile 'com.github.nitrico.lastadapter:lastadapter:2.3.0'
        // kapt 'com.android.databinding:compiler:GRADLE_PLUGIN_VERSION' // this line only for Kotlin projects
    }
  2. Create item layouts using Data Binding

    master

    LastAdapter relies on Android Data Binding. Every item layout must use <layout> as the root element.

    Crucial Requirement: All item layouts must use the exact same variable name for the data object (e.g., item). This name is used to reference the binding class (e.g., BR.item) when initializing the adapter.

    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
            <variable name="item" type="com.github.yourpackage.item.Header"/>
        </data>
        
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{item.text}"/>
            
    </layout>
  3. How LastAdapter handles different view types

    master

    LastAdapter determines which layout to use for an item based on a hierarchy of resolution:

    1. LayoutHandler: If you provide a custom layout handler, it takes precedence.
    2. TypeHandler: If a type handler is provided, it uses the AbsType returned by getItemType.
    3. Class Mapping: If neither handler is used, it looks up the class in the internal mapping established via .map() calls.

    If no mapping or handler is found for a specific object type, the adapter will throw a RuntimeException.

  4. Use LastAdapter with multiple view types

    master

    You can map different model classes to specific layout resources using the .map() method. This allows the adapter to handle multiple item types automatically.

    To enable automatic UI updates when the list changes, pass an ObservableList as the data source. Use a standard List if you do not require automatic updates.

    // Kotlin
    LastAdapter(listOfItems, BR.item)
               .map<Header>(R.layout.item_header)
               .map<Point>(R.layout.item_point)
               .into(recyclerView)
    // Java
    new LastAdapter(listOfItems, BR.item)
               .map(Header.class, R.layout.item_header)
               .map(Point.class, R.layout.item_point)
               .into(recyclerView);
  5. Use LayoutHandler for complex layout logic

    master

    If your logic for choosing a layout is more complex than simple class-to-layout mapping, use the LayoutHandler interface (or the .layout {} DSL in Kotlin). The handler receives the item and its position, allowing you to return different layout resource IDs based on custom criteria.

    // Kotlin sample
    LastAdapter(listOfItems, BR.item).layout { item, position ->
        when (item) {
            is Header -> if (position == 0) R.layout.item_header_first else R.layout.item_header
            else -> R.layout.item_point 
        }
    }.into(recyclerView)
    // Java sample
    new LastAdapter(listOfItems, BR.item)
               .handler(handler)
               .into(recyclerView);
    
    private LayoutHandler handler = new LayoutHandler() {
        @Override public int getItemLayout(@NotNull Object item, int position) {
            if (item instanceof Header) {
                return (position == 0) ? R.layout.item_header_first : R.layout.item_header;
            } else {
                return R.layout.item_point;
            }
        }
    };
  6. Map data classes to layouts in LastAdapter

    master

    You must tell LastAdapter which layout to use for each class in your list. You can do this using the map function. You can map a class to a simple layout resource ID, or to a more complex AbsType (like Type or ItemType) to handle custom logic.

    // Map a class to a layout resource and a DataBinding variable ID
    adapter.map<MyDataClass>(R.layout.my_item_layout, R.id.data)
    
    // Map a class to a specific AbsType
    adapter.map<MyDataClass>(myCustomType)
  7. Use ObservableListCallback to sync an ObservableList with a RecyclerView.Adapter

    master

    The ObservableListCallback is a utility class that implements ObservableList.OnListChangedCallback. It automatically maps changes in an Android ObservableList (such as item insertions, removals, or range changes) to the corresponding notify methods on a RecyclerView.Adapter. This ensures that your RecyclerView stays in sync with the underlying data source without manual adapter updates.

    Important Threading Requirement: All modifications to the ObservableList must be performed on the main thread. If the callback is triggered from a background thread, the class will throw an IllegalStateException.

    // Assuming you have an ObservableList and a RecyclerView.Adapter
    val observableList = ObservableList<Any>()
    val myAdapter = MyAdapter(observableList)
    
    // Create the callback and register it with the list
    val callback = ObservableListCallback(myAdapter)
    observableList.addOnListChangedCallback(callback)
  8. Use layout() to dynamically select layouts

    master

    The layout function allows you to provide a custom logic for selecting a layout resource based on the item and its position. This is useful when a single class might require different layouts depending on its state or position.

    adapter.layout { item, position ->
        if (item is SpecialItem) R.layout.special_layout else R.layout.normal_layout
    }