屏幕方向锁定

34

有没有一种可靠的方法来锁定所有Android设备上的屏幕方向?下面的代码适用于我的Nexus S和其他手机,但由于某种原因,ROTATION_90对应于Xoom上的SCREEN_ORIENTATION_REVERSE_PORTRAIT。

有没有办法可靠地将旋转映射到方向?

private void lockScreenOrientation() {
    if (!mScreenOrientationLocked) {
        final int orientation = getResources().getConfiguration().orientation;
        final int rotation = getWindowManager().getDefaultDisplay().getOrientation();

        if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            }
            else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
        }
        else if (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270) {
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
            }
            else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
            }
        }

        mScreenOrientationLocked = true;
    }
}

private void unlockScreenOrientation() {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
    mScreenOrientationLocked = false;
}

编辑:这段代码旨在获取当前方向并将其锁定。方向将被暂时锁定,然后释放给用户。


2
哇,为了锁定屏幕方向居然需要这么多代码。我只是需要在一个屏幕上锁定方向而已,只需要setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)就可以了。 - javisrk
3
代码锁定在当前方向,而不是任意方向。该代码对除了Honeycomb纵向以外的所有内容都有效。我可以在其中硬编码该情况,但这不是一个永久的解决方案,因为设备制造商可以指定任何旋转/方向组合。 - Michael Pardo
1
我现在也遇到了完全相同的问题,写了与您的代码示例几乎完全相同的东西,但它在我的 Galaxy 10.1 上不起作用。自从发布这篇文章以来,您有取得任何进展吗? - Rich
1
不幸的是,这只适用于某些设备。似乎有一些设备其中旋转90度是自然的“相反”方向,而另一半设备则是旋转270度是自然的“相反”方向。例如,这会颠倒Kindle Fire HD上的横向视图。 - enl8enmentnow
请查看我的编辑,以使其适用于所有设备,它会检查旋转是否已更改。 - enl8enmentnow
显示剩余3条评论
8个回答

34

这是我的解决方案,它在任何 Android SDK 上都适用于手机和平板电脑。

switch (getResources().getConfiguration().orientation){
        case Configuration.ORIENTATION_PORTRAIT:
            if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.FROYO){
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                int rotation = getWindowManager().getDefaultDisplay().getRotation();
                if(rotation == android.view.Surface.ROTATION_90|| rotation == android.view.Surface.ROTATION_180){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                } else {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                }
            }   
        break;

        case Configuration.ORIENTATION_LANDSCAPE:
            if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.FROYO){
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            } else {
                int rotation = getWindowManager().getDefaultDisplay().getRotation();
                if(rotation == android.view.Surface.ROTATION_0 || rotation == android.view.Surface.ROTATION_90){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                } else {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                }
            }
        break;
    }

这对我很有效。我有一个Xoom和一个Nexus 7用于测试。Xoom的“自然”方向是横向,因此它的横向方向值为ROTATION_0。它的纵向位置是ROTATION_270。Nexus7的“自然”方向是纵向,因此它的纵向方向值为ROTATION_0,而它的横向方向是ROTATION_90。 - SkolVikingsGuy
1
我认为你应该比较 <= FROYO 而不仅仅是 < FROYO。我相信 REVERSE 值直到 GINGERBREAD 才可用。 - SkolVikingsGuy
6
我的巨大恐惧是这些数值可能因设备而异,以至于没有正确的方法可以完成这个任务。在这方面,Android 系统很混乱。令人惊讶的是,没有一种简单的方法可以获取当前方向,然后稍后将设备设置为相同的方向,而不必花费一整天来找出如何做,最后可能还会出现错误。 - SkolVikingsGuy
+1 表示没有人应该期望在 FROYO 或更早的版本中出现锁定的反向方向。 - ateiob
但是有些坏消息,我在Galaxy S3上测试了一下,发现了一个意外的行为。我的应用程序只能在横屏模式下工作。如果我让应用程序保持打开状态,然后锁定手机(关闭屏幕),然后再次进入手机,应用程序会以纵向模式重新启动。我调试了代码,似乎“getResources().getConfiguration().orientation”总是返回纵向模式,而不是在清单中指定的应用程序横向模式设置。 - PerracoLabs
刚在Nexus9上测试了一下,使用的是Android 6.0版本。仍然像魔法般地运行良好。 - Nantoka

18

我稍微修改了diyism的答案,以弥补在2.3版本之前无法使用reverse_landscape和reverse_portrait模式的事实。

private static void disableRotation(Activity activity)
{       
    final int orientation = activity.getResources().getConfiguration().orientation;
    final int rotation = activity.getWindowManager().getDefaultDisplay().getOrientation();

    // Copied from Android docs, since we don't have these values in Froyo 2.2
    int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8;
    int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9;

    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.FROYO)
    {
        SCREEN_ORIENTATION_REVERSE_LANDSCAPE = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        SCREEN_ORIENTATION_REVERSE_PORTRAIT = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
    }

    if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90)
    {
        if (orientation == Configuration.ORIENTATION_PORTRAIT)
        {
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
        else if (orientation == Configuration.ORIENTATION_LANDSCAPE)
        {
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }
    }
    else if (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270) 
    {
        if (orientation == Configuration.ORIENTATION_PORTRAIT) 
        {
            activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        }
        else if (orientation == Configuration.ORIENTATION_LANDSCAPE) 
        {
            activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        }
    }
}

private static void enableRotation(Activity activity)
{
    activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}

3
该代码并不适用于所有设备。例如,Xoom将ROTATION_270映射为PORTRAIT而不是REVERSE_PORTRAIT。以下的解决方案可以解决这个问题。 - SkolVikingsGuy

6

如果需要暂时锁定屏幕,您可以轻松使用以下代码:

//developing for android tablets **<uses-sdk android:minSdkVersion="12" />**
//works perfectly... **WATCH OUT**: look portrait to reverse-portrait on api level 13 :)

currentActivity.setRequestedOrientation(currentActivity.getResources().getConfiguration().orientation);

//to re-enable sensor, just do:

currentActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

在显示对话框和执行重要后台工作期间,我将其用作临时屏幕锁定。

请确保在尝试访问当前活动时,当前活动是有效的,否则它将无法正常工作 :)

祝你好运 :)


它只在某些版本的安卓上完美运行。你测试了哪些版本?你测试过横屏和反向横屏方向的手机吗? - Juozas Kontvainis
嘿,看起来如果设备方向为纵向,并且您使用锁定功能,则会将其锁定到反向纵向在API级别13(3.2,我想)上。因此,代码应该进行相应的调整,我正在处理 :) 对于更改感到抱歉,这次我不会更改它... - cV2

3
// Works on all devices. The other solution only works on 1/2 of the devices.
// Lock orientation
int rotation = getWindowManager().getDefaultDisplay().getRotation();
lockOrientation(rotation, Surface.ROTATION_270);
// Ensure that the rotation hasn't changed
if (getWindowManager().getDefaultDisplay().getRotation() != rotation) {
    lockOrientation(rotation, Surface.ROTATION_90);
}
// ...
private void lockOrientation(int originalRotation, int naturalOppositeRotation) {
    int orientation = getResources().getConfiguration().orientation;
    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
        // Are we reverse?
        if (originalRotation == Surface.ROTATION_0 || originalRotation == naturalOppositeRotation) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        } else {
            setReversePortrait();
        }
    } else {
        // Are we reverse?
        if (originalRotation == Surface.ROTATION_0 || originalRotation == naturalOppositeRotation) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        } else {
            setReverseLandscape();
        }
    }
}

@SuppressLint("InlinedApi") 
private void setReversePortrait() {
    if (Build.VERSION.SDK_INT >= 9) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
}

@SuppressLint("InlinedApi") 
private void setReverseLandscape() {
    if (Build.VERSION.SDK_INT >= 9) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
}

我看到你上面关于Kindle Fire HD的评论。目前我也遇到了类似的问题,我有两种不同的解决方案(基本相同的逻辑),在我所有的手机和平板电脑上都有效,但在Kindle Fire HD上却无效。基于android.os.Build值编写特殊情况代码感觉比我舒适范围内的“hacky”还要更加“hacky”。你有没有找出为什么Fire HD会有所不同、优雅的解决方案以及/或者Kindle系列设备中有多少设备表现相同?很想讨论一下。谢谢。 - Rich
@Rich,这是因为一些手机的naturalOppositeRotation不同。使用上述代码可以通过检查旋转是否发生变化来确定您手机的默认值。我在第一次确定后将naturalOppositeRotation的值保存在用户首选项中。 - enl8enmentnow
@Rich,你没有理解内联API的重点...这与解决方案无关。在9之前是不存在REVERSE的。 - enl8enmentnow
1
所以,这个天才之处在于,在调用setRequestedOrientation之后,getRotation返回新值...我以为它会是一个异步过程,并且不会立即可用。非常有洞察力,而且很疯狂,它隐藏在线程的深处,只有一个赞(来自我)。 - Rich
@Rich,你懂的。为了公平起见,这个完整的解决方案是在原始问题提出几个月后添加的。 - enl8enmentnow
我在 Kindle Fire HD 7" 上尝试了这种技术,结果导致屏幕翻转了一下又立即恢复(由于两次调用 setRequestedOrientation)。请查看我的答案中的硬编码解决方案。如果其他人遇到同样的问题,你也可以在这个解决方案中轻松使用 isLandscape270() - Eric Simonton

2

这个解决方案是在其他方案的基础上构建的。它是处理enl8enmentnow解决的问题的不同方法:在某些设备上,横屏是ROTATION_90,但在(少数)其他设备上,它是ROTATION_270。当我尝试像enl8enmentnow的解决方案一样在Kindle Fire HD 7"上使用时,它会使屏幕翻转到倒置,然后立即恢复正常。除了硬编码哪些设备将横屏视为270外,我没有看到其他想法,因此这里是硬编码的解决方案:

public static void unlockOrientation() {

    activity.setRequestedOrientation(
        ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}

public static void lockOrientation() {

    if (Build.VERSION.SDK_INT < 18) {
        activity.setRequestedOrientation(getOrientation());
    } else {
        activity.setRequestedOrientation(
            ActivityInfo.SCREEN_ORIENTATION_LOCKED);
    }
}

private static int getOrientation() {

    int port = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
    int revP = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
    int land = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
    int revL = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
    if (Build.VERSION.SDK_INT < 9) {
        revL = land;
        revP = port;
    } else if (isLandscape270()) {
        land = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
        revL = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
    }

    Display display = activity.getWindowManager().getDefaultDisplay();
    boolean wide = activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
    switch (display.getRotation()) {
        case Surface.ROTATION_0:
            return wide ? land : port;
        case Surface.ROTATION_90:
            return wide ? land : revP;
        case Surface.ROTATION_180:
            return wide ? revL : revP;
        case Surface.ROTATION_270:
            return wide ? revL : port;
        default:
            throw new AssertionError();
    }
}

private static boolean isLandscape270() {

    return android.os.Build.MANUFACTURER.equals("Amazon")
        && !(android.os.Build.MODEL.equals("KFOT") || android.os.Build.MODEL.equals("Kindle Fire"));
}

isLandscape270()可以检测设备是否为第二代或更高版本的Kindle(请参考此链接,从此链接获取MODEL)。如果您知道其他设备是否也应包括在内,请评论告知。

此外,在API版本大于等于18的情况下,它将简单地使用setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED)。我只在模拟器上进行了测试,请评论告知它在真实设备上是否存在问题。


2

使用setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR),因为屏幕方向是由物理方向传感器确定的:显示屏将根据用户移动设备的方式旋转。这允许任何4个可能的旋转,无论设备通常会如何操作(例如,某些设备通常不会使用180度旋转)。你的代码也应该适用于Xoom ...


1

我正在尝试在代码中暂时锁定当前方向。这样做不起作用,因为它是永久性的。请阅读我的代码以了解我想要做什么。 - Michael Pardo
哈哈,我刚刚读了你问题顶部的描述,它并没有明确说明你正在寻找一个可编程的可逆解决方案。 - adam

-1

我的解决方案:

int orientation=act.getResources().getConfiguration().orientation;
int rotation=act.getWindowManager().getDefaultDisplay().getOrientation();
if (orientation==Configuration.ORIENTATION_PORTRAIT)
   {if (rotation==Surface.ROTATION_0 || rotation==Surface.ROTATION_270) //0 for phone, 270 for tablet
       {act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
       }
    else
        {act.setRequestedOrientation(9);//instead of SCREEN_ORIENTATION_REVERSE_PORTRAIT when <= android 2.2
        }
   }
else
    {if (rotation==Surface.ROTATION_90 || rotation==Surface.ROTATION_0) //90 for phone, 0 for tablet
        {act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }
     else
         {act.setRequestedOrientation(8);//instead of SCREEN_ORIENTATION_REVERSE_LANDSCAPE when <= android 2.2
         }
    }

@NeTeInStEiN 是的。正如在这个帖子中所看到的那样。因为你是少数观察到这一点的人之一,所以给你点赞。 - ateiob

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