当应用程序离线时,我在应用程序底部添加了一个小TextView
。所以我有BroadcastReceiver
来监控网络连接的变化,在onReceive
中,我会显示横幅。这是横幅类,它在现有视图的顶部添加TextView
:
public static void show() {
if (!isShowing && !isAppBackgrounded()) {
MyApplication app = MyApplication.getInstance();
WindowManager windowManager = (WindowManager) app.getSystemService(Context.WINDOW_SERVICE);
Resources res = app.getResources();
TextView offlineTv = app.getOfflineTv();
if (offlineTv.getWindowToken() != null) {
return;
}
offlineTv.setText("Offline");
offlineTv.setTextColor(ContextCompat.getColor(app, R.color.yellow));
offlineTv.setGravity(Gravity.CENTER);
offlineTv.setBackgroundColor(ContextCompat.getColor(app, R.color.dark_grey));
offlineTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, app.getResources().getInteger(R.integer.offline_banner_text_size));
WindowManager.LayoutParams params = createLayoutParams(WindowManager.LayoutParams.TYPE_TOAST, null);
windowManager.addView(offlineTv, params);
isShowing = true;
}
}
以下是createLayoutParams
方法
private static WindowManager.LayoutParams createLayoutParams(int type, @Nullable IBinder windowToken) {
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.format = PixelFormat.TRANSLUCENT;
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
layoutParams.height = 25;
layoutParams.gravity = GravityCompat.getAbsoluteGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, ViewCompat.LAYOUT_DIRECTION_LTR);
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.type = type;
layoutParams.token = windowToken;
layoutParams.windowAnimations = android.R.style.Animation_Toast;
return layoutParams;
}
此代码适用于除7.1.1设备之外的所有设备。在7.1.1设备中,TextView
显示一段时间然后消失。在7.1.1设备上只有一个空白空格而不是TextView
。知道为什么会这样吗?
TextView offlineTv = null;
/** Get the TextView to show the offline message */
public TextView getOfflineTv() {
if (offlineTv == null) {
offlineTv = new TextView(this);
}
return offlineTv;
}
/** Clear the offline TextView once we are done showing it */
public void clearOfflineTv() {
if (offlineTv != null) {
offlineTv = null;
}
}
这是我的BroadcastReceiver,我在其中显示/隐藏它:
public class DSConnectionChangeReceiver extends BroadcastReceiver {
/**
* Connection-changed callback
* @param context Context
* @param intent Intent
*/
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
boolean connected = false;
boolean isCellularData = false;
if (activeNetworkInfo != null) {
connected = activeNetworkInfo.isAvailable() && activeNetworkInfo.isConnected();
int type = activeNetworkInfo.getType();
isCellularData = (type == ConnectivityManager.TYPE_MOBILE) || (type == ConnectivityManager.TYPE_MOBILE_DUN);
}
if (connected) {
if (OfflineBanner.isShowing()) {
OfflineBanner.dismiss();
}
} else {
OfflineBanner.show();
}
}
}
答案 0 :(得分:2)
问题是由您添加android.R.style.Animation_Toast
windowAnimation引起的。当动画在实际的Toast上结束时,整个吐司将消失。在这种情况下,您的视图位于层次结构中,因此它不会消失,而是变为空白。
您应该做的是让layoutParams.windowAnimations
离开参数,然后创建并附加视图,并将可见性设置为View.GONE
,然后手动将视图设置到屏幕上
使用以下实用程序可以手动设置视图动画:
Animation animIn = AnimationUtils.makeInAnimation(context, true);
textView.setAnimation(animIn);
textView.setVisibility(View.VISIBLE);
textView.animate();
Snackbar Alternative:
public final class ConnectionBar {
private static boolean mIsConnected = true; //static to preserve state
private static ConnectionReceiver mReceiver; //static to detect leaks
private static SnackBar mSnack;
private ConnectionBar() { /* required */ )
public static void prepare(Context ctx) {
if (mReceiver != null) {
Log.e(TAG, "WARNING previous ConnectionBar was leaked");
}
mReceiver = new ConnectionReceiver();
ctx.registerBroadcastReceiver(mReceiver);
if (!mIsConnected) { //static so will remember from last screen
showBar(ctx);
}
}
private static void showBar(Context ctx) {
if (mSnack == null) {
mSnack = Snackbar.make(view, message, SnackBar.LENGTH_INDEFINITE);
mSnack.show();
}
}
public static void release(Context ctx) {
if (mReceiver != null) {
ctx.unregisterBroadcastReceiver(mReceiver);
mReceiver = null;
}
if (mSnack != null) {
mSnack.dismiss();
}
}
private static class ConnectionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
boolean isCellularData = false; //migrate this how you want
if (activeNetworkInfo != null) {
ConnectionBar.mIsConnected = activeNetworkInfo.isAvailable() && activeNetworkInfo.isConnected();
int type = activeNetworkInfo.getType();
isCellularData = (type == ConnectivityManager.TYPE_MOBILE) || (type == ConnectivityManager.TYPE_MOBILE_DUN);
}
}
if (connected && ConnectionBar.mSnack != null) {
ConnectionBar.mSnack.dismiss(); //check this, might need to wrap in runOnUiThread
} else {
ConnectionBar.showBar(context);
}
}
}
然后在你的活动中:
public void onResume() {
ConnectionBar.prepare(this); //takes care of setting br too
}
public void onPause() {
ConnectionBar.release(this);
}
答案 1 :(得分:2)
如果你想在WindowManager中查看仍然不仅仅是 BroadcastReciever生命周期,你需要在类扩展服务中做到这一点。看看this tutorial
我认为Lifecycle仍然存在问题。您如何使用它以及系统如何处理它。 如果你想强迫系统不要杀死你的服务(而不是删除WindowManager),你有3个选择。
使用proper flag返回OnStartCommand。
返回Service.START_REDELIVER_INTENT;
添加前景通知
startForeground(123,NotificationFunction());
答案 2 :(得分:1)
这是Android 7.1以来的一种行为,旨在阻止应用程序使用Toast视图无限期地覆盖其他应用程序。每当您使用TYPE_TOAST
视图时,系统将为您的视图显示最多3.5秒(即LONG Toast的视图)(并且还将视图动画更改为内部Toast样式),之后您的Toast视图将被隐藏, EXCEPT 您的应用是当前关注的应用。
为避免应用程序崩溃,您的视图仍保留在视图层次结构中。换句话说,在系统隐藏后,您仍然可以在其上调用removeView
,而不会导致非法状态异常。
(参考:请参阅Android源的提交消息: https://github.com/aosp-mirror/platform_frameworks_base/commit/aa07653d2eea38a7a5bda5944c8a353586916ae9)
要在Android 7.1或更高版本上显示其他应用的视图,您可能需要请求SYSTEM_ALERT_WINDOW
权限,提示用户获取Draw Over Apps权限,并使用其他视图类型,例如{{1 }}