问题:
在5.0以上系统中被用户关闭消息通知后,系统的Toast也一起无法显示了。
原因:Toast通过NotificationManagerService 维护一个toast队列,然后通知调用 WindowManager 添加view,那么当用户关闭通知权限后,NotificationManagerService受到影响,导致Toast无法显示。
可以考虑的解决方案:
1、仿照系统的Toast然后用Handler+Queue自己的消息队列来维护,让其不受NotificationManagerService影响,显示还是使用WindowManager来addview
通过WindowManager addview 来实现,虽然Type选的TYPE_TOAST,在MIUI 8上,要是没有开权限,还是显示不了
2、通过Dialog、PopupWindow来编写一个自定义通知。
跟原来toast的初衷不一致,toast是不会获取焦点的,但是dialog和popupwindow会获取焦点,会盖住下面的内容。
3、通过直接去当前页面最外层content布局来添加View。
需要传activity,才可以获取最外层的content;而且上面覆盖dialogFragment时,通过activity的content添加的view会被dialogfragment覆盖。
本文采用的方法
本文采用了第三种方法,获取当前最外层ViewGroup,通过ViewGroup AddView来显示Toast, 并解决了上述提到的问题。
1、使用方法 需要先初始化:
SHToast.init(new SHToast.ToastListener() {
@Override
public Activity getCurrentActivity() {
return ActivityLifecycleCallbacks.getInstance().getResumeActivity();
}
});
然后可以像Toast那样使用,
SHToast.makeText(context, "默认Toast样式",
SHToast.LENGTH_SHORT).show();
或者 直接调用
SHToast.toast("测试测试");
如果没有初始化,可以调用
SHToast.toast(activity, "测试测试");
源码见:SHToast
2、实现原理
- 首先,消息队列还是采用Handler+Queue来维护.
- 传入activity,需要在初始化时调用:
SHToast.init(new SHToast.ToastListener() {
@Override
public Activity getCurrentActivity() {
return ActivityLifecycleCallbacks.getInstance().getResumeActivity();
}
});我在ActivityLifecycleCallbacks里维护了一个activity列表,可以获取当前resume的activity。
ActivityLifecycleCallbacks实现了 Application.ActivityLifecycleCallbacks
这样每次toast 都会调用目前resume的activity,如果没有activity resume,就不显示toast了。
- 解决dialogFragment覆盖的问题,关键过程就是如何获取最外层viewgroup。
- 首先判断是否有android.app.Fragment appFragment,如果有,循环直到获取最外层ViewGroup,然后获取content元素
container = rootView.findViewById(android.R.id.content);
如果没有获取到,就到下一步; - 然后判断是否有可见的android.support.v4.app.Fragment,同理,如果有,循环直到获取最外层ViewGroup,然后获取content元素
container = rootView.findViewById(android.R.id.content);
如果没有获取到,就到下一步; - 获取activity的最外层viewgroup,
container = (ViewGroup) activity.findViewById(android.R.id.content);
关键代码如下:
- 首先判断是否有android.app.Fragment appFragment,如果有,循环直到获取最外层ViewGroup,然后获取content元素
private static boolean initToastView(Activity activity, ToastMsg msg) {
if (null == activity) {
return false;
}
FragmentActivity fragmentActivity;
android.support.v4.app.Fragment visibleFragment = null;
android.app.Fragment appFragment = null;
Bundle bundle = new Bundle();
bundle.putInt("key", 0);
try {
appFragment = activity.getFragmentManager().getFragment(bundle, "key");
} catch (Exception e) {
}
if (null != appFragment) {
View rootView = appFragment.getView();
ViewParent viewParent = null;
while (null != rootView.getParent()) {
viewParent = rootView.getParent();
if (null != viewParent && viewParent instanceof ViewGroup) {
rootView = (ViewGroup) viewParent;
} else {
break;
}
}
container = (ViewGroup) rootView.findViewById(android.R.id.content);
}
if (null == container) {
if (activity instanceof FragmentActivity) {
fragmentActivity = (FragmentActivity) activity;
List<android.support.v4.app.Fragment> fragments = fragmentActivity.getSupportFragmentManager().getFragments();
if (null != fragments) {
for (android.support.v4.app.Fragment fragment : fragments) {
if (fragment != null && fragment.getUserVisibleHint()) {
visibleFragment = fragment;
break;
}
}
}
}
if (null != visibleFragment) {
View rootView = visibleFragment.getView();
container = (ViewGroup) rootView.findViewById(android.R.id.content);
}
}
if (null == container) {
container = (ViewGroup) activity.findViewById(android.R.id.content);
}
if (null == container) {
return false;
}
……
return true;
}