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/
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 网易博客:http://yangchong211.blog.163.com/
  • 新浪博客:http://blog.sina.com.cn/786041010yc
  • github:https://github.com/yangchong211
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 脉脉:yc930211
  • 360图书馆:http://www.360doc.com/myfiles.aspx
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100239.headeruserinfo.3.dT4bcV
时间: 2024-08-15 13:34:36

Android 经典笔记七 全局弹窗Dialog的相关文章

Android 经典笔记之八:网络请求数据基础介绍

关于网络请求数据总结 目录介绍 1.Http请求与响应 1.1 Http请求包的结构 1.2 HTTP响应包结构 2.Http请求方式 3.Get和Post的比较 3.1 get请求 3.2 post请求 3.3 其他区别 3.4 网络心声 4.Http响应方式 5.同步和异步 6.Http缓存机制讲解 6.1 request请求字段含义 6.2 response响应字段含义 6.3 缓存机制逻辑图 0.本人写的综合案例 案例 说明及截图 模块:新闻,音乐,视频,图片,唐诗宋词,快递,天气,记事

Android 经典笔记之八:CountDownTimer解读

目录介绍 CountDownTimer(倒计时计数器) 1.1 介绍 1.2 参数 1.3 公共方法 1.4 使用方法 1.5 源码分析 1.6 synchronized 关键字 0.本人写的综合案例案例说明及截图 模块:新闻,音乐,视频,图片,唐诗宋词,快递,天气,记事本,阅读器等等 接口:七牛,阿里云,天行,干货集中营,极速数据,追书神器等等 1.1介绍: 定时执行在一段时间后停止的倒计时,在倒计时执行过程中会在固定间隔时间得到通知.1.2参数: millisInFuture 从开始调用st

Android 经典笔记之五:DownloadManager下载管理器介绍

DownloadManager下载管理器介绍目录介绍: 0.简单介绍 1.所需权限 2.获取对象,开始下载 3.取消下载 4.Request类的介绍 4.5.指定下载的类型 4.6.定制Notification样式 4.7.设置下载文件类型 4.8.添加请求下载的网络链接的http头,比如User-Agent,gzip压缩等 5.Query 类 6.不足之处 7.代码案例[简易] 0.简单介绍 关于DownloadManager简单介绍 DownloadManager是android2.3以后,

求救。。。-android中,如何判断系统dialog,有没有显示?

问题描述 android中,如何判断系统dialog,有没有显示? 描述:现在的情况是,我有一个activity,在这个activity中,会启动系统的launcher选择框(就是系统中有多个launcher,但是没设置默认launcher.此时,我们点home键,弹出的那个界面),当我选择"总是"按钮时,会弹出一个对话框(这不是一个activity).我要怎么知道,这个框已经弹出来了?!或者,我要怎么确定,用户点击的是"总是"按钮?描述的可能有点乱,求看懂的朋友,

Android Stuido怎么设置全局横屏

问题描述 Android Stuido怎么设置全局横屏 大家好,我又来了 需求需要整个项目都是横屏,单个设置横屏觉得太麻烦 有没有朋友知道怎么设置全局都横屏 解决方案 Android学习笔记--Android中全局设置禁止横屏android设置横屏Android横屏设置

android开发-Android 里把Activity伪装成Dialog后此Activity不能横竖屏切换?

问题描述 Android 里把Activity伪装成Dialog后此Activity不能横竖屏切换? Android 里把Activity伪装成Dialog后此Activity就不能横竖屏切换了?也不能回调onConfigurationChanged. 还有什么办法能使伪装的dialog 监听到手机横竖屏的变化么 解决方案 就用Activity,不可以吗~实现什么效果~ 解决方案二: 就用Activity,不可以吗~实现什么效果~ 解决方案三: 伪装成....还是Acticvitiy. 解决方案

Android UI设计之AlertDialog弹窗控件_Android

有关android的弹窗界面相信大家见过不少了,手机上很多应用软件都涉及到弹窗控件,比如典型的每次删除一个图片或者卸载一个等都会弹出一个窗口询问是否删除/卸载等,还有我们系统的设置时间/日期等,都用到了这样的控件,下面我将通过代码来总结下常用的几个弹窗控件 activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http:/

Android开发笔记之Android中数据的存储方式(二)_Android

我们在实际开发中,有的时候需要储存或者备份比较复杂的数据.这些数据的特点是,内容多.结构大,比如短信备份等.我们知道SharedPreferences和Files(文本文件)储存这种数据会非常的没有效率.如果学过JavaWeb的朋友,首先可能想到的是数据库.当然了数据库是一个方案,那么是否还有其他的解决方案呢?今天我们在讲下Android开发笔记之Android中数据的存储方式(一) 提到的除了SharedPreferences和Files(文本文件)以外的其他几种数据储存方式:xml文件.SQ

Android开发笔记之探秘WebView_Android

概述:            一个显示网页的视图.这个类是你可以滚动自己的Web浏览器或在你的Activity中简单地显示一些在线内容的基础.它使用了WebKit渲染引擎来显示网页,包括向前和向后导航的方法(通过历史记录),放大和缩小,执行文本搜索等.          需要注意的是:为了让你的应用能够使用WebView访问互联网和加载网页,你必须添加Internet的权限在Android Manifest文件中: <uses-permission android:name="androi