Android Fragment调用resources.getColor()冻结 动态添加View不生效
问题根因
你遇到的资源加载冻结、视图高度为0两个问题,是两个独立代码错误共同导致的:
- POST请求回调默认运行在子线程,你直接在子线程中执行了资源加载、视图创建、视图添加等所有UI操作。Android UI相关操作不是线程安全的,资源访问、View操作必须在主线程执行,子线程调用时不会稳定抛出明确异常,常表现为随机卡顿、操作无响应、逻辑执行到一半中断,和你日志卡在
getColor调用位置的现象完全匹配。 - inflate banner布局时传入
null作为父容器,会导致布局XML根节点定义的layout_height、layout_margin等布局参数完全失效。Android中layout_*属性是由父容器解析生效的,没有传入父容器时这些参数会直接被丢弃,后续调用addView时如果没有手动补全布局参数,视图就会按0高度测量,即使你在布局里设置了minHeight也不会生效。
修复方案
1. 所有UI操作强制切换到主线程执行
POST请求拿到返回结果后,必须先切到主线程再执行视图相关逻辑:
// 以OkHttp/Retrofit回调为例 apiService.getChatList().enqueue(object : Callback<List<ChatData>> { override fun onResponse(call: Call<List<ChatData>>, response: Response<List<ChatData>>) { val chatList = response.body() ?: return // 切换到主线程操作UI Handler(Looper.getMainLooper()).post { addChatsToRoot(chatList) } } override fun onFailure(call: Call<List<ChatData>>, t: Throwable) { t.printStackTrace() } })
如果用协程发起请求,直接将协程上下文指定为Dispatchers.Main即可,不要在IO调度器中直接操作UI。
你可以加一行日志验证线程是否正确:
Log.d("VIEW", "运行在主线程: ${Looper.myLooper() == Looper.getMainLooper()}")
日志返回true时线程配置正确,此时你之前缺失的"Banner created2"、"Chats are added to the layout"两条日志都会正常打印。
2. 修复inflate传参,禁止传null作为父容器
inflate布局时传入父容器chatsRoot作为root参数,attachToRoot参数传false(因为你后续要手动调用addView),这样才能正确解析根节点的布局参数:
// 原错误写法:val banner = inflater.inflate(R.layout.chat_banner, null, false) val banner = inflater.inflate(R.layout.chat_banner, chatsRoot, false)
3. 替换废弃的getColor调用,使用兼容API
直接调用resources.getColor()是已经废弃的API,在不同系统版本、主题配置下容易出现兼容问题,替换为ContextCompat的兼容方法即可:
// 替换原getColor逻辑 val colorFrom = ContextCompat.getColor(requireContext(), R.color.banner_regular) val colorTo = ContextCompat.getColor(requireContext(), R.color.banner_clicked)
注意:Fragment中尽量不要直接访问resources属性,统一通过requireContext()获取上下文后再访问资源,避免Fragment脱离Activity时触发空指针。
以上三步修改完成后,视图会正常添加到容器中,高度、点击动画都会按预期生效。
内容的提问来源于stack exchange,提问作者Nikitiy
相关产品推荐
相关产品推荐

