如何在RecyclerView中使用自定义FrameLayout及解决不支持的视图类型异常
异常根因
RecyclerView完全支持以FrameLayout作为item的根布局,和你报的异常没有任何关系。
你遇到的UnsupportedOperationException: Unsupported view type supplied本质是Adapter的视图类型逻辑写错了:
- 报错的触发点是RecyclerView内部在获取ViewHolder时,传入了你在
getItemViewType()方法里返回的int值(报错栈里的2131558525是典型的Android R资源id数值),但你的onCreateViewHolder()方法没有针对这个视图类型做对应处理,直接走到了方法末尾抛出异常的默认分支。 - 最常见的错误写法就是:把布局资源的R.id值直接作为
getItemViewType()的返回值,且没有在创建ViewHolder时匹配这个值加载对应布局。
正确实现「指定item点击后按钮隐藏、进度条显示」的方案
1. 修正Adapter的视图类型逻辑
如果你的RecyclerView所有item都是同一个带进度条、按钮的FrameLayout布局,根本不需要重写getItemViewType(),用默认的统一视图类型即可。
参考实现:
class ListAdapter(private val data: MutableList<ItemState>) : RecyclerView.Adapter<ListAdapter.ItemViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { // 直接inflate你自定义的FrameLayout布局即可 val itemView = LayoutInflater.from(parent.context) .inflate(R.layout.item_with_loader_button, parent, false) return ItemViewHolder(itemView) } override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { holder.bind(data[position]) } override fun getItemCount() = data.size inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { private val progressBar: ProgressBar = view.findViewById(R.id.progress_loader) private val actionBtn: Button = view.findViewById(R.id.action_button) fun bind(state: ItemState) { // 根据数据状态控制View显示,避免复用错位 progressBar.visibility = if (state.isLoading) View.VISIBLE else View.GONE actionBtn.visibility = if (state.isLoading) View.GONE else View.VISIBLE actionBtn.setOnClickListener { val currentPos = bindingAdapterPosition if (currentPos == RecyclerView.NO_POSITION) return@setOnClickListener // 修改对应位置的数据状态为加载中 data[currentPos].isLoading = true // 定向刷新当前item,不要调用全量notifyDataSetChanged notifyItemChanged(currentPos) // 后续在此处插入你的实际异步加载逻辑,加载完成后将isLoading设为false,再次调用notifyItemChanged更新状态即可 } } } // 对应的数据实体类,用来存每个item的加载状态 data class ItemState(var isLoading: Boolean = false) }
2. 修正原有布局的无效属性
你当前写的布局有两个属性失效,需要调整:
android:layout_centerInParent是RelativeLayout的专属属性,放在FrameLayout的子View上不会生效。要让ProgressBar在FrameLayout里居中,需要给ProgressBar加android:layout_gravity="center"。- 之前写的
android:visibility="@{model.showSpinnerTrue}"存在数据绑定的类型不匹配问题:View的visibility接受int类型的枚举值,不直接接受布尔值。正确写法是android:visibility="@{model.showSpinnerTrue ? View.VISIBLE : View.GONE}",或者通过自定义BindingAdapter做布尔值到visibility的映射。
避坑提示
- 绝对不要直接在View的点击事件里硬操作控件的显隐,RecyclerView的item是复用的,滑出屏幕再滑回时会出现状态错乱,所有UI状态都要跟着数据实体走,通过定向刷新绑定更新。
- 如果你需要多种item视图类型,
getItemViewType()要返回自定义的int常量(比如TYPE_NORMAL = 0、TYPE_LOADING = 1),不能直接把R布局资源id当视图类型返回,且要在onCreateViewHolder()里对每个返回的视图类型做对应布局加载。
内容的提问来源于stack exchange,提问作者afgfdg
相关产品推荐
相关产品推荐

