Android UI篇——DialogFragment系列六之常见问题

《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系列六之常见问题)