如何使我的全屏覆盖在方向更改后保持全屏?
How do I make my full-screen overlay remain full-screen after orientation changes?
我正在制作一个应用程序,可以创建在屏幕上四处走动的小精灵动画。
我有一个主 activity,带有一个按钮 "start service"。这将启动一项服务,该服务(在 onCreate()
中)创建一个全屏视图并将其附加到根 window 管理器。
这部分工作完美。它会填满屏幕,您可以离开应用程序,动画仍然会在所有内容上可见。
旋转设备时出现问题。
精灵已经移动到屏幕中间,但这是一个无关的问题;这里重要的是黑色边界框——这个框显示了我可以绘制的canvas,它需要填满整个屏幕
视图仍然是纵向模式,尽管所有其他布局似乎都已正确更新。
部分问题来自于我指定视图尺寸的方式。我使用标志来指定 "full screen",但这 而不是 设置视图的 width
和 height
以匹配屏幕尺寸。因此我不得不在启动时手动设置它们。
我不知道如何在创建视图后更新视图的宽度和高度,但我觉得我需要这样做,因为视图的尺寸决定了 canvas.
我的服务像这样创建视图:
public class WalkerService extends Service {
private WalkerView view;
@Override
public void onCreate() {
super.onCreate();
final WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);
final DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
view = new WalkerView(this); // extends SurfaceView
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, // TYPE_SYSTEM_ALERT is denied in apiLevel >=19
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT
);
view.setFitsSystemWindows(false); // allow us to draw over status bar, navigation bar
// here I _manually_ set the dimensions, because otherwise it defaults to a square (something like 1024x1024)
params.width = metrics.widthPixels;
params.height = metrics.heightPixels;
wm.addView(view, params);
}
}
我试过监听方向变化,并在发生这种情况时旋转视图:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
view.setRotation(
newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
? 90.0f
: 0.0f
);
}
但是 setRotation()
似乎并没有影响 width
和 height
我的观点,它们也没有改变 canvas 呈现的角度。
我是否从根本上误解了如何创建全屏覆盖?无论方向如何(甚至当我的应用程序不是活动应用程序时),我如何保持在整个屏幕上绘制的能力?
可能与我将我的视图附加到 Window 服务的 window 管理器这一事实有关 — 也许这意味着我的视图有一个奇怪的父视图(我可以想象根视图可能会不受方向的影响,或者没有明确的边界供儿童伸展以填充)。
完整的源代码是 public on GitHub 以防我在这里遗漏了任何有用的信息。
WalkerView.java
可能特别相关,所以 here it is.
我认为您的情况可以通过其他方法解决。
- 创建全屏自定义视图(您可以在自定义视图的构造函数中设置适当的标志)
- 覆盖自定义视图中的
onSizeChanged()
。这将为您提供 Canvas 高度和宽度。这是可选的。如果您想用半透明颜色填充整个自定义视图,则没有必要。
- 使用上面的宽度和高度,或者简单地在自定义视图的
onDraw()
中使用 canvas.drawColor()
。
这将确保无论何时重新创建视图,它都会重新绘制以填满整个屏幕(canvas)。
我被上一个问题的答案误导了 "How to create always-top fullscreen overlay activity in Android"。也许它适用于较旧的 API 级别?我正在使用 API 级别 24。
该特定答案推荐:
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT
);
这个有问题。 WindowManager.LayoutParams
存在的构造函数如下:
因此 WindowManager.LayoutParams.FLAG_FULLSCREEN
标志被用作 int w
和 int h
的显式值。这不行!
我发现正确的全屏叠加结构是这样的:
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, // TYPE_SYSTEM_ALERT is denied in apiLevel >=19
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_FULLSCREEN,
PixelFormat.TRANSLUCENT
);
这意味着我们不再显式指定宽度和高度。布局完全依赖于我们的标志。
是的,WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
仍然是一个必需的标志;如果你想绘制状态栏等装饰,这是必须的。
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
应在 API 级别 >=19.
中代替 TYPE_SYSTEM_ALERT
附注(如果您正在阅读本文,您可能正在尝试制作全屏叠加层):
您的清单将需要以下权限(解释 here):
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
我的理解是 SYSTEM_ALERT_WINDOW
是所需的实际权限,但还需要 ACTION_MANAGE_OVERLAY_PERMISSION
:它允许您在运行时请求用户授予 SYSTEM_ALERT_WINDOW
权限。
我正在制作一个应用程序,可以创建在屏幕上四处走动的小精灵动画。
我有一个主 activity,带有一个按钮 "start service"。这将启动一项服务,该服务(在 onCreate()
中)创建一个全屏视图并将其附加到根 window 管理器。
这部分工作完美。它会填满屏幕,您可以离开应用程序,动画仍然会在所有内容上可见。
旋转设备时出现问题。
精灵已经移动到屏幕中间,但这是一个无关的问题;这里重要的是黑色边界框——这个框显示了我可以绘制的canvas,它需要填满整个屏幕
视图仍然是纵向模式,尽管所有其他布局似乎都已正确更新。
部分问题来自于我指定视图尺寸的方式。我使用标志来指定 "full screen",但这 而不是 设置视图的 width
和 height
以匹配屏幕尺寸。因此我不得不在启动时手动设置它们。
我不知道如何在创建视图后更新视图的宽度和高度,但我觉得我需要这样做,因为视图的尺寸决定了 canvas.
我的服务像这样创建视图:
public class WalkerService extends Service {
private WalkerView view;
@Override
public void onCreate() {
super.onCreate();
final WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);
final DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
view = new WalkerView(this); // extends SurfaceView
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, // TYPE_SYSTEM_ALERT is denied in apiLevel >=19
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT
);
view.setFitsSystemWindows(false); // allow us to draw over status bar, navigation bar
// here I _manually_ set the dimensions, because otherwise it defaults to a square (something like 1024x1024)
params.width = metrics.widthPixels;
params.height = metrics.heightPixels;
wm.addView(view, params);
}
}
我试过监听方向变化,并在发生这种情况时旋转视图:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
view.setRotation(
newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
? 90.0f
: 0.0f
);
}
但是 setRotation()
似乎并没有影响 width
和 height
我的观点,它们也没有改变 canvas 呈现的角度。
我是否从根本上误解了如何创建全屏覆盖?无论方向如何(甚至当我的应用程序不是活动应用程序时),我如何保持在整个屏幕上绘制的能力?
可能与我将我的视图附加到 Window 服务的 window 管理器这一事实有关 — 也许这意味着我的视图有一个奇怪的父视图(我可以想象根视图可能会不受方向的影响,或者没有明确的边界供儿童伸展以填充)。
完整的源代码是 public on GitHub 以防我在这里遗漏了任何有用的信息。
WalkerView.java
可能特别相关,所以 here it is.
我认为您的情况可以通过其他方法解决。
- 创建全屏自定义视图(您可以在自定义视图的构造函数中设置适当的标志)
- 覆盖自定义视图中的
onSizeChanged()
。这将为您提供 Canvas 高度和宽度。这是可选的。如果您想用半透明颜色填充整个自定义视图,则没有必要。 - 使用上面的宽度和高度,或者简单地在自定义视图的
onDraw()
中使用canvas.drawColor()
。
这将确保无论何时重新创建视图,它都会重新绘制以填满整个屏幕(canvas)。
我被上一个问题的答案误导了 "How to create always-top fullscreen overlay activity in Android"。也许它适用于较旧的 API 级别?我正在使用 API 级别 24。
该特定答案推荐:
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT
);
这个有问题。 WindowManager.LayoutParams
存在的构造函数如下:
因此 WindowManager.LayoutParams.FLAG_FULLSCREEN
标志被用作 int w
和 int h
的显式值。这不行!
我发现正确的全屏叠加结构是这样的:
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, // TYPE_SYSTEM_ALERT is denied in apiLevel >=19
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_FULLSCREEN,
PixelFormat.TRANSLUCENT
);
这意味着我们不再显式指定宽度和高度。布局完全依赖于我们的标志。
是的,WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
仍然是一个必需的标志;如果你想绘制状态栏等装饰,这是必须的。
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
应在 API 级别 >=19.
TYPE_SYSTEM_ALERT
附注(如果您正在阅读本文,您可能正在尝试制作全屏叠加层):
您的清单将需要以下权限(解释 here):
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
我的理解是 SYSTEM_ALERT_WINDOW
是所需的实际权限,但还需要 ACTION_MANAGE_OVERLAY_PERMISSION
:它允许您在运行时请求用户授予 SYSTEM_ALERT_WINDOW
权限。