android 系统全局弹框_Android 经典笔记七 全局弹窗Dialog

本文详细介绍了Android全局弹窗的实现,包括利用系统Dialog、WindowManager添加View以及在服务中获取栈顶Activity弹窗的方法。同时,讨论了权限问题,特别是Android 6.0以上版本的SYSTEM_ALERT_WINDOW权限,以及遇到的'Unable to add window'异常的解决方案。文中还提供了一个自定义Loading Dialog的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录介绍

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.其他说明

好消息

博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!

如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

1.全局弹窗分析

开始认为dialog需要依附在Activity上,后经查询可采取悬浮窗的模式,使其不必依附于Activity,可在任一页面弹出

2.全局弹窗必要条件

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);设置dialog的类型

清单文件配置:

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添加自定义属性

@null

true

true

true

@android:color/transparent

@android:color/transparent

true

0.6

false

开始使用

// 添加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.其他说明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值