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í)順序阻塞式顯示各種類(lèi)型彈窗,默認(rèn)從最高優(yōu)先級(jí)開(kāi)始顯示
- 只有上一個(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
- 彈窗包括多種類(lèi)型,Dialog、PopupWindow、Activity等等
接著開(kāi)始編碼去實(shí)現(xiàn)功能,先定一個(gè)枚舉類(lèi),羅列出支持的彈窗類(lèi)型,包括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)聽(tīng) */ void setOnWindowDismissListener(OnWindowDismissListener listener); /** * 設(shè)置窗口展示監(jiān)聽(tīng) */ void setOnWindowShowListener(OnWindowShowListener listener); }
以及彈窗顯示和關(guān)閉的監(jiān)聽(tīng)接口,
/** * 窗口關(guān)閉監(jiān)聽(tīng) */ public interface OnWindowDismissListener { /** * */ void onDismiss(); } /** * 窗口展示監(jiān)聽(tīng) */ public interface OnWindowShowListener { void onShow(); }
接下來(lái)定義個(gè)包裹類(lèi)WindowWrapper去封裝彈窗相關(guān)的屬性和狀態(tài),包括彈窗、優(yōu)先級(jí)、能否顯示、窗體類(lèi)型等等,在處理彈窗顯示邏輯時(shí)將會(huì)用到。
/** * 窗口參數(shù)類(lèi) */ public class WindowWrapper { /** * 窗口 */ private IWindow mWindow; /** * 優(yōu)先級(jí),值越大優(yōu)先級(jí)越高 */ private int mPriority; /** * 當(dāng)前是否處于show狀態(tài) */ private boolean isShowing; /** * 是否滿(mǎn)足show的條件 */ private boolean isCanShow; /** * 彈窗類(lèi)型 */ private WindowType mWindowType; /** * 彈窗名稱(chēng) */ 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; /** * 彈窗類(lèi)型 */ private WindowType windowType; /** * 是否滿(mǎn)足show的條件 */ private boolean isCanShow; /** * 彈窗名稱(chēng) */ 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類(lèi)去統(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); } } /** * 彈窗滿(mǎn)足展示條件 * * @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開(kāi)始顯示 */ 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類(lèi)有三個(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類(lèi)就可以了。具體使用方法參見(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 ListView與getView調(diào)用卡頓問(wèn)題解決辦法
這篇文章主要介紹了Android ListView與getView調(diào)用卡頓問(wèn)題解決辦法的相關(guān)資料,這里提供實(shí)例及解決辦法幫助大家解決這種問(wèn)題,需要的朋友可以參考下2017-08-08Android Fragment動(dòng)態(tài)創(chuàng)建詳解及示例代碼
這篇文章主要介紹了Android Fragment動(dòng)態(tài)創(chuàng)建詳解的相關(guān)資料,并附實(shí)例代碼及實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-11-11Android 第三方應(yīng)用接入微信平臺(tái)研究情況分享(二)
微信平臺(tái)開(kāi)放后倒是挺火的,許多第三方應(yīng)用都想試下,這里把我的整個(gè)研究情況給出來(lái),希望可以共同學(xué)習(xí),感興趣的朋友可以了解下2013-01-01android 調(diào)用JNI SO動(dòng)態(tài)庫(kù)的方法
android 調(diào)用JNI 分為靜態(tài)調(diào)用與動(dòng)態(tài)調(diào)用,接下來(lái)通過(guò)本文給大家介紹android 調(diào)用JNI SO動(dòng)態(tài)庫(kù)的方法,感興趣的朋友一起看看吧2021-11-11Android 基于RecyclerView實(shí)現(xiàn)的歌詞滾動(dòng)自定義控件
這篇文章主要介紹了Android 基于RecyclerView實(shí)現(xiàn)的歌詞滾動(dòng)自定義控件,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Android計(jì)步功能的實(shí)現(xiàn)代碼
本篇文章主要介紹了Android計(jì)步功能的實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03Android邊框裁切的正確姿勢(shì)實(shí)現(xiàn)示例
這篇文章主要為大家介紹了Android邊框裁切的正確姿勢(shì)實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Android編程中TextView寬度過(guò)大導(dǎo)致Drawable無(wú)法居中問(wèn)題解決方法
這篇文章主要介紹了Android編程中TextView寬度過(guò)大導(dǎo)致Drawable無(wú)法居中問(wèn)題解決方法,以實(shí)例形式較為詳細(xì)的分析了TextView設(shè)置及xml布局與調(diào)用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Android編程設(shè)計(jì)模式之模板方法模式詳解
這篇文章主要介紹了Android編程設(shè)計(jì)模式之模板方法模式,結(jié)合實(shí)例形式詳細(xì)分析了Android模板方法模式的概念、功能、使用場(chǎng)景、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2017-12-12