Kotlin自定义ArrayAdapter报错排查:InflateException及相关异常
问题分析与解决方案
错误根源
你遇到的两个错误本质是同一问题的两种表现:
- InflateException:自定义item布局
customcardcomponent.xml的根布局未设置有效的layout_width和layout_height属性,导致LayoutInflater在传入parent但attachToRoot=false时,无法为item生成适配ListView的LayoutParams。 - 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
相关产品推荐
相关产品推荐

