在方向更改后,如何使全屏叠加保持全屏?

时间:2017-01-04 02:06:14

标签: android android-layout android-service android-view android-fullscreen

我正在创建一个应用程序,可以在屏幕上创建微小的精灵动画。

我有一个主要活动,有一个按钮"启动服务"。这将启动一个服务,该服务(在onCreate()中)创建一个全屏视图并将其附加到根窗口管理器。

这部分完美无缺。它填满了屏幕,您可以离开应用程序,动画仍然可以在所有内容中看到。

initial render is fine

旋转设备时出现问题。

view does not rotate with device

Sprites已移至屏幕中间,但这是一个无关的问题;这里重要的是黑暗的边界框 - 这个框显示我允许绘制的画布,它需要填满整个屏幕

即使所有其他布局似乎都已正确更新,视图仍然是纵向模式。

部分问题来自于我如何指定视图的尺寸。我使用标记来指定"全屏",但这确实设置视图的widthheight以匹配屏幕&#39 ;尺寸。因此,我必须在启动时手动设置它们。

创建视图后,我不知道更新视图宽度和高度的方法,但我觉得我需要,因为视图的尺寸决定了画布的尺寸。

我的服务创建了这样的视图:

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()似乎不会影响我视图的widthheight,也不会改变画布的渲染角度。

我是否从根本上误解了如何创建全屏叠加层?无论方向如何(甚至当我的应用程序不是活动应用程序时),我如何保持在整个屏幕上绘图的能力?

也许这与我将视图附加到Window Service的窗口管理器这一事实有关 - 也许这意味着我的视图有一个奇怪的父级(我可以想象,根视图可能不会是受到方向的影响,或者没有明确的边界让孩子们伸展以填充)。

完整的源代码是public on GitHub,以防我在这里省略了任何有用的信息。

WalkerView.java可能特别相关,因此here it is

2 个答案:

答案 0 :(得分:1)

我被上一个问题"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存在的构造函数如下:

enter image description here

因此,WindowManager.LayoutParams.FLAG_FULLSCREEN标记将用作int wint 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仍然是必需的标志;如果你想绘制状态栏等装饰,这是必要的。

在API级别> = 19中应使用

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY代替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权限。

答案 1 :(得分:0)

我认为您的情况可以通过替代方法解决。

  1. 创建全屏自定义视图(您可以在自定义视图的构造函数中设置相应的标志)
  2. 在自定义视图中覆盖onSizeChanged()。这将为您提供画布的高度和宽度。这是可选的。如果你想用半透明的颜色填充整个自定义视图,这将是必要的。
  3. 使用上方的宽度和高度,或只使用自定义视图的canvas.drawColor()中的onDraw()
  4. 这将确保无论何时重新创建视图,都会重新绘制它以填充整个屏幕(画布)。