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()
給關掉了,用單例方式顯然不太合適。趁著版本大改版,花了點時間重新擼了一個。根據我們的專案需要,調研了下,大概需要符合以下場景:
不用提供佈局,內建專案中常用預設的樣式
2、
支援自定義複雜的佈局、動畫、對話方塊大小、背景色等設定
3、 統一管理多個dialog並順序彈出
第一點:大部分情況下,使用對話方塊的樣式都是一致的,所以內建了預設的 dialog
樣式,可以避免呼叫方每次再去找佈局檔案,儘可能的簡化呼叫。ps:內建 dialog
樣式可以根據需求自行修改。
第二點:如果需要自定義複雜的佈局,需要支援佈局子View的建立及一系列互動事件。
第三點:專案中有個需求,可能一次會產生多個 dialog
,需要依次彈出 dialog
。
基於以上需求點,使用 DialogFragment
封裝了一個通用 Dialog
—— SYDialog
,先看最終效果圖
二、效果圖

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

image
三、為什麼選擇DialogFragment?
DialogFragment
繼承自 Fragment
,即可以用 Fragment
來展示 Dialog
,相比於用 AlertDialog
或者 Dialog
, DialogFragment
更有優勢:
- 當手機配置變化導致
Activity
重建時(比如旋轉螢幕)或點選物理返回鍵時,DialogFragment
可以管理好自己的生命週期 -
DialogFragment
繼承自Fragment
,所以DialogFragment
也可以當做一個內嵌的元件來使用,所以DialogFragment
有更好的複用性
四、UML圖
用一個UML圖大致來表示一下類之間的關係:

SYDialog.png
五、使用文件
1、使用內建dialog:
- 內建一個Button的樣式:
new SYDialog.Builder(this) .setTitle("我是標題") .setContent("您好,我們將在30分鐘處理,稍後通知您訂單結果!") .setPositiveButton(new IDialog.OnClickListener() { @Override public void onClick(IDialog dialog) { dialog.dismiss(); } }) .show();
效果圖:

IMG_20180919_192552.jpg
- 內建二個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();
效果圖:

IMG_20180919_192531.jpg
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
並展示,直到佇列是空為止。
六、原始碼地址
上述例子原始碼: AndroidStudy" target="_blank" rel="nofollow,noindex">https://github.com/crazyqiang/AndroidStudy
引用:
【1】 https://developer.android.com/reference/android/app/DialogFragment
【2】 https://blog.csdn.net/lmj623565791/article/details/37815413