zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

Android 经典笔记七 全局弹窗Dialog

Android经典笔记 全局 弹窗 Dialog
2023-09-27 14:27:25 时间

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
Android Jetpack Compose——一个简单的笔记APP 此项目功能较为简单,基本就是使用Room数据库实现CRUD,但是此项目实现了一个干净的架构,项目使用MVVM架构进行设计,每一个模块的职责划分清晰,功能明确,没有冗余的代码。其中涉及了Hilt依赖注入,对于数据库的的操作,使用接口实现类进行获取,然后将实现类的CRUD操作封装在一个数据类中,最后通过Hilt自动注入依赖,供外部调用。