View->LayoutInflater

LayoutInflater.inflate()XML布局文件变成View对象

获取LayoutInflater对象的方式

//1、通过 LayoutInflater 的静态方法 from 获取
val layoutInflater: LayoutInflater = LayoutInflater.from(this)

//2、通过系统服务 getSystemService 方法获取
val layoutInflater: LayoutInflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

//3、如果是在 Activity 或 Fragment 可直接获取到实例
layoutInflater //相当于调用 getLayoutInflater()

LayoutInflater.inflate()方法详解

public View inflate(
	@LayoutRes int resource, // XML资源文件的id
	@Nullable ViewGroup root, // 要添加的父布局
	boolean attachToRoot // 是否添加,默认为true
){}

XML文件

  • R.layout.activity_main根布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_ll"
    android:layout_width="300dp"
    android:layout_height="300dp"
    android:gravity="center"
    android:background="@android:color/holo_red_light">
    <TextView
        android:id="@+id/root_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
</LinearLayout>
  • R.layout.inflate_layout要动态填充的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/inflate_ll"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:gravity="center"
    android:background="@android:color/holo_blue_light">
    <TextView
        android:id="@+id/inflate_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello inflate" />
</LinearLayout>

如果root==null,不管attachRoottrue还是false

const val TAG = "Yang"
class MainActivity : AppCompatActivity() {
    var rootLinearLayout : LinearLayout ?= null
    var inflateLinearLayout : LinearLayout ?= null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        rootLinearLayout = findViewById(R.id.root_ll)
        inflateLinearLayout = layoutInflater.inflate(R.layout.inflate_layout, null) as? LinearLayout
        Log.i(TAG, "rootLinearLayout = ${rootLinearLayout}, rootLinearLayout?.layoutParams = ${rootLinearLayout?.layoutParams}, rootLinearLayout?.parent = ${rootLinearLayout?.parent}")
        Log.i(TAG, "<---------最外层LinearLayout start----------->")
        rootLinearLayout?.forEach {
            Log.i(TAG, "child = $it, child.layoutParams = ${it.layoutParams}")
        }
        Log.i(TAG, "<---------最外层LinearLayout end----------->")


        Log.i(TAG, "addView before inflateLinearLayout = ${inflateLinearLayout}, inflateLinearLayout?.layoutParams = ${inflateLinearLayout?.layoutParams}, inflateLinearLayout?.parent = ${inflateLinearLayout?.parent}")
        Log.i(TAG, "<---------内层LinearLayout addView before start----------->")
        inflateLinearLayout?.forEach {
            Log.i(TAG, "child = $it, child.layoutParams = ${it.layoutParams}")
        }
        Log.i(TAG, "<---------内层LinearLayout addView before end----------->")


        rootLinearLayout?.addView(inflateLinearLayout)
        Log.i(TAG, "addView after inflateLinearLayout = ${inflateLinearLayout}, inflateLinearLayout?.layoutParams = ${inflateLinearLayout?.layoutParams}, inflateLinearLayout?.parent = ${inflateLinearLayout?.parent}")
        Log.i(TAG, "<---------内层LinearLayout addView after start----------->")
        inflateLinearLayout?.forEach {
            Log.i(TAG, "child = $it, child.layoutParams = ${it.layoutParams}")
        }
        Log.i(TAG, "<---------内层LinearLayout addView after end----------->")
    }
}

// log result
2024-05-16 20:48:47.437 21332-21332 Yang        I  rootLinearLayout = android.widget.LinearLayout{40b9e68 V.E...... ......ID 0,0-0,0 #7f0a03cf app:id/root_ll}, rootLinearLayout?.layoutParams = android.widget.FrameLayout$LayoutParams@fa65281, rootLinearLayout?.parent = androidx.appcompat.widget.ContentFrameLayout{7ba9f26 V.E...... ......I. 0,0-0,0 #1020002 android:id/content}
2024-05-16 20:48:47.437 21332-21332 Yang        I  <---------最外层LinearLayout start----------->
2024-05-16 20:48:47.437 21332-21332 Yang        I  child = androidx.appcompat.widget.AppCompatTextView{4578767 V.ED..... ......ID 0,0-0,0 #7f0a03d2 app:id/root_tv}, child.layoutParams = android.widget.LinearLayout$LayoutParams@5303914
2024-05-16 20:48:47.437 21332-21332 Yang        I  <---------最外层LinearLayout end----------->
2024-05-16 20:48:47.437 21332-21332 Yang        I  addView before inflateLinearLayout = android.widget.LinearLayout{6495bd V.E...... ......ID 0,0-0,0 #7f0a01ca app:id/inflate_ll}, inflateLinearLayout?.layoutParams = null, inflateLinearLayout?.parent = null
2024-05-16 20:48:47.437 21332-21332 Yang        I  <---------内层LinearLayout addView before start----------->
2024-05-16 20:48:47.438 21332-21332 Yang        I  child = androidx.appcompat.widget.AppCompatTextView{da187b2 V.ED..... ......ID 0,0-0,0 #7f0a01cb app:id/inflate_tv}, child.layoutParams = android.widget.LinearLayout$LayoutParams@acb3303
2024-05-16 20:48:47.438 21332-21332 Yang        I  <---------内层LinearLayout addView before end----------->
2024-05-16 20:48:47.438 21332-21332 Yang        I  addView after inflateLinearLayout = android.widget.LinearLayout{6495bd V.E...... ......ID 0,0-0,0 #7f0a01ca app:id/inflate_ll}, inflateLinearLayout?.layoutParams = android.widget.LinearLayout$LayoutParams@961d280, inflateLinearLayout?.parent = android.widget.LinearLayout{40b9e68 V.E...... ......ID 0,0-0,0 #7f0a03cf app:id/root_ll}
2024-05-16 20:48:47.438 21332-21332 Yang        I  <---------内层LinearLayout addView after start----------->
2024-05-16 20:48:47.438 21332-21332 Yang        I  child = androidx.appcompat.widget.AppCompatTextView{da187b2 V.ED..... ......ID 0,0-0,0 #7f0a01cb app:id/inflate_tv}, child.layoutParams = android.widget.LinearLayout$LayoutParams@acb3303
2024-05-16 20:48:47.438 21332-21332 Yang        I  <---------内层LinearLayout addView after end----------->
  • 未通过ViewGroup.addView()添加的ViewGroup不在任何父布局中,其LayoutParamsnull。但是这个ViewGroup包裹的View对象的LayoutParams不为null,因为包裹的View对象有确定的父布局,只是这个父布局没有自己的父布局
  • 通过ViewGroup.addView()添加的ViewGroup已经被添加到一个父布局中,其LayoutParams不再为null。这个ViewGroup包裹的View对象的LayoutParams也不为null,因为包裹的View对象有确定的父布局

如果root!=nullattachRoot==false

class MainActivity : AppCompatActivity() {
    var rootLinearLayout : LinearLayout ?= null
    var inflateLinearLayout : LinearLayout ?= null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        rootLinearLayout = findViewById(R.id.root_ll)
        inflateLinearLayout = layoutInflater.inflate(R.layout.inflate_layout, rootLinearLayout, false) as? LinearLayout
        Log.i(TAG, "rootLinearLayout = ${rootLinearLayout}, rootLinearLayout?.layoutParams = ${rootLinearLayout?.layoutParams}, rootLinearLayout?.parent = ${rootLinearLayout?.parent}")
        Log.i(TAG, "<---------最外层LinearLayout start----------->")
        rootLinearLayout?.forEach {
            Log.i(TAG, "child = $it, child.layoutParams = ${it.layoutParams}")
        }
        Log.i(TAG, "<---------最外层LinearLayout end----------->")


        Log.i(TAG, "addView before inflateLinearLayout = ${inflateLinearLayout}, inflateLinearLayout?.layoutParams = ${inflateLinearLayout?.layoutParams}, inflateLinearLayout?.parent = ${inflateLinearLayout?.parent}")
        Log.i(TAG, "<---------内层LinearLayout addView before start----------->")
        inflateLinearLayout?.forEach {
            Log.i(TAG, "child = $it, child.layoutParams = ${it.layoutParams}")
        }
        Log.i(TAG, "<---------内层LinearLayout addView before end----------->")


        rootLinearLayout?.addView(inflateLinearLayout)
        Log.i(TAG, "addView after inflateLinearLayout = ${inflateLinearLayout}, inflateLinearLayout?.layoutParams = ${inflateLinearLayout?.layoutParams}, inflateLinearLayout?.parent = ${inflateLinearLayout?.parent}")
        Log.i(TAG, "<---------内层LinearLayout addView after start----------->")
        inflateLinearLayout?.forEach {
            Log.i(TAG, "child = $it, child.layoutParams = ${it.layoutParams}")
        }
        Log.i(TAG, "<---------内层LinearLayout addView after end----------->")
    }
}

// log result
2024-05-16 21:01:34.611 23963-23963 Yang         I  rootLinearLayout = android.widget.LinearLayout{ff5db0c V.E...... ......ID 0,0-0,0 #7f0a03cf app:id/root_ll}, rootLinearLayout?.layoutParams = android.widget.FrameLayout$LayoutParams@bb41c55, rootLinearLayout?.parent = androidx.appcompat.widget.ContentFrameLayout{b10266a V.E...... ......I. 0,0-0,0 #1020002 android:id/content}
2024-05-16 21:01:34.611 23963-23963 Yang         I  <---------最外层LinearLayout start----------->
2024-05-16 21:01:34.612 23963-23963 Yang         I  child = androidx.appcompat.widget.AppCompatTextView{190d75b V.ED..... ......ID 0,0-0,0 #7f0a03d2 app:id/root_tv}, child.layoutParams = android.widget.LinearLayout$LayoutParams@7ab9bf8
2024-05-16 21:01:34.612 23963-23963 Yang         I  <---------最外层LinearLayout end----------->
2024-05-16 21:01:34.612 23963-23963 Yang         I  addView before inflateLinearLayout = android.widget.LinearLayout{76728d1 V.E...... ......ID 0,0-0,0 #7f0a01ca app:id/inflate_ll}, inflateLinearLayout?.layoutParams = android.widget.LinearLayout$LayoutParams@1fb8e37, inflateLinearLayout?.parent = null
2024-05-16 21:01:34.612 23963-23963 Yang         I  <---------内层LinearLayout addView before start----------->
2024-05-16 21:01:34.612 23963-23963 Yang         I  child = androidx.appcompat.widget.AppCompatTextView{2fa7fa4 V.ED..... ......ID 0,0-0,0 #7f0a01cb app:id/inflate_tv}, child.layoutParams = android.widget.LinearLayout$LayoutParams@464810d
2024-05-16 21:01:34.612 23963-23963 Yang         I  <---------内层LinearLayout addView before end----------->
2024-05-16 21:01:34.613 23963-23963 Yang         I  addView after inflateLinearLayout = android.widget.LinearLayout{76728d1 V.E...... ......ID 0,0-0,0 #7f0a01ca app:id/inflate_ll}, inflateLinearLayout?.layoutParams = android.widget.LinearLayout$LayoutParams@1fb8e37, inflateLinearLayout?.parent = android.widget.LinearLayout{ff5db0c V.E...... ......ID 0,0-0,0 #7f0a03cf app:id/root_ll}
2024-05-16 21:01:34.613 23963-23963 Yang         I  <---------内层LinearLayout addView after start----------->
2024-05-16 21:01:34.613 23963-23963 Yang         I  child = androidx.appcompat.widget.AppCompatTextView{2fa7fa4 V.ED..... ......ID 0,0-0,0 #7f0a01cb app:id/inflate_tv}, child.layoutParams = android.widget.LinearLayout$LayoutParams@464810d
2024-05-16 21:01:34.613 23963-23963 Yang         I  <---------内层LinearLayout addView after end----------->
  • 未通过ViewGroup.addView()添加的ViewGroup因为LayoutInflater.inflate()传入了root!=null参数,其LayoutParams不为null,但是因为传入了attachRoot==false参数,要填充的布局不会显示,需要使用ViewGroup.addView()方法要填充的布局R.layout.inflate_layout才能显示到根布局R.layout.activity_main

如果root!=nullattachRoot==true

class MainActivity : AppCompatActivity() {
    var rootLinearLayout : LinearLayout ?= null
    var inflateLinearLayout : LinearLayout ?= null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        rootLinearLayout = findViewById(R.id.root_ll)
        inflateLinearLayout = layoutInflater.inflate(R.layout.inflate_layout, rootLinearLayout, true) as? LinearLayout
        Log.i(TAG, "rootLinearLayout = ${rootLinearLayout}, rootLinearLayout?.layoutParams = ${rootLinearLayout?.layoutParams}, rootLinearLayout?.parent = ${rootLinearLayout?.parent}")
        Log.i(TAG, "<---------最外层LinearLayout start----------->")
        rootLinearLayout?.forEach {
            Log.i(TAG, "child = $it, child.layoutParams = ${it.layoutParams}")
        }
        Log.i(TAG, "<---------最外层LinearLayout end----------->")


        Log.i(TAG, "addView before inflateLinearLayout = ${inflateLinearLayout}, inflateLinearLayout?.layoutParams = ${inflateLinearLayout?.layoutParams}, inflateLinearLayout?.parent = ${inflateLinearLayout?.parent}")
        Log.i(TAG, "<---------内层LinearLayout start----------->")
        inflateLinearLayout?.forEach {
            Log.i(TAG, "child = $it, child.layoutParams = ${it.layoutParams}")
        }
        Log.i(TAG, "<---------内层LinearLayout end----------->")
    }
}

// log result
2024-05-16 21:14:32.730 24204-24204 Yang         I  rootLinearLayout = android.widget.LinearLayout{f4b682d V.E...... ......ID 0,0-0,0 #7f0a03cf app:id/root_ll}, rootLinearLayout?.layoutParams = android.widget.FrameLayout$LayoutParams@ca74162, rootLinearLayout?.parent = androidx.appcompat.widget.ContentFrameLayout{ceee2f3 V.E...... ......I. 0,0-0,0 #1020002 android:id/content}
2024-05-16 21:14:32.730 24204-24204 Yang         I  <---------最外层LinearLayout start----------->
2024-05-16 21:14:32.730 24204-24204 Yang         I  child = androidx.appcompat.widget.AppCompatTextView{5cedbb0 V.ED..... ......ID 0,0-0,0 #7f0a03d2 app:id/root_tv}, child.layoutParams = android.widget.LinearLayout$LayoutParams@a235a29
2024-05-16 21:14:32.730 24204-24204 Yang         I  child = android.widget.LinearLayout{b3c11ae V.E...... ......ID 0,0-0,0 #7f0a01ca app:id/inflate_ll}, child.layoutParams = android.widget.LinearLayout$LayoutParams@8abb4f
2024-05-16 21:14:32.730 24204-24204 Yang         I  <---------最外层LinearLayout end----------->
2024-05-16 21:14:32.731 24204-24204 Yang         I  addView before inflateLinearLayout = android.widget.LinearLayout{f4b682d V.E...... ......ID 0,0-0,0 #7f0a03cf app:id/root_ll}, inflateLinearLayout?.layoutParams = android.widget.FrameLayout$LayoutParams@ca74162, inflateLinearLayout?.parent = androidx.appcompat.widget.ContentFrameLayout{ceee2f3 V.E...... ......I. 0,0-0,0 #1020002 android:id/content}
2024-05-16 21:14:32.731 24204-24204 Yang         I  <---------内层LinearLayout start----------->
2024-05-16 21:14:32.731 24204-24204 Yang         I  child = androidx.appcompat.widget.AppCompatTextView{5cedbb0 V.ED..... ......ID 0,0-0,0 #7f0a03d2 app:id/root_tv}, child.layoutParams = android.widget.LinearLayout$LayoutParams@a235a29
2024-05-16 21:14:32.731 24204-24204 Yang         I  child = android.widget.LinearLayout{b3c11ae V.E...... ......ID 0,0-0,0 #7f0a01ca app:id/inflate_ll}, child.layoutParams = android.widget.LinearLayout$LayoutParams@8abb4f
2024-05-16 21:14:32.731 24204-24204 Yang         I  <---------内层LinearLayout end----------->
  • 未通过ViewGroup.addView()添加的ViewGroup因为LayoutInflater.inflate()传入了root!=null参数,其LayoutParams不为null,传入了attachRoot==true参数,要填充的布局会直接显示,不需要使用ViewGroup.addView()方法手动添加
09-30
你提出的问题非常关键: > **“我明明传了 `recyclerItemLayoutRes`,但最终却用了 `R.layout.skeleton_default_item`(默认骨架)”** 这说明当前 `onCreateViewHolder` 的逻辑存在 **优先级判断错误** —— 它把本该用于动态生成骨架的 `recyclerItemLayoutRes`,错误地跳过了,直接走到了兜底默认项。 --- ### ❌ 问题出在这一段: ```kotlin val skeletonLayoutRes: Int = when { config.skeletonItemType != null -> SkeletonItemType.toLayoutRes(config.skeletonItemType!!) config.recyclerSkeletonItemLayoutRes != null -> config.recyclerSkeletonItemLayoutRes!! config.recyclerItemLayoutRes != null -> { null // ←← 这里返回的是 null!导致下面进入 else 分支! } else -> R.layout.skeleton_default_item } ?: R.layout.skeleton_default_item ``` #### 🔥 关键错误: - 当 `config.recyclerItemLayoutRes != null` 时,`when` 表达式返回 `null` - 而 `when` 整体结果为 `null` - 所以触发了后面的 `?: R.layout.skeleton_default_item` - 最终 `skeletonLayoutRes = R.layout.skeleton_default_item` - 即使你有 `recyclerItemLayoutRes`,也**不会动态生成骨架**,而是直接 inflate 默认布局! --- ### ✅ 正确逻辑应该是: > 只有当 **没有设置任何 skeleton 相关 layout** 时,才使用默认布局。 > 如果只设置了 `recyclerItemLayoutRes`,应进入 `run{}` 分支,通过它**动态生成骨架 View**。 --- ### ✅ 修复后的正确代码如下: ```kotlin override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SkeletonViewHolder { val config = buildContext.config val context = parent.context // Step 1: 尝试获取预定义的 skeleton 布局资源(来自 type 或显式设置) val skeletonLayoutRes: Int? = when { config.skeletonItemType != null -> SkeletonItemType.toLayoutRes(config.skeletonItemType!!) config.recyclerSkeletonItemLayoutRes != null -> config.recyclerSkeletonItemLayoutRes else -> null // 不依赖 recyclerItemLayoutRes 在这里决定 } // Step 2: 如果有指定 skeleton layout,则直接 inflate val skeletonView: View? = skeletonLayoutRes?.takeIf { it != 0 }?.let { resId -> LayoutInflater.from(context).inflate(resId, parent, false) } // Step 3: 如果没有预定义 skeleton layout,但有原始 item layout,则动态生成骨架 val finalView = skeletonView ?: run { val itemLayoutRes = config.recyclerItemLayoutRes ?: throw IllegalArgumentException("Either recyclerItemLayoutRes, recyclerSkeletonItemLayoutRes, or skeletonItemType must be set") val itemView = LayoutInflater.from(context).inflate(itemLayoutRes, parent, false) measureItemView(itemView, parent.measuredWidth) SkeletonFactory.createViewGroupSkeleton(itemView as ViewGroup, buildContext, false) } return SkeletonViewHolder(finalView) } ``` --- ### ✅ 修复点总结 | 问题 | 修复方式 | |------|----------| | ❌ `when` 中对 `recyclerItemLayoutRes` 返回 `null` 导致误入默认分支 | ✅ 移除它在 `when` 中的判断,只用于 fallback 阶段 | | ❌ 错误地将 `recyclerItemLayoutRes` 视为 layout res 源头 | ✅ 明确区分:它是用来生成骨架的模板,不是 skeleton 自身布局 | | ❌ 兜底太早启用 `R.layout.skeleton_default_item` | ✅ 改成:只有当所有选项都缺失时才抛异常或考虑是否设默认值 | --- ### 🛠️ 如果你想保留“自动使用默认布局”的行为 可以在构造配置时就设置好兜底值,比如: ```kotlin if (recyclerSkeletonItemLayoutRes == null && skeletonItemType == null && recyclerItemLayoutRes == null) { recyclerSkeletonItemLayoutRes = R.layout.skeleton_default_item } ``` 这样上面的 `onCreateViewHolder` 就能自然走 `skeletonLayoutRes` 分支,无需额外处理。 --- ### 💡 调试建议:打印日志确认路径 加几行 log 看看到底走了哪条路: ```kotlin Log.d("Skeleton", "skeletonItemType: ${config.skeletonItemType}") Log.d("Skeleton", "recyclerSkeletonItemLayoutRes: ${config.recyclerSkeletonItemLayoutRes}") Log.d("Skeleton", "recyclerItemLayoutRes: ${config.recyclerItemLayoutRes}") Log.d("Skeleton", "Using skeletonView from: ${if (skeletonView != null) "predefined layout" else "generated from item"}") ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值