Android Fragment之mContainerId以及mContainer区别

104 阅读2分钟

整体讲解

在 Android 开发中,Fragment 的 mContainer 和 mContainerId 是两个重要的属性,它们在 Fragment 的动态添加过程中发挥着关键作用。下面详细梳理它们的赋值原理。

mContainerId 的赋值原理

1. 动态添加 Fragment 时指定容器 ID

在使用 FragmentTransaction 的 addreplace 等方法动态添加 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 和 mContainerFragment 能够准确地将自己的视图添加到 Activity 布局中的指定容器位置。