如何在安卓系统中全局强制屏幕方向?

19

有许多应用程序可以强制屏幕旋转,即使某个应用显式要求以另一种方向查看也能起作用。现在我正在禁用加速度计旋转系统设置并设置我的首选方向,但是某些应用仍然可以覆盖这个设置。

这是其中一个可以覆盖应用请求方向的应用:

https://play.google.com/store/apps/details?id=nl.fameit.rotate&hl=en


3
这可能对你有所帮助。链接是一个Java文件,它包含用于旋转锁定的工具的代码。 - Ahmad
这似乎是一个面向终端用户的问题,而不是编程问题。答案是您无法全局设置方向。任何应用程序都可以选择自己处理方向,并且如果确实如此,则可以选择忽略设备方向的更改或根据其自己的标准更改方向。一些应用程序甚至没有能力在横向(或纵向)模式下正确显示,因此即使可能,覆盖它们选择的方向也不可取。 - Carl
1
你可以创建一个隐藏的系统对话框,并在其上设置特定标志,然后请求横向方向。当我确定它可行时,我会发布整个源代码。 - kagronick
@Ahmad,那似乎只是禁用系统自动旋转,这并不能直接让你控制屏幕方向。 - Sam
4个回答

26

我尝试了kagronick的方法但无法使其工作。经过一段时间的摸索,我最终使用了一个系统覆盖窗口,并删除了我发现全都是不必要的LayoutParams,最终找到了解决方案:

orientationChanger = new LinearLayout(this);
// Using TYPE_SYSTEM_OVERLAY is crucial to make your window appear on top
// You'll need the permission android.permission.SYSTEM_ALERT_WINDOW
WindowManager.LayoutParams orientationLayout = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 0, PixelFormat.RGBA_8888);
// Use whatever constant you need for your desired rotation
orientationLayout.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;

// Optional: Replace "Context" with "Service" when used in a service
WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
wm.addView(orientationChanger, orientationLayout);
orientationChanger.setVisibility(View.VISIBLE);
你可以在我发布的这个应用中看到我的成功:Force Dock Rotation

我尝试了你的应用程序并发现它可以正常工作。然而,你在这个答案中的代码对我来说不起作用。它没有改变屏幕方向,当我回到主屏幕时,图标和文本是看不见的。你最终是否改变了这个算法以使它在你的应用程序中正常工作? - Sam
3
进一步调查后发现,当代码从服务中运行时,您上面的代码正常工作,但是从活动中运行时却不行。可能与所使用的上下文有关。(我不擅长Android,所以不确定。) - Sam
@Sam 嘿,谢谢你的提示!从服务中调用它完成了工作! - sjkm
@Sam 抱歉我来晚了,但问题在于上下文。当从活动调用系统服务时,您应该使用“Context”代替“Service”。我已经编辑答案以反映这一信息。 - Abandoned Cart

15

这可以通过创建一个隐藏的系统对话框来实现。虽然有点小技巧,但足够疯狂以使其奏效。

    wm = (WindowManager) content.getSystemService(Service.WINDOW_SERVICE);

    orientationChanger = new LinearLayout(content);
    orientationChanger.setClickable(false);
    orientationChanger.setFocusable(false);
    orientationChanger.setFocusableInTouchMode(false);
    orientationChanger.setLongClickable(false);

    orientationLayout = new WindowManager.LayoutParams(
            LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
            windowType, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.RGBA_8888);

    wm.addView(orientationChanger, orientationLayout);
    orientationChanger.setVisibility(View.GONE);

    orientationLayout.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
    wm.updateViewLayout(orientationChanger, orientationLayout);
    orientationChanger.setVisibility(View.VISIBLE);

嘿,我尝试了你的代码片段,并进行了几次修改,但运行不成功。你是从服务中执行这段代码吗?谢谢。 - sjkm
是的,我相信我做到了。 - kagronick
1
只有当我选择 windowType = LayoutParams.TYPE_APPLICATION_OVERLAY 时才能正常工作 :) - JBSnorro

15
作为旁注,你也可以全局更改屏幕方向,而不强制在不支持的应用程序中使用它。(我知道这个问题是关于覆盖应用程序偏好的,但这仍然是有用的和大部分相关的信息。)
  1. Grant access to system settings (WRITE_SETTINGS) in AndroidManifest.xml

    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    
  2. Import Settings

    import android.provider.Settings;
    
  3. Ensure Android screen auto-rotation is disabled

    Settings.System.putInt(
        getContentResolver(),
        Settings.System.ACCELEROMETER_ROTATION,
        0
    );
    
  4. Set USER_ROTATION to the desired setting, which should be one of the ROTATION_ constants. These values represent screen rotations from the device's natural orientation, which could be either landscape or portrait.

    Settings.System.putInt(
        getContentResolver(),
        Settings.System.USER_ROTATION,
        Surface.ROTATION_0 //Or a different ROTATION_ constant
    );
    
请注意,即使您的应用程序未运行或已卸载,对 USER_ROTATION 的更改仍将持续存在。如果我没记错的话,用户可以通过手动禁用和启用自动旋转来重置此值。

代码中的第3点应该写在哪里?此外,您的片段是否强制所有应用程序的方向,还是只强制其所编写的应用程序的方向? - Basher51
@Basher51,最好在第4点的代码运行之前运行。当启用Android自动旋转时,第4点的代码似乎没有任何效果,这就是为什么需要第3点的原因。 - Sam
@Basher51,它似乎影响所有应用程序(这就是我所说的“全局”),除了指定自己方向偏好的应用程序,因此它并不真正“强制”方向。 - Sam

5

创建系统视图

创建一个不可见的 View,它始终显示在其他应用程序之上。 View 可以指定自己的方向偏好,因此您的视图方向可以用于覆盖底层视图和应用程序的方向。

选项

我意识到有几种不同的视图类型可以用来实现这一点,每种类型都有其自身的缺点。

  1. System Overlay window (TYPE_SYSTEM_OVERLAY)

    Requires the SYSTEM_ALERT_WINDOW permission. (Add the following to AndroidManifest.xml.)

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    
  2. Toast window (TYPE_TOAST)

    No permission required, except on MIUI V8.

    Doesn't display over some window types, so their orientations mightn't be affected:

操作指南

  1. 创建一个View
  2. 创建一个WindowManager.LayoutParams
    1. type设置为您上面选择的值。
    2. 将其widthheight设置为零,以防止与不支持重叠窗口的应用程序发生问题。(例如, 当您尝试启用无障碍服务时,如果有一个窗口位于"确定"按钮之上,则Android将不允许您按下该按钮。)
    3. 将其screenOrientation设置为您想要的任何屏幕方向值

示例

public class ScreenOrientationEnforcer {

    private final View view;
    private final WindowManager windows;

    public ScreenOrientationEnforcer(Context context) {
        windows = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        view = new View(context);
    }

    public void start() {
        WindowManager.LayoutParams layout = generateLayout();
        windows.addView(view, layout);
        view.setVisibility(View.VISIBLE);
    }

    public void stop() {
        windows.removeView(view);
    }

    private WindowManager.LayoutParams generateLayout() {
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();

        //So we don't need a permission or activity
        //Note that this won't work on some devices running MIUI
        layoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;

        //Just in case the window type somehow doesn't enforce this
        layoutParams.flags =
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
            | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

        //Prevents breaking apps that detect overlying windows enabling
        //(eg UBank app, or enabling an accessibility service)
        layoutParams.width = 0;
        layoutParams.height = 0;

        //Try to make it completely invisible
        layoutParams.format = PixelFormat.TRANSPARENT;
        layoutParams.alpha = 0f;

        //The orientation to force
        layoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;

        return layoutParams;
    }

}

ScreenOrientationEnforcer screenOrientationEnforcer
    = new ScreenOrientationEnforcer(context);

//Force screen orientation
screenOrientationEnforcer.start();

//Stop forcing screen orientation
screenOrientationEnforcer.stop();

缺点

  • 根据我的经验,如果您的应用程序崩溃或被杀死,视图可能会持续存在。重新启动设备似乎可以解决此问题。

参考资料


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接