How do I make my full-screen overlay remain full-screen after orientation changes?

笑着哭i 提交于 2020-01-04 02:28:29

问题


I am making an app that creates tiny sprite animations that walk around your screen.

I have a main activity, with a button "start service". This starts a service, which (in onCreate()) creates a full-screen view and attaches it to the root window manager.

This part works perfectly. It fills the screen, and you can leave the app, and the animations will still be visible over everything.

The problem emerges when you rotate the device.

Sprites have moved to the middle of the screen, but this is an unrelated issue; the important thing here is the dark bounding box — this box shows you the canvas I am allowed to draw into, and it needs to fill the whole screen

The view is still portrait-mode, even though all other layouts seem to have updated correctly.


Part of the problem comes from how I have specified the dimensions of the view. I used flags to specify "full screen", but this did not set the view's width and height to match the screen's dimensions. Thus I had to set those manually at startup.

I don't know a way to update the width and height of the view once the view is created, but I feel that I need to, since the dimensions of the view determine the dimensions of the canvas.

My service creates the view like so:

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);
    }
}

I have tried listening for orientation changes, and rotating the view when that happens:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    view.setRotation(
            newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
                    ? 90.0f
                    : 0.0f
    );
}

But setRotation() didn't seem to affect width and height of my view, nor did they change the angle at which the canvas was rendered.


Am I fundamentally misunderstanding how to create a full-screen overlay? How can I maintain the ability to draw over the entire screen, regardless of orientation (and even when my app is not the active app)?

Maybe it's related to the fact that I attach my view to the Window Service's window manager — perhaps this means my view has an odd parent (I could imagine that maybe the root view would not be affected by orientation, or would have no defined boundaries for children to stretch to fill).

Full source code is public on GitHub in case I've omitted any useful information here.

WalkerView.java may be particularly relevant, so here it is.


回答1:


I was misled by the answer to a previous question "How to create always-top fullscreen overlay activity in Android". Maybe it worked for an older API level? I'm using API level 24.

That particular answer had recommended:

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
);

There is a problem with this. The constructors that exist for WindowManager.LayoutParams are as follows:

So the WindowManager.LayoutParams.FLAG_FULLSCREEN flag gets used as an explicit value for int w and int h. This is no good!


I found that the correct construction for a full-screen overlay is like so:

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
);

This means we no longer explicitly specify a width and height. The layout relies entirely on our flags instead.

Yes, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN is a required flag still; it is necessary if you want to draw over decorations such as the status bar.

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY should be used instead of TYPE_SYSTEM_ALERT in API level >=19.


Bonus notes (if you're reading this, you're probably trying to make a full-screen overlay):

Your manifest will need the following permissions (explanation here):

<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

My understanding is that SYSTEM_ALERT_WINDOW is the actual permission required, but that ACTION_MANAGE_OVERLAY_PERMISSION is needed also: it lets you request at runtime that the user grant the SYSTEM_ALERT_WINDOW privilege.




回答2:


I think your situation can be solved with an alternative approach.

  1. Create a Full Screen custom view (you can set the appropriate flag in the constructor of the custom view)
  2. Override onSizeChanged() in the custom view. This will give you the Canvas height and width. This is optional. If you want to fill the whole custom view with a translucent color, this won't be necessary.
  3. Use the width and height from above, or simply use canvas.drawColor() in the onDraw() of the custom view.

This will ensure that whenever the view is recreated, it will be redrawn to fill the whole screen(canvas).



来源:https://stackoverflow.com/questions/41455138/how-do-i-make-my-full-screen-overlay-remain-full-screen-after-orientation-change

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!