RecyclerView.Adapter 内的 Android Kotlin ImageView 未按预期更新

标签 android kotlin android-recyclerview imageview android-databinding

最终在我的服务器中实现设备类型的图标后,我用服务器中的图标替换了之前使用的临时可绘制对象,并使用 coil 加载它们。顺便说一句,它们的 ImageView 位于适配器内部。第一次绘制时,一切都正确“对齐”,标签和图像是正确的配对。但是,如果我进行某种排序,ImageView 中的图像将不再与标签对齐,或者完全消失。我的应用程序中有一个行为,首先我将从服务器获取并显示数据,如果本地数据等于服务器的数据,那么我将只使用本地数据。但我认为这不会影响任何事情,因为我将服务器数据的每个字段保存在数据库上。我还添加了一些日志记录,以查看 coil 应检索哪些 URL,它们是每个图标的正确 URL。我调用 notifyDatasetChanged() 来重绘适配器,因为排序可能会返回与默认列表(我拥有的所有项目)不同长度的项目。

这是我的适配器的代码:

class ActuatorDeviceInfoAdapter(
    val ctx: FragmentActivity,
    var itemLst: MutableList<ActuatorDeviceInfo>,
    val showToggle: Boolean
) : RecyclerView.Adapter<ActuatorDeviceInfoAdapter.ViewHolder>() {
    private lateinit var binding: AdapterActuatorBinding
    var onItemClick: ((ActuatorDeviceInfo) -> Unit)? = null
    var onToggleClick: ((ActuatorDeviceInfo, Boolean, AdapterActuatorBinding) -> Unit)? = null

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): ActuatorDeviceInfoAdapter.ViewHolder {
        binding = AdapterActuatorBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ActuatorDeviceInfoAdapter.ViewHolder, position: Int) {
        val pic = itemLst[position]
        holder.bind(pic)
    }

    override fun getItemCount(): Int = itemLst.size

    inner class ViewHolder(private val binding: AdapterActuatorBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun bind(actuatorDeviceInfo: ActuatorDeviceInfo) {
            binding.actuatorDevInfo = actuatorDeviceInfo

            if (showToggle) {
                binding.tglStatus.visibility = View.VISIBLE
                binding.tglStatus.isChecked = actuatorDeviceInfo.status
            } else {
                binding.tglStatus.visibility = View.GONE
            }

            setIcon(actuatorDeviceInfo.type!!)
        }

        init {
            itemView.setOnClickListener {
                onItemClick?.invoke(itemLst[adapterPosition])
            }
            binding.tglStatus.setOnClickListener {
                onToggleClick?.invoke(itemLst[adapterPosition], binding.tglStatus.isChecked, binding)
            }
        }
    }

    private fun setIcon(type: ActuatorType) {
        val iconPath = "${Utils.instance.getServerHost(ctx)}${type.iconPath}"
        binding.imgIcon.load(iconPath)
    }

    companion object{
        const val TAG = "ActuatorDeviceInfoAdapter"
    }
}

最佳答案

问题出在私有(private)函数setIcon中。 您需要使用每个 ActuatorDeviceInfo 更新每个项目的 View 。 您需要使用 Item View 的 Binding 来设置数据和图像。

bind() 内的 For 语句使用每个项目的绑定(bind)。 但是 setIcon 使用了一些其他具有最后初始化 View 的绑定(bind),但它不应该按照您的要求。

解决方案

  1. 删除变量 private Lateinit var 绑定(bind):AdapterActuatorBinding
  2. 将 onCreateViewHolder 函数中的局部变量绑定(bind)声明为 val binding =
  3. 将私有(private) setIcon 函数移至 ViewHolder 类。

以上 1,2 步骤是可选的。

结果

class ActuatorDeviceInfoAdapter(
    val ctx: FragmentActivity,
    var itemLst: MutableList<ActuatorDeviceInfo>,
    val showToggle: Boolean
) : RecyclerView.Adapter<ActuatorDeviceInfoAdapter.ViewHolder>() {
    // 1. Removed
    // private lateinit var binding: AdapterActuatorBinding
    var onItemClick: ((ActuatorDeviceInfo) -> Unit)? = null
    var onToggleClick: ((ActuatorDeviceInfo, Boolean, AdapterActuatorBinding) -> Unit)? = null

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): ActuatorDeviceInfoAdapter.ViewHolder {
        // 2. Declared
        val binding = AdapterActuatorBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ActuatorDeviceInfoAdapter.ViewHolder, position: Int) {
        val pic = itemLst[position]
        holder.bind(pic)
    }

    override fun getItemCount(): Int = itemLst.size

    inner class ViewHolder(private val binding: AdapterActuatorBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun bind(actuatorDeviceInfo: ActuatorDeviceInfo) {
            binding.actuatorDevInfo = actuatorDeviceInfo

            if (showToggle) {
                binding.tglStatus.visibility = View.VISIBLE
                binding.tglStatus.isChecked = actuatorDeviceInfo.status
            } else {
                binding.tglStatus.visibility = View.GONE
            }

            setIcon(actuatorDeviceInfo.type!!)
        }
        // 3. Moved
        private fun setIcon(type: ActuatorType) {
            val iconPath = "${Utils.instance.getServerHost(ctx)}${type.iconPath}"
            binding.imgIcon.load(iconPath)
        }

        init {
            itemView.setOnClickListener {
                onItemClick?.invoke(itemLst[adapterPosition])
            }
            binding.tglStatus.setOnClickListener {
                onToggleClick?.invoke(itemLst[adapterPosition], binding.tglStatus.isChecked, binding)
            }
        }
    }

    

    companion object{
        const val TAG = "ActuatorDeviceInfoAdapter"
    }
}

您可以通过遵循最佳实践来避免此类问题。

  • 不要在嵌套 block 或内部类中声明相同的变量名称(变量遮蔽)

关于RecyclerView.Adapter 内的 Android Kotlin ImageView 未按预期更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77498987/

相关文章:

android - Intent.FLAG_GRANT_READ_URI_PERMISSION 标志用法

Android 位置管理器错误 + 附加日志文件

android - 关系房间数据库 : The class must be either entity or database view

java - Android KeyStore 加密和解密数据

三星 Galaxy S4 联系人列表的 Java 边栏代码

带有 Kotlin 和 Spring BootPost 的 REST API 返回空对象

kotlin - Jetpack Compose - 重组时遇到问题

android - 如何在回收站 View 中显示不同尺寸的图像

java - 从 RecyclerView 中删除最后一项不起作用

Android:在 Adapter (RecyclerView) 中手动清除 Glide 是一种好习惯吗?