Android DialogFragment使用總結(jié)

DiaoFragment使用背景

在 Android 中顯示對話框有兩種類型可供使用,一種是 DialogFragment,而另一種則是 Dialog,Android 官方推薦使用 DialogFragment 來代替 Dialog

1.DialogFragment 本身是 Fragment 的子類,有著和 Fragment 基本一樣的生命周期,使用 DialogFragment 來管理對話框,當(dāng)旋轉(zhuǎn)屏幕和按下后退鍵的時候可以更好的管理其生命周期

2.在手機(jī)配置變化導(dǎo)致 Activity 需要重新創(chuàng)建時,例如旋轉(zhuǎn)屏幕,基于 DialogFragment 的對話框?qū)?FragmentManager 自動重建,然而基于 Dialog 實現(xiàn)的對話框卻沒有這樣的能力

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

  1. 覆寫其 onCreateDialog 方法 — ①
  2. 覆寫其 onCreateView 方法 — ②

雖然這兩種方式都能實現(xiàn)相同的效果,但是它們各有自己適合的應(yīng)用場景:

  1. 方法 ①,一般用于創(chuàng)建替代傳統(tǒng)的 Dialog 對話框的場景,UI 簡單,功能單一。
  2. 方法 ②,一般用于創(chuàng)建復(fù)雜內(nèi)容彈窗或全屏展示效果的場景,UI 復(fù)雜,功能復(fù)雜,一般有網(wǎng)絡(luò)請求等異步操作。

應(yīng)用

基本用法

對于方法 ①
創(chuàng)建一個 Dialog 并返回它即可:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    //為了樣式統(tǒng)一和兼容性,可以使用 V7 包下的 AlertDialog.Builder
    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();
}

當(dāng)然,你也可以使用自定義 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();
}

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

@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;
}
對于方法 ②,和普通的 Fragment 用法基本一致:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_dialog, container, false);
    // Do Someting
    return rootView;
}
/**
 * 設(shè)置主題需要在 onCreate() 方法中調(diào)用 setStyle() 方法
 */
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setStyle(DialogFragment.STYLE_NO_TITLE, R.style.CustomDialog);
}

處理屏幕翻轉(zhuǎn)

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

擴(kuò)展閱讀:Dialog 在屏幕翻轉(zhuǎn)時的處理

無標(biāo)題欄/全屏

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

無標(biāo)題欄

對于方法 ① :

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = getActivity().getLayoutInflater();
    View view = inflater.inflate(R.layout.fragment_dialog, null);
    Dialog dialog = new Dialog(getActivity(), R.style.CustomDialog);
    // 關(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() 的第一個參數(shù)有四個可選值:
     * STYLE_NORMAL|STYLE_NO_TITLE|STYLE_NO_FRAME|STYLE_NO_INPUT
     * 其中 STYLE_NO_TITLE 和 STYLE_NO_FRAME 可以關(guān)閉標(biāo)題欄
     * 每一個參數(shù)的詳細(xì)用途可以直接看 Android 源碼的說明
     */
    setStyle(DialogFragment.STYLE_NO_TITLE, R.style.CustomDialog);
}

實現(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(), R.style.CustomDialog);
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.setContentView(view);
    dialog.setCanceledOnTouchOutside(true);
    //Do something
    // 設(shè)置寬度為屏寬、位置靠近屏幕底部
    Window window = dialog.getWindow();
    window.setBackgroundDrawableResource(R.color.transparent);
    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 dialog;
}

代碼 12 行設(shè)置了窗口的背景色為透明,這一步是必須的;

代碼 15 行設(shè)置窗口的寬度為 MATCH_PARENT,效果是和屏幕寬度一樣大,同樣你也可以設(shè)置高度的值。設(shè)置寬度和高度除了 MATCH_PARENT 和 WRAP_CONTENT ,也可以直接設(shè)置成具體的數(shù)值。

方法 ②:

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

@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);
        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;
}

代碼 14 行設(shè)置了窗口的背景色為透明,這一步是必須的;

代碼 15 行設(shè)置了窗口的 Pading 值全部為0,這一步也是必須的,內(nèi)容不能填充全部寬度和高度。

其他的和 ① 中的類似。

應(yīng)用場景區(qū)別

文章一開始簡單總結(jié)了方法 ① 和方法 ② 的應(yīng)用場景,這里說明下:

  • 從基本的用法中可以看到方法 ① 為簡單的替代 Dialog 提供了非常方便的創(chuàng)建方式,比方法 ② 有優(yōu)勢
  • 方法 ① 在使用了多線程(例如網(wǎng)絡(luò)請求)的情況下,不能正確的獲取當(dāng)前 Fragment 的狀態(tài),會產(chǎn)生空指針異常。方法 ② 則沒有此問題,而且,其創(chuàng)建方式默認(rèn)使用了自定義 View,更便于應(yīng)對復(fù)雜 UI 的場景。

這里舉例說明:方法 ① 和方法 ② 的//Do something 代碼處,我們進(jìn)行一些異步操作:

TextView title = rootView.findViewById(R.id.dialoag_tv);
title.setText("Value A");
new SomeTask().execute(url);

private class SomeTask extends AsyncTask<String, Void, Boolean> {

        @Override
        protected Boolean doInBackground(String... params) {
            // 一些網(wǎng)絡(luò)請求
            // 成功時 return true;
            // 異常時 return false;
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            // if (!isVisible()) return;
            if (aBoolean) {
                title.setText("Value B");
            }
        }
}

如果在網(wǎng)絡(luò)請求未結(jié)束或者剛結(jié)束時關(guān)閉了當(dāng)前 DialogFragment ,代碼 19 行就會報空指針異常了,代碼 17 的作用就是判斷 DialogFragment 當(dāng)前的狀態(tài)是否為可用態(tài),但是 isVisible() 以及 isHide() 對于方法 ① 無法正確返回其狀態(tài),方法 ② 則正常。方法 ① 和方法 ② 創(chuàng)建 DialogFragment 的一些狀態(tài)并不是完全一致。

PS:你也可以使用靜態(tài) Handler 結(jié)合弱引用等來處理異步操作,那樣對 DialogFragment 狀態(tài)的獲取就不重要了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容