提问人:rminaj 提问时间:11/17/2023 最后编辑:rminaj 更新时间:11/17/2023 访问量:52
RecyclerView.Adapter 中的 Android Kotlin ImageView 未按预期更新
Android Kotlin ImageView inside RecyclerView.Adapter doesn't update as expected
问:
在最终在我的服务器中实现设备类型的图标后,我将之前使用的临时可绘制对象替换为服务器中的图标,并使用 .顺便说一句,对他们来说,在适配器内。在第一次绘制时,一切都正确“对齐”,标签和图像是正确的配对。但是,如果我进行某种排序,s 中的图像不再与标签对齐,或者它们完全消失了。我的应用程序中有一个行为,首先我将从服务器获取并显示数据,如果本地数据等于服务器的数据,那么我将只使用本地数据。但我认为这不应该影响任何事情,因为我将服务器数据的每个字段保存在数据库上。我还添加了一些日志记录,以查看应该由哪个 URL 检索,它们是每个图标的正确 URL。我调用以重新绘制适配器,因为排序可能会返回与默认列表(即我拥有的所有项目)不同长度的项目。coil
ImageView
ImageView
coil
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"
}
}
答:
2赞
Sanjay S
11/17/2023
#1
问题出在私有函数上。
您需要使用每个 ActuatorDeviceInfo 更新每个 Item 的视图。
您需要使用“项目绑定”视图来设置数据和图像。setIcon
对于语句,bind() 使用每个项的绑定。 但是 setIcon 使用其他一些绑定,这些绑定具有最后一个初始化的视图,根据您的要求,它不应该这样做。
溶液
- 删除变量 .
private lateinit var binding: AdapterActuatorBinding
- 将 onCreateViewHolder 函数中的局部变量绑定声明为 。
val binding =
- 将私有 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"
}
}
您可以通过遵循最佳实践来避免此类问题。
- 不要在嵌套块或内部类中声明相同的变量名称(变量阴影)
评论
0赞
rminaj
11/17/2023
这很有趣。因此,具有/给定了与全局数据绑定不同的数据绑定。ViewHolder
1赞
Sanjay S
11/23/2023
没有“全局”数据绑定。将只有每个项目数据绑定对象。问题是,您将最后一项的数据绑定存储为全局变量(实例变量),但 bind 方法也接收正确的绑定对象。这就像你在回忆你最后一次见面的人,每当任何人打来电话时,你都会像在与你最后一次见面的人交谈一样对待他们。
0赞
SnailBird
11/17/2023
#2
您可以对每个项目进行图像化,将其隔离视图。在视图更新之前,必须对其进行初始化,以避免脏数据影响其他项目。
评论