DSL

AdaptScope

AdaptScope provides functions for declarations in the adapter. Following are the most important ones:

create(createView: (parent: ViewGroup) -> ViewSource)

This is used as the default view creator when no viewtype if defined or if no viewtype is used.

adapt<Person> {
    create {
        val personBinding = LayoutPersonBinding.inflate(LayoutInflater.from(it.context), it, false)
        ViewSource.BindingViewSource(personBinding, ViewBinding::getRoot)
    }.bind { /* ... */ }
}

create(viewType: Int, createView: (parent: ViewGroup) -> ViewSource)

This is used as the creator for the specified viewType (which is an Int). This is to be used in conjunction with defineViewTypes. Refer below for sample for both together.

defineViewTypes(mapToViewType: (data: T, position: Int) -> Int)

Lets you decide the viewtype given some data and position.

adapt<Person> {
    defineViewTypes { data, position ->
        if (data.name.startsWith("A")) {
            return 1
        } else {
            return 0
        }
    }
    create(0) {
        // view source for type 0
    }.bind { /* ... */ }
    create(1) {
        // view source for type 1
    }.bind { /* ... */ }
}

contentEquals(checkContentEquality: (data: T, otherData: T) -> Boolean)

Returns whether both provided data items are the same. This is used to see if the data has updated and needs to be rebound to the view.

adapt<Person> {
    contentEquals { data, otherData ->
        data == otherData
    }
}

contentEquals(checkContentEquality: (data: T, otherData: T) -> Boolean)

Returns whether both provided data have the same content. This is used to see if the data has updated and needs to be rebound to the view.

This is Optional. The default implementation checks object equality (i.e, (a == b) or Object.equals(a, b) ). This can ussually be ignored safely while using data classes.

adapt<Person> {
    contentEquals { data, otherData ->
        data == otherData
    }
}

itemEquals(checkEquality: (data: T, otherData: T) -> Boolean)

Returns where the provided items are the same. This is used to know if the items represent the same thing.

This is Optional. The default implementation checks reference equality (i.e, (a === b)). It is advisable to provide an implementation of this item for better performance.

adapt<Person> {
    itemEquals { data, otherData ->
        data.name == otherData.name
    }
}

BindScope

index: Int

Current adapter index for your binding. Is a computed property that fetches the actual current index so its safe to use inside functions.

Capturing this onto a variable and then using it inside callbacks will result in wrong index used, make sure to use the computed property directly if used inside a callback. This is because the index of the same binding may change due to adapter rearranging due to updated data.

    adapt {
        create { /* ... */ }.bind {
            // val currentIndex = index <-- Do not do this to use inside callbacks like click listeners, 
            // instead directly use index

            binding.myBtn.setOnClickListener {
                // onMyButtonClicked(currentIndex) <-- Do not use this. It may lead to your click listeners using old index even after position changes
                // Instead directly use the index computer property like below
                onMyButtonClicked(index)
            }
        }
    }

data: T

Provides the data to be bound to the current binding

binding: V

Provides the viewtype created provided in the create call, this is the view or binding to which the data must be bound

viewHolder: RecyclerView.ViewHolder

Provides the internal viewholder used for the current bind operation, this is provided so as to allow usage with other libraries which require interacting with ViewHolders such as ItemMoveHelper. You can ignore this in most normal cases.

LifecycleRenewAttachable

withLifecycle(attach: BindScope.(LifecycleOwner) -> Unit)

Provides access to Lifecycle of specific viewholder. This can safely be used to add lifecycle listeners, livedata observers, fetch lifecycleScope's (CoroutineScope scoped to this lifecycle). This is only called once per lifecycle creation. If data for a viewholder changes, the lifecycle is destroyed and a new one is created for which this is called again.

    adapt {
        create { /* ... */ }.bind { /* ... */ }.withLifecycle { lifecycleOwner ->
            val friendsLiveData: LiveData<List<Friend>> = myViewModel.fetchFriendsLiveDataForPerson(data.name)

            // Allows listening to livedata with proper lifecycle handling
            friendsLiveData.observe(lifecycleOwner) { friends ->
                binding.details.text = if (friends.isEmpty()) "You have no friends!" else "You have ${friends.size} friends"
            }

            // Allows using coroutineScope scoped to this binding (Requires androidx.lifecycle)
            lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
                // ...
            }
        }
    }