Android 经典笔记七 全局弹窗Dialog
2023-09-27 14:27:25 时间
3.2. 获取WindowManager,直接添加view
3.3. 在服务里,获取栈顶的Activity,弹窗
4.1. 自定义Loading类
4.2. 给自定义的Dialog添加自定义属性
4.3. Loading布局
4.4. 开始使用
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.dT4bcVAndroid Jetpack Compose——一个简单的笔记APP 此项目功能较为简单,基本就是使用Room数据库实现CRUD,但是此项目实现了一个干净的架构,项目使用MVVM架构进行设计,每一个模块的职责划分清晰,功能明确,没有冗余的代码。其中涉及了Hilt依赖注入,对于数据库的的操作,使用接口实现类进行获取,然后将实现类的CRUD操作封装在一个数据类中,最后通过Hilt自动注入依赖,供外部调用。
相关文章
- Android 华为推送服务集成
- android-配置虚拟机Virtual device
- 《深入理解Android 5 源代码》——导读
- 《Android应用开发入门经典(第3版)》——第1.3节理解Java代码和XML布局
- 《Android应用开发入门经典(第3版)》——第1.5节定制应用
- 《Android应用开发入门经典(第3版)》——第1.6节小结
- 《Android应用开发入门经典(第3版)》——第1.9节练习
- 《Android应用开发入门经典(第3版)》——第6.2节使用输入控件
- 《Android应用开发入门经典(第3版)》——第6.4节ProgressBar和SeekBar
- 《Android应用开发入门经典(第3版)》——第6.5节ImageView
- 《Android应用开发入门经典(第3版)》——第6.6节小结
- 【Android进度条】三种方式实现自定义圆形进度条ProgressBar
- android 常用时间格式转换代码
- Android RingtoneManager铃声管理
- Android 高清加载巨图方案 拒绝压缩图片
- 超级简单的Android Studio jni 实现(无需命令行)
- android圆角View实现及不同版本这间的兼容
- android UI进阶之弹窗的使用(2)--实现通讯录的弹窗效果
- Android数据库升级实例
- Android在网络上分析获取图片(支持bmp格式)
- Facebook:React向Windows、Tizen、iOS和Android开放