一、背景
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吧!
三、為什麼選擇DialogFragment?DialogFragment
繼承自
Fragment
,即可以用
Fragment
來展示
Dialog
,相比于用
AlertDialog
或者
Dialog
,
DialogFragment
更有優勢:
● 當手機配置變化導緻
Activity
重建時(比如旋轉螢幕)或點選實體傳回鍵時,
DialogFragment
可以管理好自己的生命周期
●
DialogFragment
Fragment
,是以
DialogFragment
也可以當做一個内嵌的元件來使用,是以
DialogFragment
有更好的複用性
四、UML圖
用一個UML圖大緻來表示一下類之間的關系:
● 内置一個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();
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
原文釋出時間為:2018-09-26
本文作者:mmmqqq
本文來自雲栖社群合作夥伴“
安卓巴士Android開發者門戶”,了解相關資訊可以關注“
”。