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()方法,刪掉它,大功告成。。。

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