天天看點

Android基于DialogFragment封裝一個通用的Dialog

一、背景

Dialog是項目中最常用的一個功能之一了,接手項目的時候發現項目中是封裝了一個dialog的,但是發現是用單例封裝的,大緻如下:

private MyDialog() {
     }

    public static MyDialog getInstance() {
        return DialogHolder.instance;
    }

    private static class DialogHolder {
        private static MyDialog instance = new MyDialog();
    }
   
   public void show(){}
   public void dismiss(){}
           

使用單例除了可能會有記憶體洩漏問題,在使用過程中還發現一個問題:不同頁面的dialog可以互相影響,想想也對,因為全局隻有一個

dialog

嘛,項目中有一個場景:A頁面跳B頁面,一進B頁面的

onCreate()

時需要彈一個dialog,發現每次都彈不出來,debug發現原來在A頁面的

onStop()

方法裡調用了

dismiss()

方法,A頁面跳B頁面生命周期走的是:

A頁面:   onPause()
 B頁面:    onCreate()
 B頁面:    onStart()
 B頁面:    onResume()
 A頁面:    onStop()           

是以原因也找到了,每次在B的

onCreate()

裡面剛調用了

show()

,緊接着又調用了A的

onStop()

中的

dismiss()

給關掉了,用單例方式顯然不太合适。趁着版本大改版,花了點時間重新撸了一個。根據我們的項目需要,調研了下,大概需要符合以下場景:

1、

不用提供布局,内置項目中常用預設的樣式

2、

支援自定義複雜的布局、動畫、對話框大小、背景色等設定

3、

統一管理多個dialog并順序彈出

第一點:大部分情況下,使用對話框的樣式都是一緻的,是以内置了預設的

dialog

樣式,可以避免調用方每次再去找布局檔案,盡可能的簡化調用。ps:内置

dialog

樣式可以根據需求自行修改。

第二點:如果需要自定義複雜的布局,需要支援布局子View的建立及一系列互動事件。

第三點: 項目中有個需求,可能一次會産生多個

dialog

,需要依次彈出

dialog

基于以上需求點,使用

DialogFragment

封裝了一個通用

Dialog

——

SYDialog

,先看最終效果圖

二、效果圖

gif圖比較模糊,直接掃二維碼下載下傳APK吧!

Android基于DialogFragment封裝一個通用的Dialog

三、為什麼選擇DialogFragment?

DialogFragment

繼承自

Fragment

,即可以用

Fragment

來展示

Dialog

,相比于用

AlertDialog

或者

Dialog

DialogFragment

更有優勢:

  • 當手機配置變化導緻

    Activity

    重建時(比如旋轉螢幕)或點選實體傳回鍵時,

    DialogFragment

    可以管理好自己的生命周期
  • DialogFragment

    Fragment

    ,是以

    DialogFragment

    也可以當做一個内嵌的元件來使用,是以

    DialogFragment

    有更好的複用性

四、UML圖

用一個UML圖大緻來表示一下類之間的關系:

五、使用文檔

1、使用内置dialog:

  • 内置一個Button的樣式:
new SYDialog.Builder(this)
           .setTitle("我是标題")
          .setContent("您好,我們将在30分鐘處理,稍後通知您訂單結果!")
          .setPositiveButton(new IDialog.OnClickListener() {
               @Override
               public void onClick(IDialog dialog) {
                   dialog.dismiss();
               }
           })
           .show();           

效果圖:

  • 内置二個Button的樣式:
new SYDialog.Builder(this)
           .setTitle("我是标題")
          .setContent("您好,我們将在30分鐘處理,稍後通知您訂單結果!")
          .setPositiveButton(new IDialog.OnClickListener() {
               @Override
               public void onClick(IDialog dialog) {
                   dialog.dismiss();
               }
           })
          .setNegativeButton(new IDialog.OnClickListener() {
               @Override
               public void onClick(IDialog dialog) {
                   dialog.dismiss();
               }
            })
           .show();           

2、使用自定義布局的樣式:

new SYDialog.Builder(this)
           .setDialogView(R.layout.layout_dialog)//設定dialog布局
           .setAnimStyle(R.style.translate_style)//設定動畫 預設沒有動畫
           .setScreenWidthP(0.85f) //設定螢幕寬度比例 0.0f-1.0f
           .setGravity(Gravity.CENTER)//設定Gravity
           .setWindowBackgroundP(0.2f)//設定背景透明度 0.0f-1.0f 1.0f完全不透明
           .setCancelable(true)//設定是否屏蔽實體傳回鍵 true不屏蔽  false屏蔽
           .setCancelableOutSide(true)//設定dialog外點選是否可以讓dialog消失
           .setBuildChildListener(new IDialog.OnBuildListener() {
                    //設定子View
                    @Override
                    public void onBuildChildView(final IDialog dialog, View view, int layoutRes) {
                        //dialog: IDialog
                        //view: DialogView
                        //layoutRes :Dialog的資源檔案 如果一個Activity裡有多個dialog 可以通過layoutRes來區分
                        final EditText editText = view.findViewById(R.id.et_content);
                        Button btn_ok = view.findViewById(R.id.btn_ok);
                        btn_ok.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                String editTextStr = null;
                                if (!TextUtils.isEmpty(editText.getText())) {
                                    editTextStr = editText.getText().toString();
                                }
                                dialog.dismiss();
                                Toast.makeText(MyApplication.getApplication(), editTextStr, Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
              }).show();           

代碼中注釋已經很詳細了,如果是自定義布局并且需要處理互動事件,可以通過設定

setBuildChildListener

并實作其回調,并在回調接口中建立子View并處理互動事件,使用起來還是很友善的。

3、統一管理多個Dialog依次彈出

SYDialog.Builder builder1 = new SYDialog.Builder(this);
SYDialog.Builder builder2 = new SYDialog.Builder(this)
//添加第一個Dialog
SYDialogsManager.getInstance().requestShow(new DialogWrapper(builder1));
//添加第二個Dialog
SYDialogsManager.getInstance().requestShow(new DialogWrapper(builder2));           

DialogWrapper

來包裝一層

Dialog

,友善後續添加資料資訊。

SYDialogsManager

通過單例來實作,確定隻有一個執行個體,内部有一個容器隊列

ConcurrentLinkedQueue

來儲存多個

Dialog

requestShow()

方法中首先會判斷目前是否有正在顯示的彈窗,如果有,則在隊列中等待,否則從隊列中取出并展示,并在隊列中清空該資料,當一個

Dialog

展示完畢,會繼續嘗試在隊列中取出

Dialog

并展示,直到隊列是空為止。

六、源碼位址

上述例子源碼:

https://github.com/crazyqiang/AndroidStudy

引用:

【1】

https://developer.android.com/reference/android/app/DialogFragment

【2】

https://blog.csdn.net/lmj623565791/article/details/37815413

繼續閱讀