merge不是View,不能设宽高/边距/ID;include的android:id仅作用于被引入布局的根View(根为merge时无效);ViewStub需调用setVisibility(VISIBLE)或inflate()才加载——三者定位不同,混用会导致findViewById失败、层级冗余或界面空白。
merge 不是 View,不能设宽高/边距/ID;include 的 android:id 只作用于被引入布局的根 View(若根是 则无效);ViewStub 必须调用 setVisibility(View.VISIBLE) 或 inflate() 才真正加载——三者定位完全不同,混用或错配会直接导致 findViewById 失败、层级冗余或界面空白。
你写 ,这个 android:id 会被赋给 title_bar.xml 的根 View。但前提是它的根不能是 ——否则 ID 丢失,findViewById(R.id.title1) 返回 null。
android:layout_width 和 android:layout_height,单写一个无效android:layout_marginTop)也只在宽高都重写后才生效title_bar.xml 根是 ,且你 include 两次,记得给两个 分别设不同 android:id,否则 findViewById 只能拿到第一个 时,ID 无效 → 改用 findViewById(R.id.tv_title) 直接找子控件(需确保不重名) 是占位符,不是 View,所以所有 XML 属性(android:layout_width、android:background、android:id)全被忽略——这是设计使然,不是 bug。
引入的布局,其根 ViewGroup 类型和父容器一致(比如父是 ,被 include 的也是 ),这时换成 就能砍掉一层嵌套,避免多包一层无意义容器LayoutInflater.inflate(R.layout.xxx, parent, true) 加载含 的布局时,parent 必须非 null,且第三个参数必须为 true,否则 merge 内容不会 attach 到父容器 初始化时是 0x0 的不可见占位符,它**不会解析内部 layout,也不创建任何子 View**——直到你主动触发。
viewStub.setVisibility(View.VISIBLE) —— 自动 inflate 并替换自身View inflated = viewStub.inflate() —— 返回加载后的根 View,可继续 findViewById 操作ViewStub 对象失效,再次调用 inflate() 抛 IllegalStateException
上设 a
ndroid:visibility="gone" 来“预隐藏”,它只认 invisible 或 visible(但初始就是 invisible)最容易被忽略的是:merge 的属性一律无效、include 的 ID 在 merge 下消失、ViewStub 不调用就不加载——这三个行为不是缺陷,而是机制设计。写布局时先想清楚「我要复用?减层?还是懒加载?」,再选标签,比死记语法重要得多。