BottomSheetDialogFragment 使用方式、方法执行顺序、源码解析

BottomSheetDialogFragment 使用方式、方法执行顺序、源码解析

BottomSheetDialogFragment 本质是一个Dialog(这一点后边读源码的时候会进行确认)。下边分三块说一下BottomSheetDialogFragment。

  • BottomSheetDialogFragment 方法执行顺序
  • BottomSheetDialogFragment 使用方式
  • BottomSheetDialogFragment 源码解析

一、方法执行顺序

当我们手动调用 BottomSheetDialogFragment#show(FragmentManager manager, String tag)方法时,Fragment中会执行:

E/xiaxl:: show(FragmentManager manager, String tag)
E/xiaxl:: onCreate
E/xiaxl:: onCreateDialog(Bundle savedInstanceState)
E/xiaxl:: onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
E/xiaxl:: onViewCreated
E/xiaxl:: onStart
E/xiaxl:: mDialog.show()
E/xiaxl:: onResume

二、使用方式

2.1、build.gradle添加依赖

dependencies {
    compile 'com.android.support:design:25.3.1'
}

2.2、FragmentFeedCmt.java

public class FragmentFeedCmt extends BottomSheetDialogFragment {

    // 构造方法
    public static FragmentFeedCmt newInstance(Long feedId) {
        Bundle args = new Bundle();
        args.putLong("FEED_ID", feedId);
        FragmentFeedCmt fragment = new FragmentFeedCmt();
        fragment.setArguments(args);
        return fragment;
    }

    // show的时候调用
    @Override
    public void show(FragmentManager manager, String tag) {
        Log.e("xiaxl: ", "show");
        super.show(manager, tag);
    }
    // create dialog
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Log.e("xiaxl: ", "onCreateDialog");
        return super.onCreateDialog(savedInstanceState);
    }
    // 创建View
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.e("xiaxl: ", "onCreateView");
        View inflate = inflater.inflate(R.layout.layout1, container);
        //
        initData();
        return inflate;
    }
    // 加载数据
    private void initData() {
        // 数据
        Bundle args = getArguments();
        long feedId = args.getLong("FEED_ID", -1);
    }
}

2.3、show

private FragmentFeedCmt mBottomSheetDialogFragment;

public void dialog() {
    if(mBottomSheetDialogFragment==null){
        mBottomSheetDialogFragment = FragmentFeedCmt.newInstance(123l);    
    }
    mBottomSheetDialogFragment.show(getSupportFragmentManager(), "Dialog");
}

三、源代码学习

这里从Fragment#show(FragmentManager manager, String tag)方法来跟踪学习:

DialogFragment.java

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit();
}

下边跟踪FragmentTransaction#add(this, tag)

BackStackRecord.java

@Override
public FragmentTransaction add(Fragment fragment, String tag) {
    doAddOp(0, fragment, tag, OP_ADD);
    return this;
}
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    // 省略部分代码...

    Op op = new Op();
    op.cmd = opcmd;
    op.fragment = fragment;
    addOp(op);
}

这里有一点要注意:containerViewId == 0 传入的containerViewId 参数为0

当Fragment进行显示时,执行以下方法:

void executeOps() {
        final int numOps = mOps.size();
        for (int opNum = 0; opNum < numOps; opNum++) {
            final Op op = mOps.get(opNum);
            final Fragment f = op.fragment;
            f.setNextTransition(mTransition, mTransitionStyle);
            switch (op.cmd) {
                case OP_ADD:
                    f.setNextAnim(op.enterAnim);
                    mManager.addFragment(f, false);
                    break;
                case OP_REMOVE:
                    f.setNextAnim(op.exitAnim);
                    mManager.removeFragment(f);
                    break;
                case OP_HIDE:
                    f.setNextAnim(op.exitAnim);
                    mManager.hideFragment(f);
                    break;
                case OP_SHOW:
                    f.setNextAnim(op.enterAnim);
                    mManager.showFragment(f);
                    break;
                case OP_DETACH:
                    f.setNextAnim(op.exitAnim);
                    mManager.detachFragment(f);
                    break;
                case OP_ATTACH:
                    f.setNextAnim(op.enterAnim);
                    mManager.attachFragment(f);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
            }
            if (!mAllowOptimization && op.cmd != OP_ADD) {
                mManager.moveFragmentToExpectedState(f);
            }
        }
        if (!mAllowOptimization) {
            // Added fragments are added at the end to comply with prior behavior.
            mManager.moveToState(mManager.mCurState, true);
        }
    }

下边,我们跟踪mManager.addFragment(f, false);

FragmentManagerImpl.java

addFragment 代码最终执行到moveToState方法,这里直接看moveToState

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
    // Fragments that are not currently added will sit in the onCreate() state.
    if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
        newState = Fragment.CREATED;
    }
    if (f.mRemoving && newState > f.mState) {
        // While removing a fragment, we can't change it to a higher state.
        newState = f.mState;
    }
    // Defer start if requested; don't allow it to move to STARTED or higher
    // if it's not already started.
    if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
        newState = Fragment.STOPPED;
    }
    if (f.mState < newState) {
        // For fragments that are created from a layout, when restoring from
        // state we don't want to allow them to be created until they are
        // being reloaded from the layout.
        if (f.mFromLayout && !f.mInLayout) {
            return;
        }
        if (f.getAnimatingAway() != null) {
            // The fragment is currently being animated...  but!  Now we
            // want to move our state back up.  Give up on waiting for the
            // animation, move to whatever the final state should be once
            // the animation is done, and then we can proceed from there.
            f.setAnimatingAway(null);
            moveToState(f, f.getStateAfterAnimating(), 0, 0, true);
        }
        switch (f.mState) {
            case Fragment.INITIALIZING:
                if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                if (f.mSavedFragmentState != null) {
                    f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
                    f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
                            FragmentManagerImpl.VIEW_STATE_TAG);
                    f.mTarget = getFragment(f.mSavedFragmentState,
                            FragmentManagerImpl.TARGET_STATE_TAG);
                    if (f.mTarget != null) {
                        f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                    }
                    f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
                            FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
                    if (!f.mUserVisibleHint) {
                        f.mDeferStart = true;
                        if (newState > Fragment.STOPPED) {
                            newState = Fragment.STOPPED;
                        }
                    }
                }
                f.mHost = mHost;
                f.mParentFragment = mParent;
                f.mFragmentManager = mParent != null
                        ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
                dispatchOnFragmentPreAttached(f, mHost.getContext(), false);
                f.mCalled = false;
                f.onAttach(mHost.getContext());
                if (!f.mCalled) {
                    throw new SuperNotCalledException("Fragment " + f
                            + " did not call through to super.onAttach()");
                }
                if (f.mParentFragment == null) {
                    mHost.onAttachFragment(f);
                } else {
                    f.mParentFragment.onAttachFragment(f);
                }
                dispatchOnFragmentAttached(f, mHost.getContext(), false);

                if (!f.mRetaining) {
                    f.performCreate(f.mSavedFragmentState);
                    dispatchOnFragmentCreated(f, f.mSavedFragmentState, false);
                } else {
                    f.restoreChildFragmentState(f.mSavedFragmentState);
                    f.mState = Fragment.CREATED;
                }
                f.mRetaining = false;
                if (f.mFromLayout) {
                    // For fragments that are part of the content view
                    // layout, we need to instantiate the view immediately
                    // and the inflater will take care of adding it.
                    f.mView = f.performCreateView(f.getLayoutInflater(
                            f.mSavedFragmentState), null, f.mSavedFragmentState);
                    if (f.mView != null) {
                        f.mInnerView = f.mView;
                        if (Build.VERSION.SDK_INT >= 11) {
                            ViewCompat.setSaveFromParentEnabled(f.mView, false);
                        } else {
                            f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                        }
                        if (f.mHidden) f.mView.setVisibility(View.GONE);
                        f.onViewCreated(f.mView, f.mSavedFragmentState);
                        dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false);
                    } else {
                        f.mInnerView = null;
                    }
                }
            case Fragment.CREATED:
                if (newState > Fragment.CREATED) {
                    if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                    if (!f.mFromLayout) {
                        ViewGroup container = null;
                        if (f.mContainerId != 0) {
                            if (f.mContainerId == View.NO_ID) {
                                throwException(new IllegalArgumentException(
                                        "Cannot create fragment "
                                                + f
                                                + " for a container view with no id"));
                            }
                            container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
                            if (container == null && !f.mRestored) {
                                String resName;
                                try {
                                    resName = f.getResources().getResourceName(f.mContainerId);
                                } catch (NotFoundException e) {
                                    resName = "unknown";
                                }
                                throwException(new IllegalArgumentException(
                                        "No view found for id 0x"
                                        + Integer.toHexString(f.mContainerId) + " ("
                                        + resName
                                        + ") for fragment " + f));
                            }
                        }
                        f.mContainer = container;
                        f.mView = f.performCreateView(f.getLayoutInflater(
                                f.mSavedFragmentState), container, f.mSavedFragmentState);
                        if (f.mView != null) {
                            f.mInnerView = f.mView;
                            if (Build.VERSION.SDK_INT >= 11) {
                                ViewCompat.setSaveFromParentEnabled(f.mView, false);
                            } else {
                                f.mView = NoSaveStateFrameLayout.wrap(f.mView);
                            }
                            if (container != null) {
                                container.addView(f.mView);
                            }
                            if (f.mHidden) {
                                f.mView.setVisibility(View.GONE);
                            }
                            f.onViewCreated(f.mView, f.mSavedFragmentState);
                            dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
                                    false);
                            // Only animate the view if it is visible. This is done after
                            // dispatchOnFragmentViewCreated in case visibility is changed
                            f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
                                    && f.mContainer != null;
                        } else {
                            f.mInnerView = null;
                        }
                    }

                    f.performActivityCreated(f.mSavedFragmentState);
                    dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
                    if (f.mView != null) {
                        f.restoreViewState(f.mSavedFragmentState);
                    }
                    f.mSavedFragmentState = null;
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState > Fragment.ACTIVITY_CREATED) {
                    f.mState = Fragment.STOPPED;
                }
            case Fragment.STOPPED:
                if (newState > Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
                    f.performStart();
                    dispatchOnFragmentStarted(f, false);
                }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
                    f.performResume();
                    dispatchOnFragmentResumed(f, false);
                    f.mSavedFragmentState = null;
                    f.mSavedViewState = null;
                }
        }
    } else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.RESUMED:
                if (newState < Fragment.RESUMED) {
                    if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
                    f.performPause();
                    dispatchOnFragmentPaused(f, false);
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
                    f.performStop();
                    dispatchOnFragmentStopped(f, false);
                }
            case Fragment.STOPPED:
                if (newState < Fragment.STOPPED) {
                    if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);
                    f.performReallyStop();
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState < Fragment.ACTIVITY_CREATED) {
                    if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);
                    if (f.mView != null) {
                        // Need to save the current view state if not
                        // done already.
                        if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
                            saveFragmentViewState(f);
                        }
                    }
                    f.performDestroyView();
                    dispatchOnFragmentViewDestroyed(f, false);
                    if (f.mView != null && f.mContainer != null) {
                        Animation anim = null;
                        if (mCurState > Fragment.INITIALIZING && !mDestroyed
                                && f.mView.getVisibility() == View.VISIBLE
                                && f.mPostponedAlpha >= 0) {
                            anim = loadAnimation(f, transit, false,
                                    transitionStyle);
                        }
                        f.mPostponedAlpha = 0;
                        if (anim != null) {
                            final Fragment fragment = f;
                            f.setAnimatingAway(f.mView);
                            f.setStateAfterAnimating(newState);
                            final View viewToAnimate = f.mView;
                            anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
                                    viewToAnimate, anim) {
                                @Override
                                public void onAnimationEnd(Animation animation) {
                                    super.onAnimationEnd(animation);
                                    if (fragment.getAnimatingAway() != null) {
                                        fragment.setAnimatingAway(null);
                                        moveToState(fragment, fragment.getStateAfterAnimating(),
                                                0, 0, false);
                                    }
                                }
                            });
                            f.mView.startAnimation(anim);
                        }
                        f.mContainer.removeView(f.mView);
                    }
                    f.mContainer = null;
                    f.mView = null;
                    f.mInnerView = null;
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                    if (mDestroyed) {
                        if (f.getAnimatingAway() != null) {
                            // The fragment's containing activity is
                            // being destroyed, but this fragment is
                            // currently animating away.  Stop the
                            // animation right now -- it is not needed,
                            // and we can't wait any more on destroying
                            // the fragment.
                            View v = f.getAnimatingAway();
                            f.setAnimatingAway(null);
                            v.clearAnimation();
                        }
                    }
                    if (f.getAnimatingAway() != null) {
                        // We are waiting for the fragment's view to finish
                        // animating away.  Just make a note of the state
                        // the fragment now should move to once the animation
                        // is done.
                        f.setStateAfterAnimating(newState);
                        newState = Fragment.CREATED;
                    } else {
                        if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
                        if (!f.mRetaining) {
                            f.performDestroy();
                            dispatchOnFragmentDestroyed(f, false);
                        } else {
                            f.mState = Fragment.INITIALIZING;
                        }

                        f.performDetach();
                        dispatchOnFragmentDetached(f, false);
                        if (!keepActive) {
                            if (!f.mRetaining) {
                                makeInactive(f);
                            } else {
                                f.mHost = null;
                                f.mParentFragment = null;
                                f.mFragmentManager = null;
                            }
                        }
                    }
                }
        }
    }

    if (f.mState != newState) {
        Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
                + "expected state " + newState + " found " + f.mState);
        f.mState = newState;
    }
}
  • 代码比较长,不用从头看到尾,只看以下几个关键方法的调用就行。
  • Fragment创建后,FragmentManagerImpl会依次调用Fragment的以下方法,来完善Fragment的生命周期。
E/xiaxl:: onAttach
E/xiaxl:: performCreate // performCreate会调用 onCreate 方法
E/xiaxl:: performCreateView  // performCreateView会调用 onCreateView 方法
E/xiaxl:: onViewCreated
E/xiaxl:: performStart  // performStart会执行 onStart 方法
E/xiaxl:: performResume // performResume会执行 onResume
  • performCreateView加载View后,f.mView会被添加到container.addView(f.mView);,但这里我们的containerViewId == 0 ,因此f.mView不会被添加到任何View中。

DialogFragment

这里我们回到DialogFragment

这里有的一点需提前注意的知识点:
onActivityCreated方法是在onViewCreated之后执行的。因此f.mView代表的即为FragmentRootView

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    if (!mShowsDialog) {
        return;
    }

    View view = getView();
    if (view != null) {
        if (view.getParent() != null) {
            throw new IllegalStateException(
                    "DialogFragment can not be attached to a container view");
        }
        mDialog.setContentView(view);
    }
    final Activity activity = getActivity();
    if (activity != null) {
        mDialog.setOwnerActivity(activity);
    }
    mDialog.setCancelable(mCancelable);
    mDialog.setOnCancelListener(this);
    mDialog.setOnDismissListener(this);
    if (savedInstanceState != null) {
        Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
        if (dialogState != null) {
            mDialog.onRestoreInstanceState(dialogState);
        }
    }
}

代码中 通过View view = getView(); 获取到Fragment的RootView,并将RootView添加到了mDialogContentView

扫描二维码关注公众号,回复: 9371883 查看本文章
@Override
public void onStart() {
    super.onStart();

    if (mDialog != null) {
        mViewDestroyed = false;
        mDialog.show();
    }
}

在代码执行到onStart 的时候,dialog显示了出来。
到此可以看出BottomSheetDialogFragment本质是一个Dialog

========== THE END ==========

您对“我的文章”有任何疑问,可用微信扫描以下“二维码”向我提问!

在这里插入图片描述

如果文章对您有帮助,请扫描以下二维码支持我!

在这里插入图片描述

发布了250 篇原创文章 · 获赞 143 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/aiwusheng/article/details/80981336