Nous allons voir dans cette article comment utiliser un flexible adapter provenant de la librairie suivante :
https://github.com/davideas/FlexibleAdapter
Cette article montre la mise en place de ce Flexible Adapter utilisant deux items. Un premier article vous montrant la mise en place du flexible Adapter avec un seul item est disponible ici
Voici une capture d’écran du résultat final

1 – Voici l’activité principale de l’application
package fr.sebastienlaunay.kotlinflexibleadapterex2
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import fr.sebastienlaunay.kotlinflexibleadapterex2.Model.House
import fr.sebastienlaunay.kotlinflexibleadapterex2.Model.Order
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity(), OrderFlexibleAdapter.OrderListener, OrderFlexibleAdapter.HouseListener {
private var orderList = ArrayList<Order>()
private var houseList = ArrayList<House>()
private lateinit var listAdapter: OrderFlexibleAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
listAdapter = OrderFlexibleAdapter(orderList, houseList, this, this)
laListe.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
adapter = listAdapter
}
val order1 = Order(1, "Ordre 1", "Sous titre 1", false)
val order2 = Order(2, "Ordre 2", "Sous titre 2", false)
val order3 = Order(3, "Ordre 3", "Sous titre 3", true)
val order4 = Order(4, "Ordre 4", "Sous titre 4", false)
val order5 = Order(5, "Ordre 4", "Sous titre 5", true)
val order6 = Order(6, "Ordre 6", "Sous titre 6", false)
val order7 = Order(7, "Ordre 7", "Sous titre 7", false)
orderList.add(order1); orderList.add(order2); orderList.add(order3); orderList.add(order4)
orderList.add(order5); orderList.add(order6); orderList.add(order7)
val house1 = House(1, "Maison 1", "1 pièce")
val house2 = House(2, "Maison 2", "2 pièces")
val house3 = House(3, "Maison 3", "3 pièces")
val house4 = House(4, "Maison 4", "4 pièces")
val house5 = House(5, "Maison 5", "5 pièces")
houseList.add(house1); houseList.add(house2); houseList.add(house3); houseList.add(house4); houseList.add(house5)
listAdapter.setOrderAndHouseData(orderList, houseList)
btnOrder.setOnClickListener { listAdapter.updateOrderData() }
btnHouse.setOnClickListener { listAdapter.updateHouseData() }
btnBoth.setOnClickListener { listAdapter.updateData() }
btnValidate.setOnClickListener { afficherLeResultat(orderList) }
}
private fun afficherLeResultat(orderList: ArrayList<Order>) {
textResult.text = "Vous avez cliqué sur : "
orderList.filter { it.validate == true }.apply {
this.forEach {
textResult.text = textResult.text.toString() + "${it.id} - "
}
}
}
override fun onOrderClicked(orderId: Int) {
orderList.find { it.id == orderId }?.apply {
this.validate = !this.validate
}
listAdapter.updateData()
}
override fun onHouseClicked(houseId: Int) {
houseList.find { it.id == houseId }?.let { house ->
textResult.text = "Vous avez cliqué sur la maison suivante : ${house.title}"
}
}
}
Et le layout associé à cette activité
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Exemple d'utilisation d'un Flexible Adapter "
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" android:id="@+id/textView"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/laListe"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@+id/textView"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/btnValidate"/>
<Button
android:id="@+id/btnOrder"
android:text="Ordre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="4dp"
app:layout_constraintBottom_toTopOf="@+id/textResult"
/>
<Button
android:id="@+id/btnHouse"
android:text="Maison"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
app:layout_constraintBottom_toTopOf="@+id/textResult"
app:layout_constraintStart_toEndOf="@+id/btnOrder"
android:layout_marginLeft="16dp" android:layout_marginStart="16dp"/>
<Button
android:id="@+id/btnBoth"
android:text="LES 2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
app:layout_constraintBottom_toTopOf="@+id/textResult"
app:layout_constraintStart_toEndOf="@+id/btnHouse" android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"/>
<Button
android:id="@+id/btnValidate"
android:text="Valider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="4dp"
app:layout_constraintBottom_toTopOf="@+id/textResult"/>
<TextView
android:id="@+id/textResult"
tools:text="Le résultat est ici "
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
2 – Création de la classe AbsFlexibleAdapter
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.viewholders.FlexibleViewHolder abstract class AbsFlexibleAdapter<FI : AbstractFlexibleItem<VH>, VH : FlexibleViewHolder>(items: List<FI>? = null) : FlexibleAdapter<FI>(items)
3 – Création de la classe AbsFlexibleViewHolder
import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.viewholders.FlexibleViewHolder open class AbsFlexibleViewHolder(view: View, adapter: FlexibleAdapter<out IFlexible<*>>, stickyHeader: Boolean = false) : FlexibleViewHolder(view, adapter, stickyHeader)
4 – Création de l’adapter OrderFlexibleAdapter
class OrderFlexibleAdapter(orderList: List<Order>, houseList: List<House>, private val orderListener: OrderListener, private val houseListener : HouseListener) :
AbsFlexibleAdapter<AbstractFlexibleItem<AbsFlexibleViewHolder>, AbsFlexibleViewHolder>() {
var orderList = orderList
var houseList = houseList
fun updateData() {
setOrderAndHouseData(orderList,houseList)
}
fun updateOrderData() {
setOrderData(orderList)
}
fun updateHouseData() {
setHouseData(houseList)
}
fun setOrderData(orderList: List<Order>) {
val dataToDisplay = mutableListOf<AbstractFlexibleItem<AbsFlexibleViewHolder>>()
orderList.forEach { order ->
dataToDisplay.add(OrderFlexibleItem(order, orderListener))
}
updateDataSet(dataToDisplay)
}
fun setHouseData(houseList: List<House>) {
val houseDataToDisplay = mutableListOf<AbstractFlexibleItem<AbsFlexibleViewHolder>>()
houseList.forEach { house ->
houseDataToDisplay.add(HouseFlexibleItem( house, houseListener))
}
updateDataSet(houseDataToDisplay)
}
fun setOrderAndHouseData(orderList: List<Order>, houseList: List<House>) {
val dataToDisplay = mutableListOf<AbstractFlexibleItem<AbsFlexibleViewHolder>>()
orderList.forEach { order ->
dataToDisplay.add(OrderFlexibleItem(order, orderListener))
}
houseList.forEach { house ->
dataToDisplay.add(HouseFlexibleItem( house, houseListener))
}
updateDataSet(dataToDisplay)
}
interface OrderListener {
fun onOrderClicked(orderId: Int)
}
interface HouseListener {
fun onHouseClicked(houseId: Int)
}
}
5 – Création du 1er Item OrderFlexibleItem
class OrderFlexibleItem(var order: Order, private val listener: OrderFlexibleAdapter.OrderListener) :
AbstractFlexibleItem<AbsFlexibleViewHolder>(), FlexibleAdapter.OnItemClickListener {
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?,
holder: AbsFlexibleViewHolder?,
position: Int,
payloads: MutableList<Any>?
) {
(holder as ViewHolder).bind(order, listener)
}
override fun createViewHolder(
view: View,
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
): AbsFlexibleViewHolder = ViewHolder(view, adapter)
override fun getLayoutRes(): Int = R.layout.item_order
override fun equals(other: Any?): Boolean = other is OrderFlexibleItem
override fun onItemClick(view: View?, position: Int): Boolean {
return false
}
class ViewHolder(view: View, adapter: FlexibleAdapter<out IFlexible<*>>) :
AbsFlexibleViewHolder(view, adapter, false) {
fun bind(order: Order, listener: OrderFlexibleAdapter.OrderListener) {
contentView.setOnClickListener {
order.id.let { orderId ->
listener.onOrderClicked(orderId)
}
}
contentView.title.text = order.title
contentView.subTitle.text = order.subTitle
contentView.checkBox.isChecked = order.validate
}
}
}
La classe ci-dessus étant dépendante du fichier layout item_order.xml suivant :
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/order_view_item_height"
android:layout_marginBottom="5dp"
android:layout_marginLeft="@dimen/horizontal_padding"
android:layout_marginRight="@dimen/horizontal_padding"
android:layout_marginTop="5dp"
android:background="@android:color/white"
app:cardCornerRadius="4dp"
app:cardElevation="4dp"
>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
tools:text="Commande AA-10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="6dp"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/subTitle"
tools:text="0 sac"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
app:layout_constraintTop_toBottomOf="@+id/title"/>
<CheckBox
android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:clickable="false"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
Voici la représentation graphique de ce layout :

6 – Création du 2eme Item HouseFlexibleItem
class HouseFlexibleItem(var house: House, private val houseListener: OrderFlexibleAdapter.HouseListener) :
AbstractFlexibleItem<AbsFlexibleViewHolder>(), FlexibleAdapter.OnItemClickListener {
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?,
holder: AbsFlexibleViewHolder?,
position: Int,
payloads: MutableList<Any>?
) {
(holder as ViewHolder).bind(house, houseListener)
}
override fun createViewHolder(
view: View,
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
): AbsFlexibleViewHolder = ViewHolder(view, adapter)
override fun getLayoutRes(): Int = R.layout.item_house
override fun equals(other: Any?): Boolean = other is HouseFlexibleItem
override fun onItemClick(view: View?, position: Int): Boolean {
return false
}
class ViewHolder(view: View, adapter: FlexibleAdapter<out IFlexible<*>>) :
AbsFlexibleViewHolder(view, adapter, false) {
fun bind(house: House, houseListener: OrderFlexibleAdapter.HouseListener) {
contentView.houseTitle.text = house.title
contentView.houseDescription.text = house.description
contentView.setOnTouchListener { v, event ->
if (event.action == ACTION_DOWN) {
v.animate().translationX(200f).setDuration(100).withEndAction {
v.animate().translationX(0f).duration = 100
}
house.id.let { houseId ->
houseListener.onHouseClicked(houseId)
}
}
true
}
}
}
}
La classe ci-dessus étant dépendante du fichier layout item_order.xml suivant :
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="50dp">
<View
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="1dp"
android:background="@color/colorPrimary"
android:layout_marginTop="0dp"
app:layout_constraintTop_toTopOf="parent"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_margin="8dp"
android:id="@+id/linearLayout"
android:gravity="center_vertical"
app:layout_constraintTop_toBottomOf="@+id/separator"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginRight="8dp">
<TextView
android:id="@+id/houseTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:visibility="visible"
tools:text="Maison 1"/>
<TextView
android:id="@+id/houseDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:gravity="center_vertical"
android:visibility="visible"
tools:text="Description de la Maison"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
Voici la représentation graphique de ce layout :

7 – Il ne manque que les objets utilisés par l’adapter.
Classe Order.kt
class Order(var id: Int, var title: String, var subTitle: String, var validate: Boolean)
Classe House.kt
class House(var id: Int, var title: String, var description: String)
8 – Importer la librairie du Flexible adapter dans le fichier build de votre application
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
// Utilisation FlexibleAdapter -
implementation "eu.davidea:flexible-adapter:5.0.5"
implementation "eu.davidea:flexible-adapter-ui:1.0.0-b5"
}