DialogFragment 在androidx 升级到1.1.0兼容性问题

项目中封装了BaseDialogFragment,但是在androidx 从1.0.0升级到1.1.0之后每次弹窗都会crash。那么首先猜测肯定是DialogFragment在1.1.0的时候做了修改,找茬开始。

BaseDialogFragment

public abstract class BaseDialogFragment extends DialogFragment implements View.OnClickListener {

    protected View mRootView;
    private Context mContext;
    private boolean isShowing = false;
    
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        initLayout();
        mRootView = inflater.inflate(getLayoutId(), container, false);
        mContext = mRootView.getContext();
        return mRootView;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (initExtraData()) {
            initView();
            initData(); //initData中 初始化 Presenter
            initListener();
            loadData();
        } else {
            dismiss();
        }
    }

    /**
     * 返回布局id R.layout.main
     *
     * @return
     */
    //返回布局id R.layout.main
    protected abstract int getLayoutId();

    /**
     * 初始化通过bundle传过来的数据
     * getArguments().getString()  etc.
     */
    //初始化通过bundle传过来的数据
    protected boolean initExtraData() {
        return true;
    }

    protected void initView() {
    }

    protected void initListener() {
    }

    protected void initData() {
    }

    protected void loadData() {
    }

    //IllegalStateException : Can not perform this action after onSaveInstanceSate
    //ShowMedalDialogFragment  必须重写改方法 trycatch一下
    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            if (!isShowing) {
                isShowing = true;
                super.show(manager, tag);
            }
        } catch (Throwable ignore) {
        }
    }

    @Override
    public void dismiss() {
        try {
            dismissAllowingStateLoss();
        } catch (Exception e) {

        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }

    protected View findViewById(int id) {
        if (mRootView == null) {
            return null;
        } else {
            return mRootView.findViewById(id);
        }
    }

    @Override
    public Context getContext() {
        return mContext;
    }

    //region 弹窗布局初始化
    protected void initLayout() {
        Window window = null;
        Dialog dialog = getDialog();
        if (dialog == null) {
            return;
        }
        window = getDialog().getWindow();
        if (window != null) {
            window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            window.setGravity(dialogGravity());
            window.requestFeature(Window.FEATURE_NO_TITLE);
            window.setWindowAnimations(windowAnimations());
            getDialog().getWindow().getDecorView().setPadding(0, 0, 0, 0);
            dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
                @Override
                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                    return keyCode == KeyEvent.KEYCODE_BACK && !cancelable();
                }
            });
            dialog.setCancelable(cancelable()); //点击返回键对话框消失
            dialog.setCanceledOnTouchOutside(canCanceledOnTouchOutside()); //点击dialog以外区域对话框消失
            // 设置宽度为屏宽、靠近屏幕底部。
            WindowManager.LayoutParams wlp = window.getAttributes();
            wlp.width = dialogWidth();
            wlp.height = dialogHeight();
            //背景是否透明
            if (isBackgroundTransparent()) {
                wlp.dimAmount = 0.0f;
            }
            window.setAttributes(wlp);
        }
    }

    /**
     * 点击返回键对话框消失,默认为不可以
     *
     * @return
     */
    protected boolean cancelable() {
        return false;
    }

    /**
     * 点击dialog以外区域对话框消失,默认不可以
     *
     * @return
     */
    protected boolean canCanceledOnTouchOutside() {
        return false;
    }

    /**
     * dialog进入动画,默认无动画
     *
     * @return
     */
    protected int windowAnimations() {
//        R.style.anim_person_info_enter_exit
        return 0;
    }

    /**
     * dialog 宽度
     *
     * @return
     */
    protected int dialogWidth() {
        return WindowManager.LayoutParams.MATCH_PARENT;
    }

    /**
     * dialog 位置
     *
     * @return
     */
    protected int dialogHeight() {
        return WindowManager.LayoutParams.MATCH_PARENT;
    }

    /**
     * dialog 高度
     *
     * @return
     */
    protected int dialogGravity() {
        return Gravity.CENTER;
    }

    /**
     * dialog 背景是否透明
     *
     * @return
     */
    protected boolean isBackgroundTransparent() {
        return false;
    }
    //endregion 弹窗布局初始化


    protected OnDismissListener onDismissListener;

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        isShowing = false;
        if (onDismissListener != null) {
            onDismissListener.onDismiss(dialog);
        }
    }

    public BaseDialogFragment setOnDismissListener(OnDismissListener onDismissListener) {
        this.onDismissListener = onDismissListener;
        return this;
    }

    public interface OnDismissListener {
        void onDismiss(DialogInterface dialog);
    }

    @Override
    public void onClick(View v) {

    }
}

查看了源码,没有发现有什么不同呀,主要是代码太多了,是在看不下去了。就去看了看更新文档,还是没发现有提到对DialogFragment有做处理的。 我太难了!我就不信了找不到问题。然后又继承直接继承DialogFragment实现了Dialog。没有任何问题,一切正常运行。那么问题就可以确定是封装的BaseDialogFragment有问题了。我一行代码一行代码的尝试,终于在我注释掉getContext()方法的时候不会Crash了。哇,终于找到了。对于我这样一个好学的人来说,那我必须用弄懂是为什么会这样啊。然后我就去看调用getContext()并且会报出Crash的错误信息的代码。

@NonNull
    public final Context requireContext() {
        Context context = getContext();
        if (context == null) {
            throw new IllegalStateException("Fragment " + this + " not attached to a context.");
        }
        return context;
    }

就是这个地方,那么什么地方用到这个方法了呢?上代码。

androidx 1.1.0

@NonNull
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        return new Dialog(requireContext(), getTheme());
    }

就是这个地方,同时我又去看了1.0.0的代码,发现了一个天大的秘密

androidx 1.0.0

  @NonNull
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        return new Dialog(getActivity(), getTheme());
    }

没错,就是1.1.0的创建Dialog的时候第一个参数从getActivity()改成了requireContext()。但是封装的BaseDialogFragment重写了getContext()方法,在执行onCreateDialog()的时候还没有执行onCreateView(),所以获取到的contextnull,这就找到原因了。其实看了一下根本没必要重写getContext()方法,删掉它,大功告成。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章