手把手帶你玩轉(zhuǎn) DialogFragment

前言

本文已經(jīng)收錄到我的 Github 個(gè)人博客嚷堡,歡迎大佬們光臨寒舍:

我的 GIthub 博客

思維導(dǎo)圖

image

一琳水、為什么要學(xué)習(xí) DialogFragment

你還在用 Dialog 嗎几颜?

你還在經(jīng)常煩惱于屏幕翻轉(zhuǎn)的時(shí)候秒拔,Dialog 的各種奇葩情況嗎?

你想降低耦合嗎咽弦?

如果你有其中的一個(gè)煩惱,那么恭喜你胁出,遇見了 DialogFragment 型型,他恰巧就解決了上面所說的問題,如果感興趣的話全蝶,隨筆者來看下吧闹蒜!

image

二、背景

Android 官方推薦使用 DialogFragment 來代替 Dialog 抑淫,可以讓它具有更高的可復(fù)用性(降低耦合)和更好的便利性(很好的處理屏幕翻轉(zhuǎn)的情況)绷落。

而創(chuàng)建 DialogFragment 有兩種方式:

  • 法一:覆寫其 onCreateDialog 方法

一般用于創(chuàng)建替代傳統(tǒng)Dialog 對(duì)話框的場(chǎng)景,UI 簡單始苇,功能單一,不適用于使用了多線程(例如網(wǎng)絡(luò)請(qǐng)求)的情況下(因?yàn)椴荒苷_的獲取當(dāng)前 Fragment 的狀態(tài)砌烁,會(huì)產(chǎn)生空指針異常)

  • 法二:覆寫其 onCreateView 方法

一般用于創(chuàng)建復(fù)雜內(nèi)容彈窗全屏展示效果的場(chǎng)景,UI 復(fù)雜催式,功能復(fù)雜函喉,一般有網(wǎng)絡(luò)請(qǐng)求等異步操作

image

三避归、應(yīng)用

3.1 基本用法是什么

法一:

a.創(chuàng)建一個(gè)簡單的 Dialog 并返回它即可

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    // 設(shè)置主題的構(gòu)造方法
    // AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomDialog);
    builder.setTitle("注意:")
           .setMessage("是否退出應(yīng)用?")
           .setPositiveButton("確定", null)
           .setNegativeButton("取消", null)
           .setCancelable(false);
           //builder.show(); // 不能在這里使用 show() 方法
    return builder.create();
}
運(yùn)行截圖

b.你也可以使用自定義 View 來創(chuàng)建:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    // 設(shè)置主題的構(gòu)造方法
    // AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomDialog);
    LayoutInflater inflater = getActivity().getLayoutInflater();  
    View view = inflater.inflate(R.layout.fragment_dialog, null);  
    builder.setView(view) 
    // Do Someting,eg: TextView tv = view.findViewById(R.id.tv);
    return builder.create();
}
運(yùn)行截圖

PS:創(chuàng)建 Dialog 的方式有多種函似,比如下面這種槐脏,使用時(shí)略有差異喉童,需要自己注意:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = getActivity().getLayoutInflater();
    View view = inflater.inflate(R.layout.fragment_dialog, null);
    Dialog dialog = new Dialog(getActivity());
    // 設(shè)置主題的構(gòu)造方法
    // Dialog dialog = new Dialog(getActivity(), R.style.CustomDialog);
    dialog.setContentView(view);
    // Do Someting
    return dialog;
}
運(yùn)行截圖

這種情況撇寞,標(biāo)題內(nèi)容上面的白色部分,其實(shí)是默認(rèn)的標(biāo)題欄堂氯,如果需要的話蔑担,可以設(shè)置隱藏標(biāo)題欄(將在下文說到)

3.2 如何處理屏幕翻轉(zhuǎn)

如果使用傳統(tǒng)的 Dialog ,需要我們手動(dòng)處理屏幕翻轉(zhuǎn)的情況咽白,但使用 DialogFragment 的話啤握,則不需要我們進(jìn)行任何處理,FragmentManager 會(huì)自動(dòng)管理 DialogFragment 的生命周期晶框。

3.3 如何隱藏標(biāo)題欄

在基本用法里代碼注釋有設(shè)置主題的地方排抬,下面詳細(xì)說下兩種方法下設(shè)置無標(biāo)題欄的方式:

法一:

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = Objects.requireNonNull(getActivity()).getLayoutInflater();
    @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.fragment_i_o_s_dialog, null);
    Dialog dialog = new Dialog(getActivity());
    // 關(guān)閉標(biāo)題欄,setContentView() 之前調(diào)用
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.setContentView(view);
    dialog.setCanceledOnTouchOutside(true);
    return dialog;
}

法二:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setStyle(DialogFragment.STYLE_NO_TITLE, 0);
}

3.4 如何實(shí)現(xiàn)全屏

常用的形式大多是寬度上和屏幕一樣寬授段,高度自適應(yīng)蹲蒲,下面直接看代碼:

法一:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = getActivity().getLayoutInflater();
    View view = inflater.inflate(R.layout.fragment_dialog, null);
    Dialog dialog = new Dialog(getActivity(), 0);
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.setContentView(view);
    dialog.setCanceledOnTouchOutside(true);
    //Do something
    // 設(shè)置寬度為屏寬、位置靠近屏幕底部
    Window window = dialog.getWindow();
    //設(shè)置了窗口的背景色為透明侵贵,這一步是必須的
    // <color name="transparent">#50000000</color>
    window.setBackgroundDrawableResource(R.color.transparent);
    WindowManager.LayoutParams wlp = window.getAttributes();
    wlp.gravity = Gravity.BOTTOM;
    //設(shè)置窗口的寬度為 MATCH_PARENT,效果是和屏幕寬度一樣大
    wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
    wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
    window.setAttributes(wlp);
    return dialog;
}

法二:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setStyle(DialogFragment.STYLE_NO_TITLE, 0);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        getDialog().setCanceledOnTouchOutside(true);
        View rootView = inflater.inflate(R.layout.fragment_dialog, container, false);
        //Do something
        // 設(shè)置寬度為屏寬届搁、靠近屏幕底部。
        final Window window = getDialog().getWindow();
        //這步是必須的
        window.setBackgroundDrawableResource(R.color.transparent);
        //必要窍育,設(shè)置 padding卡睦,這一步也是必須的,內(nèi)容不能填充全部寬度和高度
        window.getDecorView().setPadding(0, 0, 0, 0);
        WindowManager.LayoutParams wlp = window.getAttributes();
        wlp.gravity = Gravity.BOTTOM;
        wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
        wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
        window.setAttributes(wlp);
        return rootView;
}

3.5 應(yīng)用場(chǎng)景的區(qū)別是什么

文章一開始簡單總結(jié)了法一 和法二的應(yīng)用場(chǎng)景漱抓,這里說明下:

  • 法一:為簡單的替代 Dialog 提供了非常方便的創(chuàng)建方式表锻,但是在使用了多線程(例如網(wǎng)絡(luò)請(qǐng)求)的情況下,不能正確的獲取當(dāng)前 Fragment 的狀態(tài)乞娄,會(huì)產(chǎn)生空指針異常
  • 法二:則沒有如上空指針的問題浩嫌,而且,其創(chuàng)建方式默認(rèn)使用了自定義 View补胚,更便于應(yīng)對(duì)復(fù)雜 UI 的場(chǎng)景

3.6 如何與 Activity 進(jìn)行交互码耐?

使用回調(diào)的方式

a.在 DialogFragment 中:

public interface OnDialogListener {
    void onDialogClick(String person);
}

private OnDialogListener mlistener;

public void setOnDialogListener(OnDialogListener dialogListener){
    this.mlistener = dialogListener;
}

DialogFragment 的點(diǎn)擊事件中:

public OnDialogListener mlistener;
@Override
public void onClick(View view) {
    switch (view.getId()) {
        case R.id.tv1:
            mlistener.onDialogClick("1");
            dismiss();
            break; 
       case R.id.tv2:
            mlistener.onDialogClick("2");
            dismiss();
            break;
        case R.id.tv3:
            mlistener.onDialogClick("3");
            dismiss();
            break;
        case R.id.tv4:
            mlistener.onDialogClick("4");
            dismiss();
            break;
    }
}

b.在 Activity

dialogFragment.setOnDialogListener(new PersonDialogFragment.OnDialogListener() {
    @Override
    public void onDialogClick(String person) {
        ToastUtil.showToast(person);
    }
});

3.7 如何結(jié)合動(dòng)畫使用

a.設(shè)置從下到上彈出的動(dòng)畫

private void slideToUp(View view) {
    Animation slide = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0);
    slide.setDuration(400);
    slide.setFillEnabled(true);
    slide.setFillAfter(true);
    view.startAnimation(slide);
}

b.設(shè)置從上到下彈出的動(dòng)畫

private boolean isAnimation = false;//用來判斷是否多次點(diǎn)擊。防止多次執(zhí)行

public void slideToDown(View view) {
    Animation slide = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 0.0f, 
            Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 1.0f);
    slide.setDuration(400);
    slide.setFillEnabled(true);
    slide.setFillAfter(true);
    view.startAnimation(slide);
    slide.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            //用來判斷是否多次點(diǎn)擊溶其。防止多次執(zhí)行
            isAnimation = false;
            //彈框消失
            IOSDialogFragment.this.dismiss();
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }
    });
}

c.封裝從上到下彈出的動(dòng)畫

加上判斷是否多次點(diǎn)擊骚腥。防止多次執(zhí)行

private void dialogFinish() {
    if (isAnimation) {
        return;
    }
    isAnimation = true;
    slideToDown(rootView);
}

3.8 如何在 Activity 彈出 DialogFragment ?

mBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        IOSDialogFragment fragment = new IOSDialogFragment();
        //第二個(gè)參數(shù)是 tag
        fragment.show(getSupportFragmentManager(), "android");
    }
});

3.9 如何點(diǎn)擊空白處時(shí)關(guān)閉的時(shí)候,還能使用動(dòng)畫瓶逃?

直接對(duì) DecorView 設(shè)置 onTouchListener

window.getDecorView().setOnTouchListener(new View.OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            //彈框消失的動(dòng)畫執(zhí)行相關(guān)代碼
            ....
            ....
        
        }
        return true;
    }
});

四束铭、結(jié)語

終于看完了鴨廓块!累死鴨了!如果還有什么不是很清楚的話契沫,可以看下筆者寫的示例 Demo

image

如果文章對(duì)您有一點(diǎn)幫助的話带猴,希望您能點(diǎn)一下贊,您的點(diǎn)贊懈万,是我前進(jìn)的動(dòng)力

本文參考鏈接:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拴清,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子会通,更是在濱河造成了極大的恐慌口予,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涕侈,死亡現(xiàn)場(chǎng)離奇詭異沪停,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)裳涛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門木张,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人端三,你說我怎么就攤上這事舷礼。” “怎么了技肩?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵且轨,是天一觀的道長。 經(jīng)常有香客問我虚婿,道長旋奢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任然痊,我火速辦了婚禮至朗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘剧浸。我一直安慰自己锹引,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布唆香。 她就那樣靜靜地躺著嫌变,像睡著了一般。 火紅的嫁衣襯著肌膚如雪躬它。 梳的紋絲不亂的頭發(fā)上腾啥,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼倘待。 笑死疮跑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凸舵。 我是一名探鬼主播祖娘,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼啊奄!你這毒婦竟也來了渐苏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤增热,失蹤者是張志新(化名)和其女友劉穎整以,沒想到半個(gè)月后胧辽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體峻仇,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年邑商,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了摄咆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡人断,死狀恐怖吭从,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恶迈,我是刑警寧澤涩金,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站暇仲,受9級(jí)特大地震影響步做,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奈附,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一全度、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斥滤,春花似錦将鸵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挑胸,卻和暖如春痒筒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國打工凸克, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留议蟆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓萎战,卻偏偏與公主長得像咐容,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚂维,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354