Pass ViewHolder on bind to RecyclerView items instead of ViewBinding

This change lets items get the updated position of their view holder in the adapter. Fixes an issue where the position of items was not updated after being removed from a `SelectableGenericAdapter`.
This commit is contained in:
lynxnb 2022-07-31 01:56:34 +02:00 committed by Mark Collins
parent bb922100cb
commit 8991ccac65
13 changed files with 45 additions and 25 deletions

View File

@ -84,7 +84,8 @@ private typealias InteractionFunction = (appItem : AppItem) -> Unit
class AppViewItem(var layoutType : LayoutType, private val item : AppItem, private val missingIcon : Bitmap, private val onClick : InteractionFunction, private val onLongClick : InteractionFunction) : GenericListItem<LayoutBinding<*>>() {
override fun getViewBindingFactory() = LayoutBindingFactory(layoutType)
override fun bind(binding : LayoutBinding<*>, position : Int) {
override fun bind(holder : GenericViewHolder<LayoutBinding<*>>, position : Int) {
val binding = holder.binding
binding.textTitle.text = item.title
binding.textSubtitle.text = item.subTitle ?: item.loaderResultString(binding.root.context)

View File

@ -46,7 +46,7 @@ open class GenericAdapter : RecyclerView.Adapter<GenericViewHolder<ViewBinding>>
override fun onBindViewHolder(holder : GenericViewHolder<ViewBinding>, position : Int) {
currentItems[position].apply {
adapter = this@GenericAdapter
bind(holder.binding, position)
bind(holder, position)
}
}

View File

@ -24,7 +24,7 @@ abstract class GenericListItem<V : ViewBinding> {
abstract fun getViewBindingFactory() : ViewBindingFactory
abstract fun bind(binding : V, position : Int)
abstract fun bind(holder : GenericViewHolder<V>, position : Int)
/**
* Used for filtering

View File

@ -6,6 +6,7 @@
package emu.skyline.adapter
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import emu.skyline.data.GpuDriverMetadata
import emu.skyline.databinding.GpuDriverItemBinding
@ -18,12 +19,15 @@ open class GpuDriverViewItem(
var onDelete : ((position : Int, wasChecked : Boolean) -> Unit)? = null,
var onClick : (() -> Unit)? = null
) : SelectableGenericListItem<GpuDriverItemBinding>() {
private var position = -1
private var holder : GenericViewHolder<GpuDriverItemBinding>? = null
private val adapterPosition get() = holder?.adapterPosition ?: RecyclerView.NO_POSITION
override fun getViewBindingFactory() = GpuDriverBindingFactory
override fun bind(binding : GpuDriverItemBinding, position : Int) {
this.position = position
override fun bind(holder : GenericViewHolder<GpuDriverItemBinding>, position : Int) {
this.holder = holder
val binding = holder.binding
binding.name.text = driverMetadata.name
if (driverMetadata.packageVersion.isNotBlank() || driverMetadata.packageVersion.isNotBlank()) {
@ -38,17 +42,21 @@ open class GpuDriverViewItem(
binding.radioButton.isChecked = position == selectableAdapter?.selectedPosition
binding.root.setOnClickListener {
selectableAdapter?.selectAndNotify(position)
selectableAdapter?.selectAndNotify(adapterPosition)
onClick?.invoke()
}
onDelete?.let { onDelete ->
binding.deleteButton.visibility = ViewGroup.VISIBLE
binding.deleteButton.setOnClickListener {
val wasChecked = position == selectableAdapter?.selectedPosition
selectableAdapter?.removeItemAt(position)
val pos = adapterPosition
if (pos == RecyclerView.NO_POSITION)
return@setOnClickListener
onDelete.invoke(position, wasChecked)
val wasChecked = pos == selectableAdapter?.selectedPosition
selectableAdapter?.removeItemAt(pos)
onDelete.invoke(pos, wasChecked)
}
} ?: run {
binding.deleteButton.visibility = ViewGroup.GONE

View File

@ -22,7 +22,8 @@ class HeaderRomFilterItem(private val formats : List<RomFormat>, selectedFormat
override fun getViewBindingFactory() = HeaderRomFilterBindingFactory
override fun bind(binding : HeaderRomFilterBinding, position : Int) {
override fun bind(holder : GenericViewHolder<HeaderRomFilterBinding>, position : Int) {
val binding = holder.binding
binding.chipGroup.removeViews(1, binding.chipGroup.childCount - 1)
for (format in formats) {
binding.chipGroup.addView(Chip(binding.root.context, null, R.attr.chipChoiceStyle).apply { text = format.name })

View File

@ -15,8 +15,8 @@ object HeaderBindingFactory : ViewBindingFactory {
class HeaderViewItem(private val text : String) : GenericListItem<SectionItemBinding>() {
override fun getViewBindingFactory() = HeaderBindingFactory
override fun bind(binding : SectionItemBinding, position : Int) {
binding.textTitle.text = text
override fun bind(holder : GenericViewHolder<SectionItemBinding>, position : Int) {
holder.binding.textTitle.text = text
}
override fun toString() = ""

View File

@ -6,6 +6,7 @@
package emu.skyline.adapter.controller
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.GenericViewHolder
import emu.skyline.databinding.ControllerItemBinding
import emu.skyline.di.getInputManager
import emu.skyline.input.ButtonGuestEvent
@ -15,12 +16,13 @@ import emu.skyline.input.ButtonId
* This item is used to display a particular [button] mapping for the controller
*/
class ControllerButtonViewItem(private val controllerId : Int, val button : ButtonId, private val onClick : (item : ControllerButtonViewItem, position : Int) -> Unit) : ControllerViewItem() {
override fun bind(binding : ControllerItemBinding, position : Int) {
override fun bind(holder : GenericViewHolder<ControllerItemBinding>, position : Int) {
val binding = holder.binding
content = button.long?.let { binding.root.context.getString(it) } ?: button.toString()
val guestEvent = ButtonGuestEvent(controllerId, button)
subContent = binding.root.context.getInputManager().eventMap.filter { it.value is ButtonGuestEvent && it.value == guestEvent }.keys.firstOrNull()?.toString() ?: ""
super.bind(binding, position)
super.bind(holder, position)
binding.root.setOnClickListener { onClick.invoke(this, position) }
}

View File

@ -8,6 +8,7 @@ package emu.skyline.adapter.controller
import android.view.ViewGroup
import androidx.core.view.isGone
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.GenericViewHolder
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.ControllerCheckboxItemBinding
@ -19,7 +20,8 @@ object ControllerCheckBoxBindingFactory : ViewBindingFactory {
class ControllerCheckBoxViewItem(var title : String, var summary : String, var checked : Boolean, private val onCheckedChange : (item : ControllerCheckBoxViewItem, position : Int) -> Unit) : GenericListItem<ControllerCheckboxItemBinding>() {
override fun getViewBindingFactory() = ControllerCheckBoxBindingFactory
override fun bind(binding : ControllerCheckboxItemBinding, position : Int) {
override fun bind(holder : GenericViewHolder<ControllerCheckboxItemBinding>, position : Int) {
val binding = holder.binding
binding.textTitle.isGone = title.isEmpty()
binding.textTitle.text = title
binding.textSubtitle.isGone = summary.isEmpty()

View File

@ -19,7 +19,8 @@ import emu.skyline.input.JoyConLeftController
* @param type The type of controller setting this item is displaying
*/
class ControllerGeneralViewItem(private val controllerId : Int, val type : GeneralType, private val onClick : (item : ControllerGeneralViewItem, position : Int) -> Unit) : ControllerViewItem() {
override fun bind(binding : ControllerItemBinding, position : Int) {
override fun bind(holder : GenericViewHolder<ControllerItemBinding>, position : Int) {
val binding = holder.binding
val context = binding.root.context
val controller = context.getInputManager().controllers[controllerId]!!
@ -38,7 +39,7 @@ class ControllerGeneralViewItem(private val controllerId : Int, val type : Gener
GeneralType.SetupGuide -> context.getString(R.string.setup_guide_description)
}
super.bind(binding, position)
super.bind(holder, position)
binding.root.setOnClickListener { onClick.invoke(this, position) }
}

View File

@ -2,6 +2,7 @@ package emu.skyline.adapter.controller
import android.view.ViewGroup
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.GenericViewHolder
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.ControllerHeaderBinding
@ -13,8 +14,8 @@ object ControllerHeaderBindingFactory : ViewBindingFactory {
class ControllerHeaderItem(private val text : String) : GenericListItem<ControllerHeaderBinding>() {
override fun getViewBindingFactory() = ControllerHeaderBindingFactory
override fun bind(binding : ControllerHeaderBinding, position : Int) {
binding.root.text = text
override fun bind(holder : GenericViewHolder<ControllerHeaderBinding>, position : Int) {
holder.binding.root.text = text
}
override fun areItemsTheSame(other : GenericListItem<ControllerHeaderBinding>) = other is ControllerHeaderItem

View File

@ -18,7 +18,8 @@ import emu.skyline.input.StickId
* This item is used to display all information regarding a [stick] and it's mappings for the controller
*/
class ControllerStickViewItem(private val controllerId : Int, val stick : StickId, private val onClick : (item : ControllerStickViewItem, position : Int) -> Unit) : ControllerViewItem(stick.toString()) {
override fun bind(binding : ControllerItemBinding, position : Int) {
override fun bind(holder : GenericViewHolder<ControllerItemBinding>, position : Int) {
val binding = holder.binding
val context = binding.root.context
val inputManager = context.getInputManager()
@ -39,7 +40,7 @@ class ControllerStickViewItem(private val controllerId : Int, val stick : StickI
subContent = "${context.getString(R.string.button)}: $button\n${context.getString(R.string.up)}: $yAxisPlus\n${context.getString(R.string.down)}: $yAxisMinus\n${context.getString(R.string.left)}: $xAxisMinus\n${context.getString(R.string.right)}: $xAxisPlus"
super.bind(binding, position)
super.bind(holder, position)
binding.root.setOnClickListener { onClick.invoke(this, position) }
}

View File

@ -15,13 +15,14 @@ import emu.skyline.input.ControllerType
* This item is used to display the [type] of the currently active controller
*/
class ControllerTypeViewItem(private val type : ControllerType, private val onClick : (item : ControllerTypeViewItem, position : Int) -> Unit) : ControllerViewItem() {
override fun bind(binding : ControllerItemBinding, position : Int) {
override fun bind(holder : GenericViewHolder<ControllerItemBinding>, position : Int) {
val binding = holder.binding
val context = binding.root.context
content = context.getString(R.string.controller_type)
subContent = context.getString(type.stringRes)
super.bind(binding, position)
super.bind(holder, position)
binding.root.setOnClickListener { onClick.invoke(this, position) }
}

View File

@ -8,6 +8,7 @@ package emu.skyline.adapter.controller
import android.view.ViewGroup
import androidx.core.view.isGone
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.GenericViewHolder
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.ControllerItemBinding
@ -21,8 +22,9 @@ open class ControllerViewItem(var content : String = "", var subContent : String
override fun getViewBindingFactory() = ControllerBindingFactory
override fun bind(binding : ControllerItemBinding, position : Int) {
override fun bind(holder : GenericViewHolder<ControllerItemBinding>, position : Int) {
this.position = position
val binding = holder.binding
binding.textTitle.apply {
isGone = content.isEmpty()
text = content