Android: 暂时禁用 Activity 中的屏幕旋转功能

126

我的主要活动有一些代码会进行数据库更改,这些更改不应被中断。我在另一个线程中执行繁重的工作,并使用一个进度对话框,将其设置为不可取消。然而,我注意到当我旋转手机时,它会重新启动活动,这对正在运行的进程非常不好,我会得到一个强制关闭。

我想要做的是在我的进程完成之前以编程方式禁用屏幕方向更改,完成后启用方向更改。


由于似乎没有人提到这一部分,您需要导入android.content.pm.ActivityInfo才能使用ActivityInfo标识符。 - zsalwasser
1
请查看https://dev59.com/F2w15IYBdhLWcg3wcrhC。 - diyism
1
请参考以下链接获取最佳解决方案:https://dev59.com/T3A65IYBdhLWcg3w1SRC#32885911 - Sudhir Sinha
18个回答

174

正如Chris在他的自问自答中所解释的那样,调用

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

接着

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

在实际设备上真的非常好用!

在模拟器上测试时,不要认为它有问题,使用Ctrl + F11快捷键总是会更改屏幕方向,而不会模拟传感器移动。

编辑:这并不是最佳答案。如评论中所述,这种方法存在问题。 真正的答案在这里


我找不到那些常量。谢谢。 - Christopher Perry
44
这些方法存在问题... 如果在设备不处于默认方向时调用setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);,则活动的方向会立即更改(销毁和重新创建)为设备的默认方向。例如,在手机上,如果你将其保持在横向方向,则在重新激活传感器时,活动会被切换到竖直方向并再次切换回横向方向。同样的相反问题也存在于Archos A5 IT上:在纵向使用它会导致活动被切换到横向并再次切换回纵向。 - Kevin Gaudin
1
原问题的真正答案在这里:https://dev59.com/t2865IYBdhLWcg3wa980#3821998 - Kevin Gaudin
2
这对我没有用。但是这个可以:https://dev59.com/OXE95IYBdhLWcg3wUMLW#10488012 我必须根据从getResources().getConfiguration().orientation检索到的当前方向调用setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);或setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);。 - Bitcoin Cash - ADA enthusiast
ActivityInfo.SCREEN_ORIENTATION_SENSOR 不遵循 Android 的本地方向锁定。将方向重置为 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 可以解决问题。 - tvkanters
为什么要把事情复杂化呢?对于特定的活动,只需在清单中简单添加android:screenOrientation="portrait"即可。请参见:https://dev59.com/4nRB5IYBdhLWcg3wl4AP#582585 - nnyerges

45

其他答案对我来说都不能完美解决问题,但我找到了一个可以的方法。

锁定当前方向...

if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

当允许再次更改方向时,将其设置回默认值...

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);

10
问题在于,在两种横屏模式下(即“正常”和“反转”),都将返回Configuration.ORIENTATION_PORTRAIT。因此,如果手机处于反转的横屏方向,并且您将其设置为ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,则它将翻转过来。在API 9中,ActivityInfo引入了SCREEN_ORIENTATION_REVERSE_LANDSCAPE常量,但我没有看到通过Configuration类检测这种方向的方法。 - Błażej Czapp
1
这个可行。上述问题的答案位于此回答中。https://dev59.com/0HE85IYBdhLWcg3wZymt#10453034 - Zack
对我的需求来说也非常好用,太棒了,谢谢。 - user2029541

40

以下是一个更完整且适用于API 8+的解决方案,可适用于反向纵向和横向,并可在Galaxy Tab上使用其中“自然”方向为横向(调用activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)以取消锁定方向):

@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
public static void lockActivityOrientation(Activity activity) {
    Display display = activity.getWindowManager().getDefaultDisplay();
    int rotation = display.getRotation();
    int height;
    int width;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2) {
        height = display.getHeight();
        width = display.getWidth();
    } else {
        Point size = new Point();
        display.getSize(size);
        height = size.y;
        width = size.x;
    }
    switch (rotation) {
    case Surface.ROTATION_90:
        if (width > height)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        break;
    case Surface.ROTATION_180:
        if (height > width)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        break;          
    case Surface.ROTATION_270:
        if (width > height)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        break;
    default :
        if (height > width)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
}

在平板电脑和手机上对我非常有效。 - ScruffyFox
对我来说,唯一适用于所有设备的正确答案。 - amdev
绝对是最佳答案!您可以将此方法设置为“static”,并添加“Activity activity”作为参数。 - caw

20

使用setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);锁定当前方向,无论是横屏还是竖屏。

使用setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);解除方向锁定。


1
最佳的短暂临时锁定解决方案。不要干扰当前传感器的方向。 - The incredible Jan
2
适用于 Build.VERSION.SDK_INT >= 18,更完整的答案由 tdjprog 在此页面 https://dev59.com/T3A65IYBdhLWcg3w1SRC#41812971 给出。 - bastami82

19
为了管理反向方向模式,我已经使用了以下代码来修复活动方向:
int rotation = getWindowManager().getDefaultDisplay().getRotation();

    switch(rotation) {
    case Surface.ROTATION_180:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        break;
    case Surface.ROTATION_270:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);         
        break;
    case  Surface.ROTATION_0:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        break;
    case Surface.ROTATION_90:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        break;
    }

再次允许方向:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);

14
我找到了答案。要做到这一点,在Activity中,您可以调用setRequestedOrientation(int)方法,并使用此处指定的值之一:http://developer.android.com/reference/android/R.attr.html#screenOrientation 在启动我的线程之前,我调用了setRequestedOrientation(OFF)(OFF = nosensor),当线程完成后,我调用了setRequestedOrientation(ON)(ON = sensor)。效果非常好。

11

谢谢大家。我修改了Pilot_51的解决方案,以确保我还原到了之前的状态。我还加入了一些更改来支持非横屏和非竖屏的屏幕(但没有在这样的屏幕上测试过)。

prevOrientation = getRequestedOrientation();
if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}

然后进行恢复

setRequestedOrientation(prevOrientation);

好东西 - 不确定为什么你没有使用 switch - user213345
添加了第三个选项后,忘记清理并更改为switch语句。 - ProjectJourneyman
我发现如果您没有访问活动对象但仅具有上下文,则可以使用ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,而无需获取当前配置。 - max4ever

9
protected void setLockScreenOrientation(boolean lock) {
    if (Build.VERSION.SDK_INT >= 18) {
        setRequestedOrientation(lock?ActivityInfo.SCREEN_ORIENTATION_LOCKED:ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
        return;
    }

    if (lock) {
        switch (getWindowManager().getDefaultDisplay().getRotation()) {
            case 0: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); break; // value 1
            case 2: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); break; // value 9
            case 1: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); break; // value 0
            case 3: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); break; // value 8
        }
    } else
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); // value 10
}

你能否给你的答案添加一些解释? - slfan
当您有一些后台作业时,只需调用setLockScreenOrientation(true)来锁定方向并防止销毁当前活动以重新创建它。当您确保这些作业已完成时,请调用setLockScreenOrientation(false)。 - tdjprog
2
这是最好的答案! - Fakher

7

下面是一种解决方案,它可以在保留当前屏幕方向的情况下每次都能正常工作(使用Activity.Info.SCREEN_ORIENTATION_PORTRAIT设置为0度,但用户当前可能有180度的屏幕方向)。

// Scope: Activity

private void _lockOrientation() {
    if (super.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
        super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
    } else {
        super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
    }
}

private void _unlockOrientation() {
    super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}

2
值得一提的是:仅支持API 18及以上版本。 - Dmitry Zaytsev

1
这对我非常有帮助。它解决了平板电脑/手机不同的“自然方向”问题 ;)
int rotation = getWindowManager().getDefaultDisplay().getRotation();

        Configuration config = getResources().getConfiguration();
        int naturalOrientation;

        if (((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) &&
                config.orientation == Configuration.ORIENTATION_LANDSCAPE)
                || ((rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) &&
                config.orientation == Configuration.ORIENTATION_PORTRAIT)) {
            naturalOrientation = Configuration.ORIENTATION_LANDSCAPE;
        } else {
            naturalOrientation = Configuration.ORIENTATION_PORTRAIT;
        }

        // because getRotation() gives "rotation from natural orientation" of device (different on phone and tablet)
        // we need to update rotation variable if natural orienation isn't 0 (mainly tablets)
        if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE)
            rotation = ++rotation % 4;

        switch (rotation) {
            case Surface.ROTATION_0: //0
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                break;
            case Surface.ROTATION_90: //1
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                break;
            case Surface.ROTATION_180: //2
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                break;
            case Surface.ROTATION_270: //3
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                break;
        }
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    }

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