整体讲解
在 Android 开发中,Fragment 的 mContainer 和 mContainerId 是两个重要的属性,它们在 Fragment 的动态添加过程中发挥着关键作用。下面详细梳理它们的赋值原理。
mContainerId 的赋值原理
1. 动态添加 Fragment 时指定容器 ID
在使用 FragmentTransaction 的 add、replace 等方法动态添加 Fragment 时,需要传入一个容器的资源 ID,这个 ID 会被赋值给 Fragment 的 mContainerId。
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
MyFragment myFragment = new MyFragment();
// 这里传入的 R.id.fragment_container 会赋值给 Fragment 的 mContainerId
fragmentTransaction.add(R.id.fragment_container, myFragment);
fragmentTransaction.commit();
2. BackStackRecord 记录操作
FragmentTransaction 的具体实现类是 BackStackRecord。当调用 add 方法时,BackStackRecord 会记录这个操作,同时将容器 ID 传递给 Fragment。
// BackStackRecord.java
@Override
public FragmentTransaction add(int containerViewId, Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
// ...
fragment.mContainerId = fragment.mFragmentId = containerViewId;
// ...
}
在 doAddOp 方法中,将传入的 containerViewId 赋值给 Fragment 的 mContainerId 和 mFragmentId。
mContainer 的赋值原理
1. 事务执行时查找容器视图
当 FragmentTransaction 提交事务后,FragmentManager 会在合适的时机执行事务。在执行过程中,会根据 mContainerId 查找对应的视图容器,并将其赋值给 Fragment 的 mContainer。
void addFragment(Fragment fragment, boolean moveToStateNow) {
// ...
if (!fragment.mDetached) {
if (moveToStateNow) {
moveToState(fragment, mCurState, 0, 0, false);
} else {
addFragmentToState(fragment, Fragment.INITIALIZING);
}
}
// ...
}
void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
// ...
if (f.mContainerId != 0) {
f.mContainer = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
}
// ...
}
在 moveToState 方法中,通过 mContainerId 调用 onFindViewById 方法查找对应的视图容器,并将结果赋值给 Fragment 的 mContainer。
2. 静态添加时的处理
在静态添加 Fragment 的情况下,mContainer 和 mContainerId 的赋值逻辑略有不同。当解析布局文件遇到 <fragment> 标签时,系统会创建 Fragment 实例,并将其添加到布局中。此时,mContainer 会被设置为包含该 Fragment 的父视图容器,mContainerId 则是父视图容器的 ID。
// LayoutInflater.java
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
// ...
if (name.equals("fragment")) {
// 创建 Fragment 实例
final Fragment fragment = Fragment.instantiate(context, className, args);
// ...
fragment.mContainer = (ViewGroup) parent;
fragment.mContainerId = parent.getId();
// ...
}
// ...
}
在解析布局文件时,如果遇到 <fragment> 标签,会将父视图容器赋值给 Fragment 的 mContainer,并将父视图容器的 ID 赋值给 mContainerId。
总结
-
mContainerId:在动态添加Fragment时,通过FragmentTransaction的add等方法传入的容器 ID 会直接赋值给Fragment的mContainerId。 -
mContainer:在事务执行过程中,FragmentManager会根据mContainerId查找对应的视图容器,并将其赋值给Fragment的mContainer。在静态添加时,mContainer会被设置为包含该Fragment的父视图容器。
通过 mContainerId 和 mContainer,Fragment 能够准确地将自己的视图添加到 Activity 布局中的指定容器位置。