782
汽車大全
Android 經典筆記七 全局彈窗Dialog
目錄介紹
- 1.全局彈窗分析
- 2.全局彈窗必要條件
- 3.全局彈窗實現方式 3.1. 利用係統彈出dialog 3.2. 獲取WindowManager,直接添加view 3.3. 在服務裏,獲取棧頂的Activity,彈窗
- 4.Dialog實現全局Loading加載框 4.1. 自定義Loading類 4.2. 給自定義的Dialog添加自定義屬性 4.3. Loading布局 4.4. 開始使用
- 5.遇到的問題 5.1. 權限問題 5.2. Unable to add window
- 6.其他說明
0.本人寫的綜合案例
案例
說明及截圖
模塊:新聞,音樂,視頻,圖片,唐詩宋詞,快遞,天氣,記事本,閱讀器等等
接口:七牛,阿裏雲,天行,幹貨集中營,極速數據,追書神器等等
1.全局彈窗分析
開始認為dialog需要依附在Activity上,後經查詢可采取懸浮窗的模式,使其不必依附於Activity,可在任一頁麵彈出
2.全局彈窗必要條件
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);設置dialog的類型
清單文件配置:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
3.全局彈窗實現方式
- 第一個方法利用係統彈出dialog
在alter.show()語句前加入: alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 然後在AndroidManifest.xml中加入權限:android.permission.SYSTEM_ALERT_WINDOW
- 第二個方法是獲取WindowManager,直接添加view
wmParams = new WindowManager.LayoutParams(); //獲取的是WindowManagerImpl.CompatModeWrapper mWindowManager = (WindowManager)getApplication().getSystemService(getApplication().WINDOW_SERVICE); //設置window type wmParams.type = LayoutParams.TYPE_PHONE; //設置浮動窗口不可聚焦(實現操作除浮動窗口外的其他可見窗口的操作) wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE; //調整懸浮窗顯示的停靠位置為左側置頂 wmParams.gravity = Gravity.LEFT | Gravity.TOP; // 以屏幕左上角為原點,設置x、y初始值,相對於gravity wmParams.x = 0; wmParams.y = 0; //設置懸浮窗口長寬數據 wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT; wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; LayoutInflater inflater = LayoutInflater.from(getApplication()); //獲取浮動窗口視圖所在布局 mFloatLayout = (LinearLayout) inflater.inflate(R.layout.float_layout, null); //添加mFloatLayout mWindowManager.addView(mFloatLayout, wmParams);
-
在服務裏,獲取棧頂的Activity,彈窗
public static void showActivityDialog(final Activity activity){ if(AppUtils.isActivityLiving(activity)){ int appCount = BaseApplication.getInstance().getAppCount(); Log.e("全局彈窗","------"); //隻有當APP處於前台時才彈窗 if(appCount==1){ Log.e("全局彈窗","前台"); AlertDialog.Builder builder = new AlertDialog.Builder(activity); final AlertDialog alertDialog = builder.create(); alertDialog.setCancelable(false); View view = LayoutInflater.from(activity).inflate(R.layout.dialog_custom_view, null); alertDialog.setView(view); if(alertDialog.getWindow()!=null){ Window window = alertDialog.getWindow(); window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); window.setBackgroundDrawableResource(R.color.transparent); WindowManager.LayoutParams params = window.getAttributes(); //WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.width = WindowManager.LayoutParams.MATCH_PARENT; params.height = WindowManager.LayoutParams.MATCH_PARENT; params.gravity = Gravity.CENTER; window.setAttributes(params); //window.setGravity(Gravity.CENTER); //此處可以設置dialog顯示的位置 //window.setWindowAnimations(R.style.dialog_custom_view); //添加動畫 } //報錯:Unable to add window -- token null is not for an application //全局彈窗必須依附Activity,必須在Activity運行下才能彈窗,否則崩潰 //注意,小米,三星等手機需要手動打開權限才行 if (Build.VERSION.SDK_INT >= 23) { if(!Settings.canDrawOverlays(activity)) { ToastUtils.showToast(activity,"請打開投資界允許權限開關"); Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activity.startActivity(intent); return; } else { //Android6.0以上 if (!alertDialog.isShowing()) { alertDialog.show(); } } } else { //Android6.0以下,不用動態聲明權限 if (!alertDialog.isShowing()) { alertDialog.show(); } } alertDialog.setOnKeyListener(new DialogInterface.OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if(keyCode==KeyEvent.KEYCODE_BACK){ if(alertDialog.isShowing()){ alertDialog.dismiss(); } } return false; } }); AppUtils.setBackgroundAlpha(activity,0.5f); //Unable to add window android.view.ViewRootImpl$W@12b82d6 -- permission denied for this window type //alertDialog.show(); alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { AppUtils.setBackgroundAlpha(activity,1.0f); } }); } } }
4.Dialog實現全局Loading加載框
- 給自定義的Dialog添加自定義屬性
- Loading布局
- 開始使用
-
自定義Loading
public abstract class ViewLoading extends Dialog { public abstract void loadCancel(); public ViewLoading(Context context) { super(context, R.style.Loading); // 加載布局 setContentView(R.layout.dialog_toast_view); ImageView progressImageView = (ImageView) findViewById(R.id.iv_image); //創建旋轉動畫 Animation animation =new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animation.setDuration(2000); animation.setRepeatCount(10);//動畫的重複次數 animation.setFillAfter(true);//設置為true,動畫轉化結束後被應用 progressImageView.startAnimation(animation);//開始動畫 // 設置Dialog參數 Window window = getWindow(); if(window!=null){ WindowManager.LayoutParams params = window.getAttributes(); params.gravity = Gravity.CENTER; window.setAttributes(params); } } // 封裝Dialog消失的回調 @Override public void onBackPressed() { //回調 loadCancel(); //關閉Loading dismiss(); } }
-
給自定義的Dialog添加自定義屬性
<style name="Loading" parent="@android:style/Theme.Dialog"> <item name="android:windowFrame">@null</item> <item name="android:windowIsFloating">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> <!-- 設置背景色 透明--> <item name="android:background">@android:color/transparent</item> <item name="android:windowBackground">@android:color/transparent</item> <!-- 設置是否顯示背景 --> <item name="android:backgroundDimEnabled">true</item> <!-- 設置背景透明度 --> <item name="android:backgroundDimAmount">0.6</item> <!-- 設置點擊空白不消失 --> <item name="android:windowCloseOnTouchOutside">false</item> </style>
-
開始使用
// 添加Loading mLoading = new ViewLoading(this) { @Override public void loadCancel() { //loadCancle()是按返回鍵,Loading框關閉的回調,可以做取消加載請求的操作。 } }; // 顯示Loading mLoading.show(); // 關閉Loading mLoading.dismiss();
5.遇到的問題
- 權限問題
注意,由於有些手機(如小米)限製了懸浮窗口功能,默認不能顯示,需要進入係統設置->其他應用管理, 找到你的應用,進入應用詳情,啟用懸浮窗功能。開啟這個功能之後才能顯示。
//注意,小米,三星等手機需要手動打開權限才行 if (Build.VERSION.SDK_INT >= 23) { if(!Settings.canDrawOverlays(activity)) { ToastUtils.showToast(activity,"請打開投資界允許權限開關"); Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activity.startActivity(intent); return; } else { //Android6.0以上 if (!alertDialog.isShowing()) { alertDialog.show(); } } } else { //Android6.0以下,不用動態聲明權限 if (!alertDialog.isShowing()) { alertDialog.show(); } }
- Unable to add window
原因分析 該異常表示view沒有添加到窗口管理器,通常是我們dismiss對話框的時候,activity已經不存在了,建議不要在非UI線程操作對話框。 解決方案 [解決方案]:Dialog&AlertDialog,WindowManager不能正確使用時,經常會報出該異常,原因比較多,幾個常見的場景如下: 1.上一個頁麵沒有destroy的時候,之前的Activity已經接收到了廣播。如果此時之前的Activity進行UI層麵的操作處理,就會造成crash。UI層麵的刷新,一定要注意時機,建議使用set_result來代替廣播的形式進行刷新操作,避免使用廣播的方式,代碼不直觀且容易出錯。 2.Dialog在Actitivty退出後彈出。在Dialog調用show方法進行顯示時,必須要有一個Activity作為窗口的載體,如果Activity被銷毀,那麼導致Dialog的窗口載體找不到。建議在Dialog調用show方法之前先判斷Activity是否已經被銷毀。 3.Service&Application彈出對話框或WindowManager添加view時,沒有設置window type為TYPE_SYSTEM_ALERT。需要在調用dialog.show()方法前添加dialog.getWindow().SetType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)。 4.6.0的係統上, (非定製 rom 行為)若沒有給予懸浮窗權限, 會彈出該問題, 可以通過Settings.canDrawOverlays來判斷是否有該權限. 5.某些不穩定的MIUI係統bug引起的權限問題,係統把Toast也當成了係統級彈窗,android6.0的係統Dialog彈窗需要用戶手動授權,若果app沒有加入SYSTEM_ALERT_WINDOW權限就會報這個錯。需要加入給app加係統Dialog彈窗權限,並動態申請權限,不滿足第一條會出現沒權限閃退,不滿足第二條會出現沒有Toast的情況。 建議 1.不要在非UI線程中使用對話框創建,顯示和取消對話框; 2.盡量少用單獨線程,出發是真正的耗時操作采用線程,線程也不要直接用Java式的匿名線程,除非是那種單純的操作,操作完成不需要做其他事情的。 3.如果是在fragment中發起異步網絡的回調中進行dialog的操作,那麼在操作之前,需要判斷 isAdd( ),避免fragment被回收了但是還要求dialog去dismiss 4.在Activity onDestroy中對Dialog提前進行關閉
6.其他說明 - 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
- 領英:https://www.linkedin.com/in/chong-yang-049216146/
- 簡書:https://www.jianshu.com/u/b7b2c6ed9284
- csdn:https://my.csdn.net/m0_37700275
- 網易博客:https://yangchong211.blog.163.com/
- 新浪博客:https://blog.sina.com.cn/786041010yc
- github:https://github.com/yangchong211
- 喜馬拉雅聽書:https://www.ximalaya.com/zhubo/71989305/
- 脈脈:yc930211
- 360圖書館:https://www.360doc.com/myfiles.aspx
- 開源中國:https://my.oschina.net/zbj1618/blog
- 泡在網上的日子:https://www.jcodecraeer.com/member/content_list.php?channelid=1
- 郵箱:yangchong211@163.com
- 阿裏雲博客:https://yq.aliyun.com/users/article?spm=5176.100239.headeruserinfo.3.dT4bcV
最後更新:2017-10-12 10:03:07