You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin自定义ArrayAdapter报错排查:InflateException及相关异常

问题分析与解决方案

错误根源

你遇到的两个错误本质是同一问题的两种表现:

  1. InflateException:自定义item布局customcardcomponent.xml的根布局未设置有效的layout_width和layout_height属性,导致LayoutInflater在传入parent但attachToRoot=false时,无法为item生成适配ListView的LayoutParams。
  2. UnsupportedOperationException:ListView属于AdapterView,它会自行管理子View的添加逻辑,不允许外部调用addView,因此不能将attachToRoot设为true。

修复步骤

1. 修正item布局文件(customcardcomponent.xml)

确保根布局明确设置宽高属性,示例如下:

<!-- 根布局示例,根据你的实际布局调整 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:orientation="vertical">

    <!-- 你的TextView、ProgressBar等控件 -->
</LinearLayout>

必须指定layout_width和layout_height,否则LayoutInflater无法生成正确的布局参数。

2. 修正CustomArrayAdapter的getView方法

移除@SuppressLint("ViewHolder"),实现ViewHolder模式提升性能,同时改用parent的context来inflate布局:

class CustomArrayAdapter(private val context: Context, private val customList: ArrayList<Component>)
    : ArrayAdapter<Component>(context, R.layout.customcardcomponent, customList) {

    inner class ViewHolder {
        lateinit var description: TextView
        lateinit var progressText: TextView
        lateinit var progressBar: LinearProgressIndicator
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val viewHolder: ViewHolder
        val itemView: View

        if (convertView == null) {
            // 使用parent的context确保布局参数正确
            itemView = LayoutInflater.from(parent.context).inflate(R.layout.customcardcomponent, parent, false)
            viewHolder = ViewHolder()
            viewHolder.description = itemView.findViewById(R.id.Description)
            viewHolder.progressText = itemView.findViewById(R.id.ProgressBarText)
            viewHolder.progressBar = itemView.findViewById(R.id.ProgressBar)
            itemView.tag = viewHolder
        } else {
            itemView = convertView
            viewHolder = convertView.tag as ViewHolder
        }

        // 绑定数据
        val component = customList[position]
        viewHolder.description.text = component.description
        viewHolder.progressText.text = "${component.current}/${component.max}"
        viewHolder.progressBar.max = component.max
        viewHolder.progressBar.progress = component.current

        return itemView
    }
}
  • 用parent.context替代Activity的context,既避免潜在内存泄漏,又能确保LayoutInflater获取正确的父布局参数。
  • 实现ViewHolder模式,避免重复调用findViewById,提升列表滚动性能。

3. 修正ListViewPage中设置Adapter的逻辑

将设置Adapter的代码移到循环结束后,避免重复操作:

class ListViewPage : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_listview, container, false)
        val customListView = view.findViewById<ListView>(R.id.customListView)

        val listDescriptions = arrayOf(
            "description 1",
            "description 2",
            "description 3",
            "description 4"
        )
        val listMax = arrayOf(5, 10, 25, 50)
        val listCurrent = arrayOf(current, current, current, current)

        val customList: ArrayList<Component> = ArrayList()
        for (i in listDescriptions.indices) {
            val component = Component(listMax[i], listCurrent[i], listDescriptions[i])
            customList.add(component)
        }
        // 循环结束后统一设置Adapter
        customListView.adapter = CustomArrayAdapter(requireContext(), customList)

        return view
    }
}
  • 用requireContext()替代context as Activity,避免context为空的安全问题。
  • 循环结束后再设置Adapter,逻辑更清晰,也避免多次设置的无效操作。

额外提示

  • 避免在Adapter中持有Activity引用,改用Context或parent.context,降低内存泄漏风险。
  • ListView的layout_height建议设为match_parent,wrap_content可能导致列表高度计算异常,影响滚动体验。

内容的提问来源于stack exchange,提问作者J. Weidenhagen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 21:45:29