标志 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 不适用于类型 WindowManager.LayoutParams.TYPE_SYSTEM_ERROR

Flag WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS does not work with type WindowManager.LayoutParams.TYPE_SYSTEM_ERROR

我正在尝试在 锁屏 上显示一个视图,并且还希望能够部分显示它(例如,当它处于 'inactive').
使用 WindowManager.LayoutParams.TYPE_SYSTEM_ERROR 描述的 here and here 类型(以及一些建议使用此标志的其他帖子),我可以在锁屏上显示它。但是连同标志 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 似乎在 API 21 级上不起作用。

代码:

WindowManager.LayoutParams params = new 
    WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, 
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
    WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | 
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | 
    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | 
    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSPARENT);

但经过一些测试,类型 WindowManager.LayoutParams.TYPE_SYSTEM_ERROR 和标志 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 的组合确实适用于 Android API 级别 16,但不适用于级别 21。它只停留在边缘的一侧而不会离开屏幕。我也尝试设置 horizo​​ntalmargin 属性 但无济于事。

不同的结果:

API level 16 (working)

API level 21 (not working)

添加视图的代码(精简):

layout = (RelativeLayout) getLayoutInflater().inflate(R.layout.testlayout, null).findViewById(R.id.rl);
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSPARENT);
params.x = 600;
manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
manager.addView(layout, params);

这是错误还是其他原因?如果有的话,是否有解决方法?

问答:

do these two screens have the same dimensions, resolution, and density?

分辨率是,尺寸不是,密度也不是。

if you increase the horizontal offset (the params.x value) a little, do both apis 16 and 21 go right?

在 API 级别 16 它向右移动了一点,但在 21 它只是停留在右边并且什么都不做。

更新

我看过另一个应用程序,他们能够实现这一点(请参阅 this gif)。我反编译了他们的 apk 以查看他们的源代码,我发现的关于 LayoutParams 的唯一方法是:

private LayoutParams getLayoutParams() {
    LayoutParams params = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, this.lockscreen ? 2010 : 2002, 262696, -3);
    if (this.focusable) {
        params.flags ^= 8;
    }
    params.gravity = 8388659;
    if (this.keyboard) {
        params.softInputMode = 16;
    }
    return params;
}

this.lockscreen 是一个布尔值是否应该显示在锁屏上

2010WindowManager.LayoutParams.TYPE_SYSTEM_ERROR (https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#TYPE_SYSTEM_ERROR)

2002WindowManager.LayoutParams.TYPE_PHONE, 试过这个但在锁屏上不起作用(https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#TYPE_PHONE

262696标志 = 0x40228 = FLAG_WATCH_OUTSIDE_TOUCH, FLAG_LAYOUT_NO_LIMITS, FLAG_NOT_TOUCH_MODAL, FLAG_NOT_FOCUSABLE

-3PixelFormat.TRANSLUCENT (https://developer.android.com/reference/android/graphics/PixelFormat.html#TRANSLUCENT)

所以我真的很想知道他们是如何做到这一点的,因为他们似乎也使用了类型 WindowManager.LayoutParams.TYPE_SYSTEM_ERROR

这可能是不可能的...

我找到这个 blog post:

In Android 4.4 this behavior was changed. Effectively, this change disables FLAG_LAYOUT_NO_LIMITS for TYPE_SYSTEM_ERROR Windows. This makes it impossible for user apps to place a Window over the shown navigation bar anymore.

引用了这个 Android source code commit。提交消息指出:

Limit TYPE_SYSTEM_ERROR to system decor bounds.

Because windows of TYPE_SYTEM_ERROR lie in a layer above the
Navigation Bar they cannot be allowed to extend into the Navigation
Bar area or it will block touches to the Home button.

Fixes bug 9626315

并且一段 diff 显示:

- if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0) {
+ // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
+ if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR) {

从 API19 开始,您将不得不另辟蹊径。也许是另一种 window 类型,或者以更严格的方式处理视图边界。