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 失败、层级冗余或界面空白。
include 标签:复用布局时怎么设 ID 和 layout_* 属性?
你写 ,这个 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)直接找子控件(需确保不重名)
merge 标签:什么情况下必须用?为什么设了属性没反应?
是占位符,不是 View,所以所有 XML 属性(android:layout_width、android:background、android:id)全被忽略——这是设计使然,不是 bug。
- 只允许作为 XML 文件的**根节点**,否则解析失败
- 典型场景:被
引入的布局,其根 ViewGroup 类型和父容器一致(比如父是,被 include 的也是),这时换成就能砍掉一层嵌套 - 自定义 View 的 layout 文件根节点建议用
,避免多包一层无意义容器 - 注意:用
LayoutInflater.inflate(R.layout.xxx, parent, true)加载含的布局时,parent必须非 null,且第三个参数必须为true,否则 merge 内容不会 attach 到父容器
ViewStub:为什么设置了 layout 却不显示?
初始化时是 0x0 的不可见占位符,它**不会解析内部 layout,也不创建任何子 View**——直到你主动触发。
- 两种激活方式(二选一):
✓viewStub.setVisibility(View.VISIBLE)—— 自动 inflate 并替换自身
✓View inflated = viewStub.inflate()—— 返回加载后的根 View,可继续 findViewById 操作 - inflate 后
ViewStub对象失效,再次调用inflate()抛IllegalStateException - 不能在
上设android:visibility="gone"来“预隐藏”,它只认invisible或visible(但初始就是 invisible) - 适合放错误页、加载中提示、横竖屏专用布局等「非首屏必需」内容,避免拖慢启动速度
最容易被忽略的是:merge 的属性一律无效、include 的 ID 在 merge 下消失、ViewStub 不调用就不加载——这三个行为不是缺陷,而是机制设计。写布局时先想清楚「我要复用?减层?还是懒加载?」,再选标签,比死记语法重要得多。










