Android 如何實(shí)現(xiàn)彈窗順序&優(yōu)先級(jí)控制
一般在項(xiàng)目首頁(yè)中,往往會(huì)有多個(gè)對(duì)話框需要彈出,比如活動(dòng)彈窗、更新彈窗、評(píng)分彈窗等等,而且這些彈窗是有優(yōu)先級(jí)順序的。這些彈窗一般是通過(guò)接口請(qǐng)求后返回結(jié)果再顯示的,如果只有幾個(gè)彈窗還好處理,業(yè)務(wù)邏輯上判斷一下先后顯示就可以。如果有十幾個(gè)或者更多,那么處理起來(lái)將非常麻煩,而且容易出現(xiàn)問(wèn)題。
所以封裝一個(gè)可以按照優(yōu)先級(jí)順序顯示的彈窗功能就非常有必要,首先功能需求如下:
- 按優(yōu)先級(jí)順序阻塞式顯示各種類型彈窗,默認(rèn)從最高優(yōu)先級(jí)開始顯示
- 只有上一個(gè)高優(yōu)先級(jí)彈窗顯示完或者取消顯示,下一個(gè)低優(yōu)先級(jí)彈窗才可以顯示
- 指定顯示某一個(gè)彈窗的前提是沒(méi)有更高優(yōu)先級(jí)的彈窗需要顯示
- 在顯示一個(gè)彈窗之前需要判斷是否能夠或者需要顯示
- 根據(jù)優(yōu)先級(jí)去查找指定的彈窗,優(yōu)先級(jí)相當(dāng)于唯一ID
- 彈窗包括多種類型,Dialog、PopupWindow、Activity等等
接著開始編碼去實(shí)現(xiàn)功能,先定一個(gè)枚舉類,羅列出支持的彈窗類型,包括Dialog、PopupWindow、Activity等等。
public enum WindowType {
DIALOG,
POUPOWINDOW,
TOAST,
SNACKBAR,
WIDGET,
ACTIVITY,
OTHERS
}
然后定義彈窗接口IWindow,它定義了彈窗的基本功能。
/**
* 窗口約定規(guī)則
*/
public interface IWindow {
/**
* 彈窗展示
*/
void show(Activity activity);
/**
* 彈窗關(guān)閉
*/
void dismiss();
/**
* 設(shè)置窗口關(guān)閉監(jiān)聽
*/
void setOnWindowDismissListener(OnWindowDismissListener listener);
/**
* 設(shè)置窗口展示監(jiān)聽
*/
void setOnWindowShowListener(OnWindowShowListener listener);
}
以及彈窗顯示和關(guān)閉的監(jiān)聽接口,
/**
* 窗口關(guān)閉監(jiān)聽
*/
public interface OnWindowDismissListener {
/**
*
*/
void onDismiss();
}
/**
* 窗口展示監(jiān)聽
*/
public interface OnWindowShowListener {
void onShow();
}
接下來(lái)定義個(gè)包裹類WindowWrapper去封裝彈窗相關(guān)的屬性和狀態(tài),包括彈窗、優(yōu)先級(jí)、能否顯示、窗體類型等等,在處理彈窗顯示邏輯時(shí)將會(huì)用到。
/**
* 窗口參數(shù)類
*/
public class WindowWrapper {
/**
* 窗口
*/
private IWindow mWindow;
/**
* 優(yōu)先級(jí),值越大優(yōu)先級(jí)越高
*/
private int mPriority;
/**
* 當(dāng)前是否處于show狀態(tài)
*/
private boolean isShowing;
/**
* 是否滿足show的條件
*/
private boolean isCanShow;
/**
* 彈窗類型
*/
private WindowType mWindowType;
/**
* 彈窗名稱
*/
private String mWindowName;
private WindowWrapper(Builder builder) {
mWindow = builder.window;
mPriority = builder.priority;
mWindowType = builder.windowType;
isCanShow = builder.isCanShow;
mWindowName = builder.windowName;
}
public IWindow getWindow() {
return mWindow;
}
public void setWindow(IWindow window) {
this.mWindow = window;
}
public int getPriority() {
return mPriority;
}
public void setPriority(int priority) {
this.mPriority = priority;
}
public boolean isShowing() {
return isShowing;
}
public void setShowing(boolean showing) {
isShowing = showing;
}
public WindowType getWindowType() {
return mWindowType;
}
public void setWindowType(WindowType mWindowType) {
this.mWindowType = mWindowType;
}
public boolean isCanShow() {
return isCanShow;
}
public void setCanShow(boolean canShow) {
isCanShow = canShow;
}
public String getWindowName() {
return mWindowName;
}
public void setWindowName(String mWindowName) {
this.mWindowName = mWindowName;
}
public static class Builder {
/**
* 窗口
*/
private IWindow window;
/**
* 優(yōu)先級(jí),值越大優(yōu)先級(jí)越高
*/
private int priority;
/**
* 彈窗類型
*/
private WindowType windowType;
/**
* 是否滿足show的條件
*/
private boolean isCanShow;
/**
* 彈窗名稱
*/
private String windowName;
public Builder window(IWindow window) {
this.window = window;
return this;
}
public Builder priority(int priority) {
this.priority = priority;
return this;
}
public Builder windowType(WindowType type) {
this.windowType = type;
return this;
}
public Builder setCanShow(boolean canShow) {
isCanShow = canShow;
return this;
}
public String getWindowName() {
return windowName;
}
public Builder setWindowName(String windowName) {
this.windowName = windowName;
return this;
}
public WindowWrapper build() {
return new WindowWrapper(this);
}
}
}
最后通過(guò)WindowTaskManager類去統(tǒng)一組織管理彈窗的添加、顯示、關(guān)閉等邏輯,
public class WindowTaskManager {
private List<WindowWrapper> mWindows;
private static WindowTaskManager mDefaultInstance;
private WindowTaskManager() {
}
/**
* 獲取彈窗管理者
*/
public static WindowTaskManager getInstance() {
if (mDefaultInstance == null) {
synchronized (WindowTaskManager.class) {
if (mDefaultInstance == null) {
mDefaultInstance = new WindowTaskManager();
}
}
}
return mDefaultInstance;
}
/**
* 添加彈窗
*
* @param windowWrapper 待顯示的彈窗
*/
public synchronized void addWindow(Activity activity, WindowWrapper windowWrapper) {
if (windowWrapper != null) {
if (mWindows == null) {
mWindows = new ArrayList<>();
}
if (windowWrapper.getWindow() != null) {
windowWrapper.getWindow().setOnWindowShowListener(new OnWindowShowListener() {
@Override
public void onShow() {
windowWrapper.setShowing(true);
}
});
windowWrapper.getWindow().setOnWindowDismissListener(new OnWindowDismissListener() {
@Override
public void onDismiss() {
windowWrapper.setShowing(false);
mWindows.remove(windowWrapper);
showNext(activity);
}
});
}
mWindows.add(windowWrapper);
}
}
/**
* 彈窗滿足展示條件
*
* @param priority
*/
public synchronized void enableWindow(Activity activity, int priority, IWindow window) {
WindowWrapper windowWrapper = getTargetWindow(priority);
if (windowWrapper != null) {
if (windowWrapper.getWindow() == null) {
window.setOnWindowShowListener(new OnWindowShowListener() {
@Override
public void onShow() {
windowWrapper.setShowing(true);
}
});
window.setOnWindowDismissListener(new OnWindowDismissListener() {
@Override
public void onDismiss() {
windowWrapper.setShowing(false);
mWindows.remove(windowWrapper);
showNext(activity);
}
});
}
windowWrapper.setCanShow(true);
windowWrapper.setWindow(window);
show(activity, priority);
}
}
/**
* 移除不需要顯示彈窗
*
* @param priority
*/
public synchronized void disableWindow(int priority) {
WindowWrapper windowWrapper = getTargetWindow(priority);
if (windowWrapper != null && windowWrapper.getWindow() != null) {
if (mWindows != null) {
mWindows.remove(windowWrapper);
}
}
}
/**
* 展示彈窗
* 從優(yōu)先級(jí)最高的Window開始顯示
*/
public synchronized void show(Activity activity) {
WindowWrapper windowWrapper = getMaxPriorityWindow();
if (windowWrapper != null && windowWrapper.isCanShow()) {
IWindow window = windowWrapper.getWindow();
if (window != null) {
window.show(activity);
}
}
}
/**
* 顯示指定的彈窗
*
* @param priorities
*/
public synchronized void show(Activity activity, int priorities) {
WindowWrapper windowWrapper = getTargetWindow(priorities);
if (windowWrapper != null && windowWrapper.getWindow() != null) {
WindowWrapper topShowWindow = getShowingWindow();
if (topShowWindow == null) {
int priority = windowWrapper.getPriority();
WindowWrapper maxPriorityWindow = getMaxPriorityWindow();
if (maxPriorityWindow != null && windowWrapper.isCanShow() && priority >= maxPriorityWindow.getPriority()) {
if (windowWrapper.getWindow() != null) {
windowWrapper.getWindow().show(activity);
}
}
}
}
}
/**
* 清除彈窗管理者
*/
public synchronized void clear() {
if (mWindows != null) {
for (int i = 0, size = mWindows.size(); i < size; i++) {
if (mWindows.get(i) != null) {
IWindow window = mWindows.get(i).getWindow();
if (window != null) {
window.dismiss();
}
}
}
mWindows.clear();
}
WindowHelper.getInstance().onDestroy();
}
/**
* 清除彈窗管理者
*
* @param dismiss 是否同時(shí)dismiss掉彈窗管理者維護(hù)的彈窗
*/
public synchronized void clear(boolean dismiss) {
if (mWindows != null) {
if (dismiss) {
for (int i = 0, size = mWindows.size(); i < size; i++) {
if (mWindows.get(i) != null) {
IWindow window = mWindows.get(i).getWindow();
if (window != null) {
window.dismiss();
}
}
}
}
mWindows.clear();
}
WindowHelper.getInstance().onDestroy();
}
/**
* 展示下一個(gè)優(yōu)先級(jí)最大的Window
*/
private synchronized void showNext(Activity activity) {
WindowWrapper windowWrapper = getMaxPriorityWindow();
if (windowWrapper != null && windowWrapper.isCanShow()) {
if (windowWrapper.getWindow() != null) {
windowWrapper.getWindow().show(activity);
}
}
}
/**
* 獲取當(dāng)前棧中優(yōu)先級(jí)最高的Window(優(yōu)先級(jí)相同則返回后添加的彈窗)
*/
private synchronized WindowWrapper getMaxPriorityWindow() {
if (mWindows != null) {
int maxPriority = -1;
int position = -1;
for (int i = 0, size = mWindows.size(); i < size; i++) {
WindowWrapper windowWrapper = mWindows.get(i);
if (i == 0) {
position = 0;
maxPriority = windowWrapper.getPriority();
} else {
if (windowWrapper.getPriority() >= maxPriority) {
position = i;
maxPriority = windowWrapper.getPriority();
}
}
}
if (position != -1) {
return mWindows.get(position);
} else {
return null;
}
}
return null;
}
private synchronized WindowWrapper getTargetWindow(int priority) {
if (mWindows != null) {
for (int i = 0, size = mWindows.size(); i < size; i++) {
WindowWrapper windowWrapper = mWindows.get(i);
if (windowWrapper != null && windowWrapper.getPriority() == priority) {
return windowWrapper;
}
}
}
return null;
}
/**
* 獲取當(dāng)前處于show狀態(tài)的彈窗
*/
private synchronized WindowWrapper getShowingWindow() {
if (mWindows != null) {
for (int i = 0, size = mWindows.size(); i < size; i++) {
WindowWrapper windowWrapper = mWindows.get(i);
if (windowWrapper != null && windowWrapper.getWindow() != null && windowWrapper.isShowing()) {
return windowWrapper;
}
}
}
return null;
}
}
WindowTaskManager類有三個(gè)主要方法:
- addWindow(Activity activity, WindowWrapper windowWrapper)
- enableWindow(Activity activity, int priority, IWindow window)
- disableWindow(int priority)
需要按順序顯示的對(duì)話框統(tǒng)一使用addWindow方法添加,這是還未進(jìn)行網(wǎng)絡(luò)請(qǐng)求之前就要調(diào)用的。作用是告訴WindowTaskManager一共有多少個(gè)彈窗需要按順序顯示。當(dāng)網(wǎng)絡(luò)請(qǐng)求返回之后,如果需要顯示彈窗就調(diào)用enableWindow方法去顯示,如果不需要顯示彈窗就調(diào)用disableWindow方法,將這個(gè)彈窗從顯示隊(duì)列中移除。
以上就是按順序顯示彈窗的主要邏輯,使用的話窗體先繼承IWindow,實(shí)現(xiàn)相關(guān)方法。然后通過(guò)操作WindowTaskManager類就可以了。具體使用方法參見(jiàn)源碼。
項(xiàng)目地址:github.com/Geekince/Pr…
彩蛋:
需要在DialogFragment中顯示DialogFragment時(shí)候,最好不要直接在DialogFragment啟動(dòng)顯示,而是在DialogFragment的消失回調(diào)中啟動(dòng)顯示。因?yàn)楫?dāng)前一個(gè)DialogFragment消失的時(shí)候,getChildFragmentManager可能會(huì)失效,應(yīng)該在外層使用getFragmentManager。
以上就是Android 如何實(shí)現(xiàn)彈窗順序&優(yōu)先級(jí)控制的詳細(xì)內(nèi)容,更多關(guān)于Android 實(shí)現(xiàn)彈窗順序和優(yōu)先級(jí)控制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android控件ImageSwitcher實(shí)現(xiàn)左右圖片切換功能
這篇文章主要為大家詳細(xì)介紹了Android控件ImageSwitcher實(shí)現(xiàn)左右圖片切換功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05
Android基于OpenCV實(shí)現(xiàn)QR二維碼檢測(cè)
QR碼比普通一維條碼具有快速讀取和更大的存儲(chǔ)資料容量,也無(wú)需要像一維條碼般在掃描時(shí)需要直線對(duì)準(zhǔn)掃描儀。因此其應(yīng)用范圍已經(jīng)擴(kuò)展到包括產(chǎn)品跟蹤,物品識(shí)別,文檔管理,庫(kù)存營(yíng)銷等方面。本文講解Android基于OpenCV實(shí)現(xiàn)QR二維碼檢測(cè)的步驟2021-06-06
Android編程實(shí)現(xiàn)變化的雙重選擇框功能示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)變化的雙重選擇框功能,結(jié)合實(shí)例形式分析了Android雙重選擇框功能的樣式布局與功能實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-10-10
Android EditText隨輸入法一起移動(dòng)并懸浮在輸入法之上的示例代碼
這篇文章主要介紹了Android EditText隨輸入法一起移動(dòng)并懸浮在輸入法之上,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
Android如何實(shí)現(xiàn)掃描和生成二維碼
這篇文章主要為大家詳細(xì)介紹了Android如何實(shí)現(xiàn)掃描和生成二維碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
Android?拍照功能實(shí)現(xiàn)(手機(jī)關(guān)閉依然拍照)詳解及實(shí)例代碼
這篇文章主要介紹了?Android?拍照功能實(shí)現(xiàn)(手機(jī)關(guān)閉依然拍照)詳解及實(shí)例代碼的相關(guān)資料,這對(duì)Android相機(jī)在不開手機(jī)的情況下還能繼續(xù)拍照,附有實(shí)例Demo,需要的朋友可以參考下2016-12-12
android隱式意圖激活瀏覽器的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇android隱式意圖激活瀏覽器的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
android 仿微信demo——微信消息界面實(shí)現(xiàn)(服務(wù)端)
本系列文章主要介紹了微信小程序-閱讀小程序?qū)嵗╠emo),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望能給你們提供幫助2021-06-06
Android 四種動(dòng)畫效果的調(diào)用實(shí)現(xiàn)代碼
在這里, 我將每種動(dòng)畫分別應(yīng)用于四個(gè)按鈕為例,需要的朋友可以參考下2013-01-01

