DialogFragment细枝末节

栏目: Android · 发布时间: 6年前

内容简介:3.DialogFragment中对Dialog属性的设置,我们可以在DialogFragment的onStar中来对Dialog的样式进行设置,有一点需要注意的是,对于Dialog样式的设置,必须在onCretaeDialog方法后面执行,不然得不到Dialog实例,我们可以打印一下DialogFragment从创建到show出来时执行的具体生命周期方法,具体内容如下:结语:以上便是全文的内容,是自己在使用DialogFragment中碰到了一些坑以及学习后的一些理解,希望能对您有帮助。
DialogFragment细枝末节

前言

在Android中,创建对话框有两种,一种是Dialog,另外一种则是今天的主题,官方推荐的DialogFragmet,关于Dialog的使用就不赘述了,今天主要介绍DialogFragment的使用以及一些需要注意的事项,主要有以下几点。

  • DialogFragmeng相比Dialog的优势。

  • DialogFrargment的使用。

  • DialogFragment简单源码分析。

  • DialogFragment需要注意的一些小细节。

一、DialogFragment的优势

  • 在 DialogFragment之前,我们创建对话框一般采用 Dialog,而且从代码的编写角度来看,Dialog 使用起来其实更加简单,但是 Google 却是推荐尽量使用 DialogFragment,是不是感觉很奇怪,其实原因也很简单, DialogFragment 有着 Dialog 所没有的非常好的特性,可以让它具有更高的可复用性(降低耦合)和更好的便利性(很好的处理屏幕翻转的情况)。

二、DialogFragment的使用

  • 方式一:重写DialogFragment的onCreateDialog方法

  • 方式二:重写DialogFragment的onCreateView方法

1.方式一具体使用

  • DialogFragment中重写onCreateDialog方法,返回一个Dialog。
@NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Logger.d(TAG, "onCreateDialog");
        AppCompatDialog appCompatDialog = new AppCompatDialog(requireActivity());
        TextView textView = new TextView(requireActivity());
        textView.setText("通过onCreateDialog使用DialogFragment");
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 28);
        appCompatDialog.setContentView(textView);
        return appCompatDialog;
    }
复制代码
  • 在Activity中使用DialogFragment。
private void showOnCreateDialogFragment(){
        DialogFragment dialogFragment = new DialogFragment();
        dialogFragment.show(getSupportFragmentManager(), "tag");
    }
复制代码

2.方式二具体使用

  • DialogFragment中重写onCreateView方法,该方法创建的View将会作为Dialog的内容布局,使用方式则是跟方法一在Activity中方式一致,这里就不赘述了。
@Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Logger.d(TAG, "onCreateView");
        TextView textView = new TextView(requireActivity());
        textView.setText("通过onCreateView使用DialogFragment");
        textView.setGravity(Gravity.CENTER);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 28);
        return textView;
    }
复制代码

3.DialogFragment中对Dialog属性的设置,我们可以在DialogFragment的onStar中来对Dialog的样式进行设置,有一点需要注意的是,对于Dialog样式的设置,必须在onCretaeDialog方法后面执行,不然得不到Dialog实例,我们可以打印一下DialogFragment从创建到show出来时执行的具体生命周期方法,具体内容如下:

2019-03-10 14:19:10.971 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onAttach
2019-03-10 14:19:10.971 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onCreate
2019-03-10 14:19:10.972 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onCreateDialog
2019-03-10 14:19:10.994 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onActivityCreated
2019-03-10 14:19:11.186 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onStart
2019-03-10 14:19:11.186 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onResume
复制代码
  • 从上面打印的回调生命周期方法来看,我们是可以在onStart方法中对Dialog进行属性设置的,具体代码如下:
@Override
    public void onStart() {
        super.onStart();
        Dialog dialog = getDialog();
        Window window = dialog.getWindow();
        if (window == null) {
            return;
        }
        WindowManager.LayoutParams attributes = window.getAttributes();
        //设置Dialog窗口的高度
        attributes.height = WindowManager.LayoutParams.MATCH_PARENT;
        //设置Dialog窗口的宽度
        attributes.width = WindowManager.LayoutParams.MATCH_PARENT;
        //设置Dialog的居中方向
        attributes.gravity = Gravity.CENTER;
        //设置Dialog弹出时背景的透明度
        attributes.dimAmount = 0.6f;
        //设置Dialog水平方向的间距
        attributes.horizontalMargin = 0f;
        //设置Dialog垂直方向的间距
        attributes.verticalMargin = 0f;
        //设置Dialog显示时X轴的坐标,具体屏幕X轴的偏移量
        attributes.x = 0;
        //设置Dialog显示时Y轴的坐标,距离屏幕Y轴的偏移量
        attributes.y = 0;
        //设置Dialog的透明度
        attributes.alpha = 0f;
        //设置Dialog显示和消失时的动画
        attributes.windowAnimations = 0;
        window.setAttributes(attributes);
        Logger.d(TAG, "onStart");
    }

复制代码

三、DialogFragmeng源码分析

1.下面先贴一下DialogFragment的部分源码,并进行简要分析。

public class DialogFragment extends Fragment implements OnCancelListener, OnDismissListener {
    boolean mViewDestroyed;
    boolean mDismissed;
    boolean mShownByMe;

    public DialogFragment() {
    }
    
    //标准的显示Fragment的方法,没什么好说的
    public void show(FragmentManager manager, String tag) {
        this.mDismissed = false;
        this.mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commit();
    }
    
    //传入一个事务管理,将fragment加入到事务管理中,并返回回退栈id
    //返回的mBackStackId将在下文中用到
    public int show(FragmentTransaction transaction, String tag) {
        this.mDismissed = false;
        this.mShownByMe = true;
        transaction.add(this, tag);
        this.mViewDestroyed = false;
        this.mBackStackId = transaction.commit();
        return this.mBackStackId;
    }
    
    //第一个show方法的加强版,看名字就知道,使用这个方法之后,则会立即执行fragment当中相关的方法,这个待会儿作出解释
    public void showNow(FragmentManager manager, String tag) {
        this.mDismissed = false;
        this.mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commitNow();
    }

    //Dialaog消失时的回调,setOndismissListener是在onActivityCreate中设置的,当前正是把dialog给dismiss,并没有让dialogfragment出栈
    public void dismiss() {
        this.dismissInternal(false);
    }
    
    //dismiss的加强版,先消失dialog,并将dialogfragment移除栈内
    public void dismissAllowingStateLoss() {
        this.dismissInternal(true);
    }

    //显示判断dialog,是否已经消失,如果已经消失,则不做任何操作
    //1.如果dialog实例不为空,先调用dialog的dismiss方法,隐藏dialog
    //2.如果先前调用的是public int show(FragmentTransaction transaction, String tag)
    //方法显示的dialogfragment,那么此时会根据之前返回的mBackStackId来将fragment移除栈内
    //3.如果不是则再启用事务将dialogfragment移除栈内,这里会根据传入的allowStateLoss来区分
    //提交事务的方法
    void dismissInternal(boolean allowStateLoss) {
        if (!this.mDismissed) {
            this.mDismissed = true;
            this.mShownByMe = false;
            if (this.mDialog != null) {
                this.mDialog.dismiss();
            }

            this.mViewDestroyed = true;
            if (this.mBackStackId >= 0) {
                this.getFragmentManager().popBackStack(this.mBackStackId, 1);
                this.mBackStackId = -1;
            } else {
                FragmentTransaction ft = this.getFragmentManager().beginTransaction();
                ft.remove(this);
                if (allowStateLoss) {
                    ft.commitAllowingStateLoss();
                } else {
                    ft.commit();
                }
            }

        }
    }
    
    //如果子类重写该方法,那么使用的就是你自定义的dialog
    @NonNull
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new Dialog(this.getActivity(), this.getTheme());
    }
    
    //dialog设置了onDismissListener后的回调,如果dialog正常消失,次回调中的方法不会调用到
    //因为在DialogFragment的onStar方法中将mViewDestroyed变量赋值为true,dialog显示设置到调用显示出来的生命周期回调我们已经打印过了。
    public void onDismiss(DialogInterface dialog) {
        if (!this.mViewDestroyed) {
            this.dismissInternal(true);
        }
    }

    //该方法,先判断Dialog是否已经显示,然后会取onCreateView中返回的View,如果View不为空,那么该View将作为Dialog的内容布局,所以,如果你同时重写了onCreateDialog和onCreateView方法,那么会优先采用onCreateView当中的View作为内容布局,然后再作了一些监听设置
    //设置了dialog是否可点击
    //设置了dialog的消失监听onDismissListener,所以消失时会回调文中的dimiss方法
    //设置了dialog的取消监听onCancelListener,在取消时会回调文中的onCancel方法
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (this.mShowsDialog) {
            View view = this.getView();
            if (view != null) {
                if (view.getParent() != null) {
                    throw new IllegalStateException("DialogFragment can not be attached to a container view");
                }

                this.mDialog.setContentView(view);
            }

            Activity activity = this.getActivity();
            if (activity != null) {
                this.mDialog.setOwnerActivity(activity);
            }

            this.mDialog.setCancelable(this.mCancelable);
            this.mDialog.setOnCancelListener(this);
            this.mDialog.setOnDismissListener(this);
            if (savedInstanceState != null) {
                Bundle dialogState = savedInstanceState.getBundle("android:savedDialogState");
                if (dialogState != null) {
                    this.mDialog.onRestoreInstanceState(dialogState);
                }
            }

        }
    }
}

复制代码

2.源码分析总结

  • 可以重写onCreateDialog或者onCreateView来创建DialogFragment的视图,但是onCretaeView的优先级要高于onCreateDilaog,所以没有特殊的需要,一般通过重写onCreateView来创建Dialog的视图即可。

  • 从一开始我们就打印了DialogFragment从创建视图到show出来回调的一系列生命周期方法,我们得知设置dialog的属性可以在DialogFragment中的onStar回调中进行。

  • 对于dialog的显示我们通过查看源码得知一共有三个方法,show()方法传入一个fragmentManager以及一个tag,showNow方法,传入一个fragmentManger和一个tag,show()方法,传入一个事务和一个tag,并返回该事务中回退栈的id,在调用dismissInternal()方法时,会根据显示DialogFragment的类型来操作DialogFragment出栈。

  • DialogFragment会在onActivityCreate()中对Dialog的内容试图进行一次赋值,如果你重写了onCrateView方法,并返回了一个不为空的实例,那么将会作为该Dialog的内容视图,然后再对dialog设置了是否可取消,消失监听,以及取消监听

  • 通过上面的源码简析,我们得知在DialogFragment消失时,只会将dialog给隐藏,并不会讲DialogFragment移除栈内,如果想讲DialogFragment在dialog消失时移除栈内,那么需要手动调用dismissInternal()方法

四、细节注意事项

  • 可以通过重写onCreateDialog和onCreateView来设置DialogFragment的视图,但onCraeteView设置的视图优先级要高于在onCreateDialog。

  • 设置Dialog的属性需要在onCreateDialog回调后设置,不然getDialog得到的对象则会是null,推荐在onStar方法中对Dialog进行属性设置。

  • DialogFragment单个实例只能show一次DialogFrament,如果多次展示的话,则会抛出如下异常

Process: org.lym.sourcecodeparse, PID: 27108
    java.lang.IllegalStateException: Fragment already added: DialogFragment{8262d74 #0 tag}
        at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1893)
        at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:760)
        at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2595)
        at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2382)
        at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2337)
复制代码
  • DialogFragment在正常Dismiss后并不会直接从当前的栈中移除,而是在DialogFragment中的onDestroyView()回调时,才会对DialogFragment进行出栈操作,所以如果你如果需要在Activity中频繁的显示隐藏一个DialogFragment,那么在dismiss时需要手动的调用dismissAllowingStateLoss()方法,并且再次show时不能用上一个DialogFragment实例。

  • DialogFragment并没有对Dialog的消失提供监听给调用者使用,但是我们通过源码分析得知,DialogFragment在onActivityCreate当中其实已经帮我们设置了onDismissListener,所以我们如果需要对Dialog的消失进行监听的话只需重写onDismiss方法即可,还有一种方式则是覆盖父类设置的onDismissListener监听,但是需注意的时,这个监听的复写,必须在父类onActivityCreate方法调用之后,关于消失监听的两种写法如下:

//mListener为提供到外部使用的回调
 @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        mListener.onDismiss(dialog);
        Logger.d(TAG, "onDismiss");
    }
    
    //复写setOnDismissListener必须发生在父类的onActivityCreate之后
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (getDialog() != null && null != mListener) {
            getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
                @Override
                public void onDismiss(DialogInterface dialog) {
                    ToastUtils.showToast("覆盖后的OnDismiss Listener");
                }
            });
        }
        Logger.d(TAG, "onActivityCreated");
    }
复制代码

结语:以上便是全文的内容,是自己在使用DialogFragment中碰到了一些坑以及学习后的一些理解,希望能对您有帮助。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ordering Disorder

Ordering Disorder

Khoi Vinh / New Riders Press / 2010-12-03 / USD 29.99

The grid has long been an invaluable tool for creating order out of chaos for designers of all kinds—from city planners to architects to typesetters and graphic artists. In recent years, web designers......一起来看看 《Ordering Disorder》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具