A declarative way to use recycler views.
Download or clone the repo and try the demo app.
Edit your build.gradle file
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Then add as dependency to your app/build.gradle
dependencies {
...
implementation 'com.github.dariopellegrini:DeclarativeRecycler:v0.6.2'
}
With this library you can populate your recycler view in a very declarative way, adding or removing rows and configuring action.
First of all instantiate a RecyclerManager
recyclerManager = RecyclerManager(recyclerView, layoutManager)
Then it's possible to add a row very easily (Kotlin extension are used here)
recyclerManager.push(BasicRow(
layoutID = R.layout.layout_card_cell_left, // The layout resource of the row
configuration = {
itemView, position ->
// Configuration:
// itemView is the row view containing layout elements
// position is the row position
itemView.leftMessageTextView.text = message
itemView.leftDateTextView.text = "${DateFormat.format("HH:mm:ss", Date())}"
},
onClick = { // Closure executed on row click
itemView, position ->
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
},
onLongClick = { // Closure executed on row long click
itemView, position ->
recyclerManager.remove(position, true, false)
}), animated = true, scroll = true) // Animations
Each row needs at least a layout resource id. Configuration onClick and onLongClick closures are optional. In order to add rows to recycler manager the following methods are available both for a row and list of rows:
- push;
- append;
- add at position.
In order to remove rows to recycler manager the following methods are available both for a row and list of rows:
- pop;
- removeLast;
- remove Row;
- remove at position;
- remove with closure;
- remove rows;
- clear.
After any modification to the rows list it is necessary to call
recyclerManager.reload()
For each modification method, 2 flag are available:
- animation: if true the modification comes with an animation (default to false);
- scroll: if true and if animation is true, after the rows modification the recycler view will scroll to that added or removed row position (default to false).
If animation is true it's not necessary to call the reload method.
Above example uses BasicRow class to configure a row. It comes with the library and implements Row interface, which can of course be implemented by a custom class.
// UserRow implements Row interface and must conform to it.
class UserRow(val message: String, val clicked: () -> Unit, val longClicked: (Int) -> Unit): Row {
// Mandatory
override val layoutID: Int
get() = R.layout.layout_card_cell_right
// Optional
override val configuration: ((View, Int) -> Unit)?
get() = {
itemView, _ ->
itemView.rightMessageTextView.text = message
itemView.rightDateTextView.text = "${DateFormat.format("HH:mm:ss", Date())}"
}
// Optional
override val onClick: ((View, Int) -> Unit)?
get() = {
_, _ ->
clicked()
}
// Optional
override val onLongClick: ((View, Int) -> Unit)?
get() = {
_, position ->
longClicked(position)
}
}
// Than can be added to the recyclerManager
recyclerManager.push(UserRow(
message = message,
clicked = {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
},
longClicked = { position ->
recyclerManager.remove(position, true, false)
})
, true, true)
An alternative to Row interface implementation is a BasicRow subclass
// ResponseRow inherits from BasicRow, which implements Row.
// Because ResponseRow inherits from BasicRow, all its attributes must be passed to BasicRow contructor.
class ResponseRow(val message: String, onClick: () -> Unit, onLongClick: (Int) -> Unit):
BasicRow(
layoutID = R.layout.layout_card_cell_left,
configuration = {
itemView, position ->
itemView.leftMessageTextView.text = message
itemView.leftDateTextView.text = "${DateFormat.format("HH:mm:ss", Date())}"
},
onClick = {
_, _ ->
onClick()
},
onLongClick = {
_, position ->
onLongClick(position)
}
)
Starting from version 0.7 DiffRecycerManager
has been introduced. It uses DiffUtils
to compute dynamic operations on list, giving better performances.
First create a Row
model which implements Differentiable
interface. This interface needs for 2 functions that specify difference between 2 element of the list.
class MessageRow(val message: String, val click: (MessageRow) -> Unit): Row, Differentiable<MessageRow> {
override val layoutID = R.layout.layout_card_cell_left
override val configuration: ((View, Int) -> Unit)? = { itemView, position ->
itemView.leftMessageTextView.text = message
itemView.leftDateTextView.text = "${android.text.format.DateFormat.format("HH:mm:ss", java.util.Date())}"
itemView.setOnClickListener {
click(this)
}
}
// Function which check if the 2 elements are the same
override fun isTheSame(new: MessageRow): Boolean {
return message == new.message
}
// Function which check if the 2 elements have the same content
override fun hasSameContent(new: MessageRow): Boolean {
return message == new.message
}
}
Than create a DiffRecyclerManager
and update the content using a new list.
val recyclerManager = DiffRecyclerManager<DiffRow>(recyclerView, LinearLayoutManager(this))
...
recyclerManager.reload(newList)
This will update RecyclerView
content with animation, making a difference between old and new list with the rules specified in Differentiable
interface functions.
Apart from this, add, remove and clear methods are still supported.
Usually RecyclerView
are binded to a list that represents their content. From version 0.7 it's possible to bind a list to a DiffRecyclerManager
.
var list: List<MessageRow> by bind {
recyclerManager
}
After that, every update to list
variable will update the content of DiffRecyclerManager
.
- Tests.
Dario Pellegrini, [email protected]