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" }