DialogFragment細(xì)枝末節(jié)

[圖片上傳失敗...(image-2f551a-1552206999736)]

前言

在Android中,創(chuàng)建對(duì)話框有兩種瞎疼,一種是Dialog,另外一種則是今天的主題割卖,官方推薦的DialogFragmet前酿,關(guān)于Dialog的使用就不贅述了,今天主要介紹DialogFragment的使用以及一些需要注意的事項(xiàng)究珊,主要有以下幾點(diǎn)薪者。

  • DialogFragmeng相比Dialog的優(yōu)勢(shì)。

  • DialogFrargment的使用剿涮。

  • DialogFragment簡(jiǎn)單源碼分析言津。

  • DialogFragment需要注意的一些小細(xì)節(jié)。

一取试、DialogFragment的優(yōu)勢(shì)

  • 在 DialogFragment之前悬槽,我們創(chuàng)建對(duì)話框一般采用 Dialog,而且從代碼的編寫角度來看瞬浓,Dialog 使用起來其實(shí)更加簡(jiǎn)單初婆,但是 Google 卻是推薦盡量使用 DialogFragment,是不是感覺很奇怪猿棉,其實(shí)原因也很簡(jiǎn)單磅叛, DialogFragment 有著 Dialog 所沒有的非常好的特性,可以讓它具有更高的可復(fù)用性(降低耦合)和更好的便利性(很好的處理屏幕翻轉(zhuǎn)的情況)萨赁。

二弊琴、DialogFragment的使用

  • 方式一:重寫DialogFragment的onCreateDialog方法
  • 方式二:重寫DialogFragment的onCreateView方法

1.方式一具體使用

  • DialogFragment中重寫onCreateDialog方法,返回一個(gè)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方法,該方法創(chuàng)建的View將會(huì)作為Dialog的內(nèi)容布局慰安,使用方式則是跟方法一在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中對(duì)Dialog屬性的設(shè)置化焕,我們可以在DialogFragment的onStar中來對(duì)Dialog的樣式進(jìn)行設(shè)置萄窜,有一點(diǎn)需要注意的是,對(duì)于Dialog樣式的設(shè)置撒桨,必須在onCretaeDialog方法后面執(zhí)行脂倦,不然得不到Dialog實(shí)例,我們可以打印一下DialogFragment從創(chuàng)建到show出來時(shí)執(zhí)行的具體生命周期方法元莫,具體內(nèi)容如下:

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
  • 從上面打印的回調(diào)生命周期方法來看赖阻,我們是可以在onStart方法中對(duì)Dialog進(jìn)行屬性設(shè)置的,具體代碼如下:
    @Override
    public void onStart() {
        super.onStart();
        Dialog dialog = getDialog();
        Window window = dialog.getWindow();
        if (window == null) {
            return;
        }
        WindowManager.LayoutParams attributes = window.getAttributes();
        //設(shè)置Dialog窗口的高度
        attributes.height = WindowManager.LayoutParams.MATCH_PARENT;
        //設(shè)置Dialog窗口的寬度
        attributes.width = WindowManager.LayoutParams.MATCH_PARENT;
        //設(shè)置Dialog的居中方向
        attributes.gravity = Gravity.CENTER;
        //設(shè)置Dialog彈出時(shí)背景的透明度
        attributes.dimAmount = 0.6f;
        //設(shè)置Dialog水平方向的間距
        attributes.horizontalMargin = 0f;
        //設(shè)置Dialog垂直方向的間距
        attributes.verticalMargin = 0f;
        //設(shè)置Dialog顯示時(shí)X軸的坐標(biāo),具體屏幕X軸的偏移量
        attributes.x = 0;
        //設(shè)置Dialog顯示時(shí)Y軸的坐標(biāo),距離屏幕Y軸的偏移量
        attributes.y = 0;
        //設(shè)置Dialog的透明度
        attributes.alpha = 0f;
        //設(shè)置Dialog顯示和消失時(shí)的動(dòng)畫
        attributes.windowAnimations = 0;
        window.setAttributes(attributes);
        Logger.d(TAG, "onStart");
    }

三、DialogFragmeng源碼分析

1.下面先貼一下DialogFragment的部分源碼踱蠢,并進(jìn)行簡(jiǎn)要分析火欧。

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

    public DialogFragment() {
    }
    
    //標(biāo)準(zhǔn)的顯示Fragment的方法棋电,沒什么好說的
    public void show(FragmentManager manager, String tag) {
        this.mDismissed = false;
        this.mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commit();
    }
    
    //傳入一個(gè)事務(wù)管理,將fragment加入到事務(wù)管理中苇侵,并返回回退棧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;
    }
    
    //第一個(gè)show方法的加強(qiáng)版赶盔,看名字就知道,使用這個(gè)方法之后榆浓,則會(huì)立即執(zhí)行fragment當(dāng)中相關(guān)的方法于未,這個(gè)待會(huì)兒作出解釋
    public void showNow(FragmentManager manager, String tag) {
        this.mDismissed = false;
        this.mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commitNow();
    }

    //Dialaog消失時(shí)的回調(diào),setOndismissListener是在onActivityCreate中設(shè)置的,當(dāng)前正是把dialog給dismiss陡鹃,并沒有讓dialogfragment出棧
    public void dismiss() {
        this.dismissInternal(false);
    }
    
    //dismiss的加強(qiáng)版烘浦,先消失dialog,并將dialogfragment移除棧內(nèi)
    public void dismissAllowingStateLoss() {
        this.dismissInternal(true);
    }

    //顯示判斷dialog萍鲸,是否已經(jīng)消失闷叉,如果已經(jīng)消失,則不做任何操作
    //1.如果dialog實(shí)例不為空脊阴,先調(diào)用dialog的dismiss方法握侧,隱藏dialog
    //2.如果先前調(diào)用的是public int show(FragmentTransaction transaction, String tag)
    //方法顯示的dialogfragment,那么此時(shí)會(huì)根據(jù)之前返回的mBackStackId來將fragment移除棧內(nèi)
    //3.如果不是則再啟用事務(wù)將dialogfragment移除棧內(nèi),這里會(huì)根據(jù)傳入的allowStateLoss來區(qū)分
    //提交事務(wù)的方法
    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設(shè)置了onDismissListener后的回調(diào)品擎,如果dialog正常消失,次回調(diào)中的方法不會(huì)調(diào)用到
    //因?yàn)樵贒ialogFragment的onStar方法中將mViewDestroyed變量賦值為true备徐,dialog顯示設(shè)置到調(diào)用顯示出來的生命周期回調(diào)我們已經(jīng)打印過了萄传。
    public void onDismiss(DialogInterface dialog) {
        if (!this.mViewDestroyed) {
            this.dismissInternal(true);
        }
    }

    //該方法,先判斷Dialog是否已經(jīng)顯示坦喘,然后會(huì)取onCreateView中返回的View,如果View不為空西设,那么該View將作為Dialog的內(nèi)容布局瓣铣,所以,如果你同時(shí)重寫了onCreateDialog和onCreateView方法贷揽,那么會(huì)優(yōu)先采用onCreateView當(dāng)中的View作為內(nèi)容布局,然后再作了一些監(jiān)聽設(shè)置
    //設(shè)置了dialog是否可點(diǎn)擊
    //設(shè)置了dialog的消失監(jiān)聽onDismissListener,所以消失時(shí)會(huì)回調(diào)文中的dimiss方法
    //設(shè)置了dialog的取消監(jiān)聽onCancelListener,在取消時(shí)會(huì)回調(diào)文中的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.源碼分析總結(jié)

  • 可以重寫onCreateDialog或者onCreateView來創(chuàng)建DialogFragment的視圖棠笑,但是onCretaeView的優(yōu)先級(jí)要高于onCreateDilaog,所以沒有特殊的需要禽绪,一般通過重寫onCreateView來創(chuàng)建Dialog的視圖即可蓖救。

  • 從一開始我們就打印了DialogFragment從創(chuàng)建視圖到show出來回調(diào)的一系列生命周期方法,我們得知設(shè)置dialog的屬性可以在DialogFragment中的onStar回調(diào)中進(jìn)行印屁。

  • 對(duì)于dialog的顯示我們通過查看源碼得知一共有三個(gè)方法循捺,show()方法傳入一個(gè)fragmentManager以及一個(gè)tag,showNow方法雄人,傳入一個(gè)fragmentManger和一個(gè)tag从橘,show()方法念赶,傳入一個(gè)事務(wù)和一個(gè)tag,并返回該事務(wù)中回退棧的id恰力,在調(diào)用dismissInternal()方法時(shí)叉谜,會(huì)根據(jù)顯示DialogFragment的類型來操作DialogFragment出棧。

  • DialogFragment會(huì)在onActivityCreate()中對(duì)Dialog的內(nèi)容試圖進(jìn)行一次賦值踩萎,如果你重寫了onCrateView方法停局,并返回了一個(gè)不為空的實(shí)例,那么將會(huì)作為該Dialog的內(nèi)容視圖香府,然后再對(duì)dialog設(shè)置了是否可取消董栽,消失監(jiān)聽,以及取消監(jiān)聽

  • 通過上面的源碼簡(jiǎn)析回还,我們得知在DialogFragment消失時(shí)裆泳,只會(huì)將dialog給隱藏,并不會(huì)講DialogFragment移除棧內(nèi)柠硕,如果想講DialogFragment在dialog消失時(shí)移除棧內(nèi)工禾,那么需要手動(dòng)調(diào)用dismissInternal()方法

四、細(xì)節(jié)注意事項(xiàng)

  • 可以通過重寫onCreateDialog和onCreateView來設(shè)置DialogFragment的視圖蝗柔,但onCraeteView設(shè)置的視圖優(yōu)先級(jí)要高于在onCreateDialog闻葵。

  • 設(shè)置Dialog的屬性需要在onCreateDialog回調(diào)后設(shè)置,不然getDialog得到的對(duì)象則會(huì)是null,推薦在onStar方法中對(duì)Dialog進(jìn)行屬性設(shè)置癣丧。

  • DialogFragment單個(gè)實(shí)例只能show一次DialogFrament,如果多次展示的話槽畔,則會(huì)拋出如下異常

    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后并不會(huì)直接從當(dāng)前的棧中移除,而是在DialogFragment中的onDestroyView()回調(diào)時(shí)胁编,才會(huì)對(duì)DialogFragment進(jìn)行出棧操作厢钧,所以如果你如果需要在Activity中頻繁的顯示隱藏一個(gè)DialogFragment,那么在dismiss時(shí)需要手動(dòng)的調(diào)用dismissAllowingStateLoss()方法嬉橙,并且再次show時(shí)不能用上一個(gè)DialogFragment實(shí)例早直。

  • DialogFragment并沒有對(duì)Dialog的消失提供監(jiān)聽給調(diào)用者使用,但是我們通過源碼分析得知市框,DialogFragment在onActivityCreate當(dāng)中其實(shí)已經(jīng)幫我們?cè)O(shè)置了onDismissListener,所以我們?nèi)绻枰獙?duì)Dialog的消失進(jìn)行監(jiān)聽的話只需重寫onDismiss方法即可霞扬,還有一種方式則是覆蓋父類設(shè)置的onDismissListener監(jiān)聽,但是需注意的時(shí)枫振,這個(gè)監(jiān)聽的復(fù)寫喻圃,必須在父類onActivityCreate方法調(diào)用之后,關(guān)于消失監(jiān)聽的兩種寫法如下:

//mListener為提供到外部使用的回調(diào)
 @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        mListener.onDismiss(dialog);
        Logger.d(TAG, "onDismiss");
    }
    
    //復(fù)寫setOnDismissListener必須發(fā)生在父類的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");
    }

結(jié)語:以上便是全文的內(nèi)容粪滤,是自己在使用DialogFragment中碰到了一些坑以及學(xué)習(xí)后的一些理解斧拍,希望能對(duì)您有幫助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末杖小,一起剝皮案震驚了整個(gè)濱河市饮焦,隨后出現(xiàn)的幾起案子怕吴,更是在濱河造成了極大的恐慌,老刑警劉巖县踢,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件转绷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡硼啤,警方通過查閱死者的電腦和手機(jī)议经,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谴返,“玉大人煞肾,你說我怎么就攤上這事∩じぃ” “怎么了籍救?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)渠抹。 經(jīng)常有香客問我蝙昙,道長(zhǎng),這世上最難降的妖魔是什么梧却? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任奇颠,我火速辦了婚禮,結(jié)果婚禮上放航,老公的妹妹穿的比我還像新娘烈拒。我一直安慰自己,他們只是感情好广鳍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布荆几。 她就那樣靜靜地躺著,像睡著了一般赊时。 火紅的嫁衣襯著肌膚如雪吨铸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天蛋叼,我揣著相機(jī)與錄音焊傅,去河邊找鬼剂陡。 笑死狈涮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鸭栖。 我是一名探鬼主播歌馍,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼晕鹊!你這毒婦竟也來了松却?” 一聲冷哼從身側(cè)響起暴浦,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晓锻,沒想到半個(gè)月后歌焦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砚哆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年独撇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躁锁。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纷铣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出战转,到底是詐尸還是另有隱情搜立,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布槐秧,位于F島的核電站啄踊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏色鸳。R本人自食惡果不足惜社痛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望命雀。 院中可真熱鬧蒜哀,春花似錦、人聲如沸吏砂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狐血。三九已至淀歇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匈织,已是汗流浹背浪默。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缀匕,地道東北人纳决。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像乡小,于是被迫代替她去往敵國(guó)和親阔加。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353