【问题标题】:How do I make my full-screen overlay remain full-screen after orientation changes?如何使我的全屏覆盖在方向更改后保持全屏?
【发布时间】:2017-01-04 02:06:14
【问题描述】:

我正在制作一个应用程序,它可以创建在你的屏幕上四处走动的小精灵动画。

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

这部分工作得很好。它会填满屏幕,您可以离开应用程序,动画仍然会在所有内容上可见。

旋转设备时出现问题。

精灵已经移动到屏幕中间,但这是一个无关的问题;这里重要的是黑色边界框——这个框显示了我可以在其中绘制的画布,它需要填满整个屏幕

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


部分问题来自我如何指定视图的尺寸。我使用标志来指定“全屏”,但这确实没有设置视图的widthheight 以匹配屏幕的尺寸。因此我不得不在启动时手动设置。

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

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

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,也没有改变画布的渲染角度。


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

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

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

WalkerView.java 可能特别相关,所以here it is

【问题讨论】:

  • 我认为这是一个重复的问题stackoverflow.com/a/10107525/5870896
  • 拥有 2 个视图怎么样。一个用于纵向,一个用于横向,每个都具有正确的宽度和高度。当方向发生变化时,只需删除不相关的视图并将有效视图添加到窗口管理器。
  • @SecretCoder 我相信我的代码已经类似于该问题的答案,因为这是我用来构建代码的参考之一。唯一的区别是链接的答案通过使用充气机来获取对视图的引用,而我的代码在服务中构造了一个内联视图的实例。鉴于我有与该答案相当的代码并且仍然遇到问题:我会说链接的问题回答我的问题,因此不 i> 同样的问题。
  • @KartikSharma 可以工作。是不是矫枉过正?理想情况下,我想避免给用户的屏幕旋转增加太多开销。我还怀疑覆盖消失的间隙会存在;我宁愿避免看起来有问题的事情。我确实有另一个想法:也许我可以让画布是方形的——两个尺寸都设置为Math.max(metrics.widthPixels, metrics.heightPixels)。这样,同一个画布可以用于任一方向(只需要知道屏幕边界在哪里)。
  • 能否请您添加完整代码?

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


【解决方案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 存在的构造函数如下:

所以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 仍然是必需的标志;如果你想在状态栏等装饰上绘制,这是必要的。

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 权限。

【讨论】:

    【解决方案2】:

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

    1. 创建全屏自定义视图(可以在自定义视图的构造函数中设置相应的标志)
    2. 在自定义视图中覆盖onSizeChanged()。这将为您提供画布的高度和宽度。这是可选的。如果您想用半透明颜色填充整个自定义视图,则没有必要这样做。
    3. 使用上方的宽度和高度,或者在自定义视图的onDraw() 中直接使用canvas.drawColor()

    这将确保无论何时重新创建视图,它都会重新绘制以填满整个屏幕(画布)。

    【讨论】:

    • 我的目标是在我的整个画布边界上绘制颜色 - 我绘制该框仅用于说明目的(以证明我的视图忽略了旋转,并且它的与它们的初始值相比,尺寸不会更新)。但是第 2 步听起来正是我想要的,所以我试了一下。不幸的是,我的视图的onSizeChanged()触发一次(在启动时;它从 0x0 变为 1080x1920)。旋转设备不会触发此事件。 (是的,我的服务在我的清单中订阅了android:configChanges="keyboardHidden|orientation|screenSize")。
    • 修正:onSizeChanged() 确实会在方向更改时触发,但仅在您的布局具有动态尺寸时触发。我最初使用的WindowManager.LayoutParams() 构造具有显式 设置的尺寸——WindowManager.LayoutParams.FLAG_FULLSCREEN 被解释为宽度和高度的整数值1024——因此视图的大小从未改变。但是如果与my answer 中的WindowManager.LayoutParams() 构造结合使用,onSizeChanged() 确实会被触发。
    猜你喜欢
    • 2022-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多