系统级对话框

目录介绍

  • 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.全局弹窗必要条件

  
  
  1. dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);设置dialog的类型
  2. 清单文件配置:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

3.全局弹窗实现方式

  • 第一个方法利用系统弹出dialog
        
        
    1. alter.show()语句前加入:
    2. alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    3. 然后在AndroidManifest.xml中加入权限:android.permission.SYSTEM_ALERT_WINDOW
  • 第二个方法是获取WindowManager,直接添加view
        
        
    1. wmParams = new WindowManager.LayoutParams();
    2. //获取的是WindowManagerImpl.CompatModeWrapper
    3. mWindowManager = (WindowManager)getApplication().getSystemService(getApplication().WINDOW_SERVICE);
    4. //设置window type
    5. wmParams.type = LayoutParams.TYPE_PHONE;
    6. //设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
    7. wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;
    8. //调整悬浮窗显示的停靠位置为左侧置顶
    9. wmParams.gravity = Gravity.LEFT | Gravity.TOP;
    10. // 以屏幕左上角为原点,设置x、y初始值,相对于gravity
    11. wmParams.x = 0;
    12. wmParams.y = 0;
    13. //设置悬浮窗口长宽数据
    14. wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
    15. wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    16. LayoutInflater inflater = LayoutInflater.from(getApplication());
    17. //获取浮动窗口视图所在布局
    18. mFloatLayout = (LinearLayout) inflater.inflate(R.layout.float_layout, null);
    19. //添加mFloatLayout
    20. mWindowManager.addView(mFloatLayout, wmParams);
  • 在服务里,获取栈顶的Activity,弹窗

        
        
    1. public static void showActivityDialog(final Activity activity){
    2. if(AppUtils.isActivityLiving(activity)){
    3. int appCount = BaseApplication.getInstance().getAppCount();
    4. Log.e("全局弹窗","------");
    5. //只有当APP处于前台时才弹窗
    6. if(appCount==1){
    7. Log.e("全局弹窗","前台");
    8. AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    9. final AlertDialog alertDialog = builder.create();
    10. alertDialog.setCancelable(false);
    11. View view = LayoutInflater.from(activity).inflate(R.layout.dialog_custom_view, null);
    12. alertDialog.setView(view);
    13. if(alertDialog.getWindow()!=null){
    14. Window window = alertDialog.getWindow();
    15. window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    16. window.setBackgroundDrawableResource(R.color.transparent);
    17. WindowManager.LayoutParams params = window.getAttributes();
    18. //WindowManager.LayoutParams params = new WindowManager.LayoutParams();
    19. params.width = WindowManager.LayoutParams.MATCH_PARENT;
    20. params.height = WindowManager.LayoutParams.MATCH_PARENT;
    21. params.gravity = Gravity.CENTER;
    22. window.setAttributes(params);
    23. //window.setGravity(Gravity.CENTER); //此处可以设置dialog显示的位置
    24. //window.setWindowAnimations(R.style.dialog_custom_view); //添加动画
    25. }
    26. //报错:Unable to add window -- token null is not for an application
    27. //全局弹窗必须依附Activity,必须在Activity运行下才能弹窗,否则崩溃
    28. //注意,小米,三星等手机需要手动打开权限才行
    29. if (Build.VERSION.SDK_INT >= 23) {
    30. if(!Settings.canDrawOverlays(activity)) {
    31. ToastUtils.showToast(activity,"请打开投资界允许权限开关");
    32. Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    33. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    34. activity.startActivity(intent);
    35. return;
    36. } else {
    37. //Android6.0以上
    38. if (!alertDialog.isShowing()) {
    39. alertDialog.show();
    40. }
    41. }
    42. } else {
    43. //Android6.0以下,不用动态声明权限
    44. if (!alertDialog.isShowing()) {
    45. alertDialog.show();
    46. }
    47. }
    48. alertDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
    49. @Override
    50. public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
    51. if(keyCode==KeyEvent.KEYCODE_BACK){
    52. if(alertDialog.isShowing()){
    53. alertDialog.dismiss();
    54. }
    55. }
    56. return false;
    57. }
    58. });
    59. AppUtils.setBackgroundAlpha(activity,0.5f);
    60. //Unable to add window android.view.ViewRootImpl$W@12b82d6 -- permission denied for this window type
    61. //alertDialog.show();
    62. alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
    63. @Override
    64. public void onDismiss(DialogInterface dialog) {
    65. AppUtils.setBackgroundAlpha(activity,1.0f);
    66. }
    67. });
    68. }
    69. }
    70. }

4.Dialog实现全局Loading加载框

  • 给自定义的Dialog添加自定义属性
  • Loading布局
  • 开始使用
  • 自定义Loading

        
        
    1. public abstract class ViewLoading extends Dialog {
    2. public abstract void loadCancel();
    3. public ViewLoading(Context context) {
    4. super(context, R.style.Loading);
    5. // 加载布局
    6. setContentView(R.layout.dialog_toast_view);
    7. ImageView progressImageView = (ImageView) findViewById(R.id.iv_image);
    8. //创建旋转动画
    9. Animation animation =new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
    10. animation.setDuration(2000);
    11. animation.setRepeatCount(10);//动画的重复次数
    12. animation.setFillAfter(true);//设置为true,动画转化结束后被应用
    13. progressImageView.startAnimation(animation);//开始动画
    14. // 设置Dialog参数
    15. Window window = getWindow();
    16. if(window!=null){
    17. WindowManager.LayoutParams params = window.getAttributes();
    18. params.gravity = Gravity.CENTER;
    19. window.setAttributes(params);
    20. }
    21. }
    22. // 封装Dialog消失的回调
    23. @Override
    24. public void onBackPressed() {
    25. //回调
    26. loadCancel();
    27. //关闭Loading
    28. dismiss();
    29. }
    30. }
  • 给自定义的Dialog添加自定义属性
        
        
    1. <style name="Loading" parent="@android:style/Theme.Dialog">
    2. <item name="android:windowFrame">@null</item>
    3. <item name="android:windowIsFloating">true</item>
    4. <item name="android:windowIsTranslucent">true</item>
    5. <item name="android:windowNoTitle">true</item>
    6. <!-- 设置背景色 透明-->
    7. <item name="android:background">@android:color/transparent</item>
    8. <item name="android:windowBackground">@android:color/transparent</item>
    9. <!-- 设置是否显示背景 -->
    10. <item name="android:backgroundDimEnabled">true</item>
    11. <!-- 设置背景透明度 -->
    12. <item name="android:backgroundDimAmount">0.6</item>
    13. <!-- 设置点击空白不消失 -->
    14. <item name="android:windowCloseOnTouchOutside">false</item>
    15. </style>
  • 开始使用
        
        
    1. // 添加Loading
    2. mLoading = new ViewLoading(this) {
    3. @Override
    4. public void loadCancel() {
    5. //loadCancle()是按返回键,Loading框关闭的回调,可以做取消加载请求的操作。
    6. }
    7. };
    8. // 显示Loading
    9. mLoading.show();
    10. // 关闭Loading
    11. mLoading.dismiss();

5.遇到的问题

  • 权限问题 注意,由于有些手机(如小米)限制了悬浮窗口功能,默认不能显示,需要进入系统设置->其他应用管理, 找到你的应用,进入应用详情,启用悬浮窗功能。开启这个功能之后才能显示。
        
        
    1. //注意,小米,三星等手机需要手动打开权限才行
    2. if (Build.VERSION.SDK_INT >= 23) {
    3. if(!Settings.canDrawOverlays(activity)) {
    4. ToastUtils.showToast(activity,"请打开投资界允许权限开关");
    5. Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    6. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    7. activity.startActivity(intent);
    8. return;
    9. } else {
    10. //Android6.0以上
    11. if (!alertDialog.isShowing()) {
    12. alertDialog.show();
    13. }
    14. }
    15. } else {
    16. //Android6.0以下,不用动态声明权限
    17. if (!alertDialog.isShowing()) {
    18. alertDialog.show();
    19. }
    20. }
  • Unable to add window
        
        
    1. 原因分析
    2. 该异常表示view没有添加到窗口管理器,通常是我们dismiss对话框的时候,activity已经不存在了,建议不要在非UI线程操作对话框。
    3. 解决方案
    4. [解决方案]:Dialog&AlertDialogWindowManager不能正确使用时,经常会报出该异常,原因比较多,几个常见的场景如下:
    5. 1.上一个页面没有destroy的时候,之前的Activity已经接收到了广播。如果此时之前的Activity进行UI层面的操作处理,就会造成crashUI层面的刷新,一定要注意时机,建议使用set_result来代替广播的形式进行刷新操作,避免使用广播的方式,代码不直观且容易出错。
    6. 2.DialogActitivty退出后弹出。在Dialog调用show方法进行显示时,必须要有一个Activity作为窗口的载体,如果Activity被销毁,那么导致Dialog的窗口载体找不到。建议在Dialog调用show方法之前先判断Activity是否已经被销毁。
    7. 3.Service&Application弹出对话框或WindowManager添加view时,没有设置window typeTYPE_SYSTEM_ALERT。需要在调用dialog.show()方法前添加dialog.getWindow().SetType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)。
    8. 4.6.0的系统上, (非定制 rom 行为)若没有给予悬浮窗权限, 会弹出该问题, 可以通过Settings.canDrawOverlays来判断是否有该权限.
    9. 5.某些不稳定的MIUI系统bug引起的权限问题,系统把Toast也当成了系统级弹窗,android6.0的系统Dialog弹窗需要用户手动授权,若果app没有加入SYSTEM_ALERT_WINDOW权限就会报这个错。需要加入给app加系统Dialog弹窗权限,并动态申请权限,不满足第一条会出现没权限闪退,不满足第二条会出现没有Toast的情况。
    10. 建议
    11. 1.不要在非UI线程中使用对话框创建,显示和取消对话框;
    12. 2.尽量少用单独线程,出发是真正的耗时操作采用线程,线程也不要直接用Java式的匿名线程,除非是那种单纯的操作,操作完成不需要做其他事情的。
    13. 3.如果是在fragment中发起异步网络的回调中进行dialog的操作,那么在操作之前,需要判断 isAdd( ),避免fragment被回收了但是还要求dialogdismiss
    14. 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






开始我想获取当前活动的activity实例,然后在依附其弹出dialog,不过没有找到获取的方法,只能获取到classname,后来找到了3个方法:
第一个方法利用系统弹出dialog,很牛x,不过样子有点丑
第二个方法是获取 WindowManager,直接添加view
第三个方法是找一个透明的activity当背景。
第一种其实可以通过setview去改变外观,第二种和第一种最终是利用同一个原理, 第三种实在是懒得找了,有时间的时候再补上吧。。。
--------------------------------------------------

先说具体做法,原因在其后给出:

写好Alter功能块后,在alter.show()语句前加入:

 

[java]  view plain copy
  1. alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  

 

 

注:alter为AlertDialog类型对象

然后在AndroidManifest.xml中加入权限:

 

 

[java]  view plain copy
  1. "android.permission.SYSTEM_ALERT_WINDOW" 

 


下面进行简单的解释:

如果只在Service中写入常在Activity中使用的创建Alter的代码,运行时是会发生错误的,因为Alter的显示需要依附于一个确定的Activity类。而以上做法就是声明我们要弹出的这个提示框是一个系统的提示框,即全局性质的提示框,所以只要手机处于开机状态,无论它现在处于何种界面之下,只要调用alter.show(),就会弹出提示框来。



转载自  http://blog.csdn.net/twoicewoo/article/details/7448584

------------------------------------------------------------------

  1.  d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); //系统中关机对话框就是这个属性  
  2.                 //d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  //窗口可以获得焦点,响应操作  
  3.                 //d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY);  //窗口不可以获得焦点,点击时响应窗口后面的界面点击事件


-------------------------------------------------------------------

final WindowManager wm = (WindowManager) context.getSystemService("window");

                            WindowManager.LayoutParams para = new WindowManager.LayoutParams();

                            para.height = -1;

                            para.width = -1;

                            para.format = 1;


                            para.flags = LayoutParams.FLAG_FULLSCREEN | LayoutParams.FLAG_LAYOUT_IN_SCREEN;


                            para.type = LayoutParams.TYPE_SYSTEM_ALERT;

                            final View mView = LayoutInflater.from(context).inflate(

                                    R.layout.xxxxxxxxxx, null);

                            wm.addView(mView, para);

                            mView.findViewById(R.id.button).setOnClickListener(new OnClickListener() {

                                

                                @Override

                                public void onClick(View v) {

                                    wm.removeView(mView);

                                }

                            });

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值