【问题标题】:Use of binding breaks RecyclerView item layout使用绑定中断 RecyclerView 项目布局
【发布时间】:2021-01-23 23:19:27
【问题描述】:

我正在使用 Kotlin 构建一个 Android 应用程序,并决定替换对 findViewById 的调用并使用绑定。这一切都很好,但特别是,当我为 RecyclerView 更改适配器时,它会破坏项目布局。

带有findViewById的原始代码:

class WeightListAdapter(val weights: List<WeightWithPictures>, val onWeightItemClickListener: OnWeightItemClickListener) : RecyclerView.Adapter<WeightListAdapter.WeightHolder>()  {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WeightListAdapter.WeightHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item_weight, parent, false)
        return WeightHolder(view)
    }

    override fun onBindViewHolder(holder: WeightListAdapter.WeightHolder, position: Int) {
        val weightWithPictures = weights[position]
        holder.bind(weightWithPictures)
    }

    override fun getItemCount() = weights.size

    inner class WeightHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
        private lateinit var weight: Weight
        private val weightValueView: TextView = this.itemView.findViewById(R.id.weightValue)
        private val weightDateView: TextView = this.itemView.findViewById(R.id.weightDate)
        private val weightImageView: ImageView = this.itemView.findViewById(R.id.weightImage) as ImageView

这是布局:

但是每当我使用绑定时:

class WeightListAdapter(val weights: List<WeightWithPictures>, val onWeightItemClickListener: OnWeightItemClickListener) : RecyclerView.Adapter<WeightListAdapter.WeightHolder>()  {

    private var _binding: ListItemWeightBinding? = null
    private val binding get() = _binding!!

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WeightListAdapter.WeightHolder {
        _binding = ListItemWeightBinding.inflate(LayoutInflater.from(parent.context))
        val view = binding.root
        return WeightHolder(view)
    }

    override fun onBindViewHolder(holder: WeightListAdapter.WeightHolder, position: Int) {
        val weightWithPictures = weights[position]
        holder.bind(weightWithPictures)
    }

    override fun getItemCount() = weights.size

    inner class WeightHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
        private lateinit var weight: Weight
        private val weightValueView: TextView = binding.weightValue
        private val weightDateView: TextView = binding.weightDate
        private val weightImageView: ImageView = binding.weightImage

布局中断:

关于我在这里做错了什么有什么想法吗?是bug吗?

P.S - 现在,我只是为项目视图添加注释以忽略绑定为 documented here,但我真的很想了解问题所在。

【问题讨论】:

  • 我的回答完全错误(因为我没有正确阅读)但我至少可以给你一个提示!您可以使用lateinit var binding: ListItemWeightBinding 来避免您对binding 变量对所做的事情,并使其保持非空而不需要对其进行初始化。您只需要在读取之前分配一个值(就像您在这里所做的那样)
  • 感谢您的提示,我会尝试,清理代码会很棒。

标签: android kotlin android-recyclerview android-binder


【解决方案1】:

您的绑定需要在其父级的上下文中进行扩展,以便其根视图的布局参数生效:

binding = ListItemWeightBinding.inflate(LayoutInflater.from(parent.context), parent, false)

如果您尝试长期使用它,我认为您正在为适配器创建binding 属性也会给您带来问题。每个 ViewHolder 都拥有一个具有不同绑定实例的不同视图。它现在可以工作了,因为您只在设置每个实例后立即将它用于 ViewHolder 实例化。但如果这就是你的全部意图,你应该只将绑定传递给 ViewHolder 的构造函数并省略适配器的属性。

顺便说一下,这是我用于 ViewHolders 的那种模式。更少的代码。请注意,它不必是 inner 类。

class WeightHolder(binding: ListItemWeightBinding) : RecyclerView.ViewHolder(binding.root), View.OnClickListener {

    fun bind(item: WeightWithPictures) {
        with (binding) {
            // set data for views here
        }
    }
}

【讨论】:

  • 感谢@Tenfour04 缺少父上下文确实是导致问题的原因。
【解决方案2】:

我同意@Tenfour04,使用相同的绑定实例是错误的,但我相信您的问题的根本原因在于绑定逻辑。通过绑定,数据将绑定到视图,但不会立即。所以你的视图被夸大了,但由于绑定发生在稍后阶段,计划在不久的将来发生,item_view 的宽度被缩小了。

所以试试下面的,

// oncreate view logic 
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WeightListAdapter.WeightHolder {
        val binding = ListItemWeightBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return WeightHolder(binding)
    }

// onBindViewHolder logic remains the same

// this remains same as suggested by @Tenfour04 but a change in the bind function
 
class WeightHolder(binding: ListItemWeightBinding) : RecyclerView.ViewHolder(binding.root), View.OnClickListener {

    fun bind(item: WeightWithPictures) {
        with (binding) {
            // set data for views using databindig
            customVariable = item           
            executePendingBindings() // this is important
        }
    }
}

// define the customvariable in your `item_list_view.xml`
        <variable
            name="customVariable"
            type="packagename.WeightWithPictures" />

executePendingBindings() 是我们强制绑定立即发生而不是稍后安排的方式

编辑:

这个答案来自Databinding,而不是ViewBinding

【讨论】:

  • @groo 看看这个stackoverflow-answer
  • 嗨@Sekiro 感谢我不知道 executePendingBindings() 的提示。我不需要它,我相信我们应该小心使用它(只有当我们真的需要它时),因为文档声明它应该在主 UI 线程上运行,我认为如果条目太多,它可能会减慢渲染速度,对吧? developer.android.com/reference/android/databinding/…
  • 总而言之,在需要时使用它,并在带有数据绑定的 recyclerview 中使用它,它可以强制框架立即执行它而不是调度它,并且很高兴知道您的问题已解决,快乐编码:)
  • OP 似乎在使用视图绑定,而不是数据绑定。
  • @Tenfour04 你确定吗?我们无法使用 view binding 将视图与数据绑定,据我所知,viewbindind 取代了昂贵的 findViewById 操作
猜你喜欢
  • 1970-01-01
  • 2020-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-11
  • 1970-01-01
  • 2016-12-06
  • 1970-01-01
相关资源
最近更新 更多