美文网首页
Android UI篇——DialogFragment系列六之常

Android UI篇——DialogFragment系列六之常

作者: lvTravler | 来源:发表于2019-08-25 11:08 被阅读0次

    《DialogFragment系列一之源码分析》
    《DialogFragment系列二之Dialog封装》
    《DialogFragment系列三之AlertDialog实现》
    《DialogFragment系列四之StatusDialog(Progress、Success、Error)实现》
    《DialogFragment系列五之ItemDialog(eg:BottomDialog)实现》
    《DialogFragment系列六之常见问题》

    前几篇通过对DialogFragment的源码分析定义了一个BaseDialog进而实现了AlertDialog、StatusDialog,在此期间遇到了几个比较经典的问题,与读者分享一下。

    问题一:onViewCreated()不回调

    笔者想通过onViewCreated()来设置布局控件,但是怎么也不回调,以前一直使用Fragment都会回调,现在怎么不回调了呢?带着疑问,进去看了一下源码,发现了猫腻。首先看下onViewCreated()在哪里被调用了,看下源码:

    moveToState(Fragment f, int newState, int transit, int transitionStyle,
                boolean keepActive) {
       .......
       case Fragment.CREATED:
                        // This is outside the if statement below on purpose; we want this to run
                        // even if we do a moveToState from CREATED => *, CREATED => CREATED, and
                        // * => CREATED as part of the case fallthrough above.
                        ensureInflatedFragmentView(f);
    
                        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.performCreateView(f.performGetLayoutInflater(
                                        f.mSavedFragmentState), container, f.mSavedFragmentState);
                                if (f.mView != null) {
                                    f.mInnerView = f.mView;
                                    f.mView.setSaveFromParentEnabled(false);
                                    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;
                        }
       .......
    }
    

    通过查看源码发现,onViewCreate()在moveToState()中即performCreateView创建contentView之后被调用,进入onViewCreate()方法要通过一个判断语句f.mView != null,那我们看下f.mView 是哪来的,继续回溯源码:

    void performCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                @Nullable Bundle savedInstanceState) {
            if (mChildFragmentManager != null) {
                mChildFragmentManager.noteStateNotSaved();
            }
            mPerformedCreateView = true;
            mViewLifecycleOwner = new LifecycleOwner() {
                @Override
                public Lifecycle getLifecycle() {
                    if (mViewLifecycleRegistry == null) {
                        mViewLifecycleRegistry = new LifecycleRegistry(mViewLifecycleOwner);
                    }
                    return mViewLifecycleRegistry;
                }
            };
            mViewLifecycleRegistry = null;
            //这里啊,在这里被赋值
            mView = onCreateView(inflater, container, savedInstanceState);
            if (mView != null) {
                // Initialize the LifecycleRegistry if needed
                mViewLifecycleOwner.getLifecycle();
                // Then inform any Observers of the new LifecycleOwner
                mViewLifecycleOwnerLiveData.setValue(mViewLifecycleOwner);
            } else {
                if (mViewLifecycleRegistry != null) {
                    throw new IllegalStateException("Called getViewLifecycleOwner() but "
                            + "onCreateView() returned null");
                }
                mViewLifecycleOwner = null;
            }
     }
    

    通过回溯源码,发现mView在performCreateView()里被唯一赋值,而且来源就是平时熟悉的onCreateView(),performCreateView()是在创建contentView使用,在moveToState()中先调用performCreateView()创建contentView,然后再调用onViewCreated()。既然定位到了onCreateView()那去看看其返回值:

       @Nullable
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                @Nullable Bundle savedInstanceState) {
            return null;
        }
    

    Fragment中默认返回null,而且Dialog中onCreateView()也使用Fragment的返回值,所以此时mView就会为null

        @Nullable
        @Override
        public final View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            //getDialog().setCancelable(setCancelable());
            getDialog().setCanceledOnTouchOutside(setCancelable());
            setCancelable(setCancelable());
            //设置背景透明
            getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            return super.onCreateView(inflater, container, savedInstanceState);
        }
    

    综上,onViewCreated()不回调是因为onCreateView返回值为null,但是在Dialog中没有使用onCreateView去加载布局,是在onCreateDialog中加载的布局,所以可以在onCreateDialog()中主动回调onViewCreated()此问题就迎刃而解了。

       @NonNull
        @Override
        public final android.app.Dialog onCreateDialog(Bundle savedInstanceState) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
            View dialogLayout = LayoutInflater.from(getContext()).inflate(setLayoutRes(), null);
            builder.setView(dialogLayout);
            //主动回调
            onViewCreated(dialogLayout, null);
            return builder.create();
        }
    
    问题二:设置getDialog().setCancelable(setCancelable())后点击返回键Dialog还是会dismiss

    设置了getDialog().setCancelable(setCancelable())后点击返回键Dialog还是会dismiss,很纳闷,源码里逻辑也是拦截返回事件通过boolean mCancelable来判断是否dismiss,代码如下:

       public void onBackPressed() {
            if (mCancelable) {
                cancel();
            }
        }
    

    真是百思不得其解,而后返回到DialogFragment查看,发现了一个相似的方法,如下:

     boolean mCancelable = true;
     public void setCancelable(boolean cancelable) {
            mCancelable = cancelable;
            if (mDialog != null) mDialog.setCancelable(cancelable);
      }
    

    罪魁祸首终于找到了,原来DialogFragment默认设置了mCancelable,而默认值是true,所以导致了getDialog().setCancelable(setCancelable())不管作用,那可以直接通过DialogFragment的setCancelable()来设置,代码如下:

        @Nullable
        @Override
        public final View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            //getDialog().setCancelable(setCancelable());
            setCancelable(setCancelable());
            getDialog().setCanceledOnTouchOutside(setCancelable());
                    //设置背景透明
            getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            return super.onCreateView(inflater, container, savedInstanceState);
        }
        protected boolean setCancelable() {
            return dialogParams.isCancelable;
        }
    

    到此,此问题被解决

    问题三:出现非contentView的背景

    运行代码发现出现了非contentView的背景,此背景是DialogFragment的默认背景,去掉即可,代码如下:

    @Nullable
        @Override
        public final View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            getDialog().setCancelable(setCancelable());
            getDialog().setCanceledOnTouchOutside(setCancelable());
            setCancelable(setCancelable());
            //设置背景透明
            getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            return super.onCreateView(inflater, container, savedInstanceState);
        }
    

    以上就是在实现Dialog的过程遇到的三个问题以及解决办法,特此记录一下并分享给读者!

    相关文章

      网友评论

          本文标题:Android UI篇——DialogFragment系列六之常

          本文链接:https://www.haomeiwen.com/subject/epkhectx.html