閱讀782 返回首頁    go 汽車大全


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

  上一篇:go  移動硬盤安裝ubuntu , 用EasyBCD設置啟動引導盤
  下一篇:go  2017杭州·雲棲大會全紀實