Android上的Activity方向自动更改

23

我正在开发一个基于Android的移动应用程序,minSdkVersion=15。我想为平板电脑支持两种方向,而对于智能手机则仅支持纵向方向。一切都很顺利,但是我遇到了一个小故障,让我感到很疯狂。 当智能手机处于横向模式时,我尝试触发一个新的Activity,它会在横向模式下打开一段时间,然后自动旋转到纵向模式。 我的每个Activity都扩展了一个GeneralActivity类:

public class GeneralActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // If smartphone lock orientation to portrait
        if (!Helper.isTablet(this.getApplicationContext())){
            this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
    }
}

我可以检测带有此功能的平板电脑:

public class Helper {
    public static boolean isTablet(Context context){
        Configuration config = context.getResources().getConfiguration()
        return config.smallestScreenWidthDp >= 600;
    }
}
我选择不在Manifest.xml文件中指定android:screenOrientation,因为这样可以支持平板电脑的所有界面方向。我是否遗漏了什么? 编辑 我决定采用Jonathan提出的最佳实践,但我描述的问题仍然存在。 这是我在github上的存储库: https://github.com/giacmarangoni/Android-Orientation-Test

1
嗨,我遇到了同样的问题...并且它只发生在Nougat设备上...我的情况与你的类似,我被卡住了。你有什么运气吗? - Mahesh B ツ
7个回答

26

我通过使用以下方法找到了解决这个问题的正确方式:

android:screenOrientation="locked"

在声明了此问题的清单中的每个活动中进行操作。

并且继续在onCreate()方法内使用setRequestedOrientation()编程来定义横向或纵向方向,

顺便说一句,在您的GitHub项目中,我只是更改了清单,添加了像这样的locked方向,最终它成功了:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.giacomomarangoni.orientationtest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:screenOrientation="locked">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".OtherActivity"
            android:screenOrientation="locked">
        </activity>
    </application>
</manifest>

希望它对您有所帮助 :)


4
你是一个英雄。我通常不会仅仅为了表扬而发表评论,但我想强调给任何遇到此问题的人,这是正确的解决方案。它应该被接受作为答案,特别是因为它允许任何定向锁定逻辑与标准setRequestedOrientation方法一起使用。 - Brian Acker
1
这是最好的答案。 - Athira
1
@Crono,你救了我!我在清单文件中添加了锁定,并在我的MediaPlayer加载后使用setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);释放锁定。效果非常好。 - Avramo

24

我在Android N和O上遇到了同样的问题,并找到了解决方案。

我仍然在onCreate方法中使用setRequestedOrientation,但是我已经在清单文件中为每个活动添加了以下内容:

android:screenOrientation="behind"

这将确保该活动与上一个活动以相同的方向启动。 setRequestedOrientation 之后将覆盖它,但如果与前一个活动相同,则用户不会看到任何更改。


1
你使用它时有任何副作用吗? - Sudheesh Mohan

0
在您的基本活动中,您可以放置类似以下的内容:
/*  Device types. */
static int MOBILE_DEVICE = 0;
static int SEVEN_INCH_TABLET = 1;
static int TEN_INCH_TABLET = 2;

private static int deviceType = MOBILE_DEVICE;
private boolean deviceTypeDetermined = false;


@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
protected void onCreate(Bundle savedInstanceState) {

    if ( ! deviceTypeDetermined) setDeviceType();

    /* Screen rotation only for tablets. */
    if (getDeviceType() < SEVEN_INCH_TABLET) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        lockScreenOrientation();

    ......
}



// ---------------------------------------------------------------------------------------------
static int getDeviceType() {
    return deviceType;
}
// ---------------------------------------------------------------------------------------------


// ---------------------------------------------------------------------------------------------
private void setDeviceTypeDetermined() {
    this.deviceTypeDetermined = true;
}
// ---------------------------------------------------------------------------------------------


// ---------------------------------------------------------------------------------------------
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
private void setDeviceType() {

    /*
        Let's invest on what kind of screen our APP is invited...

        We only make a difference in 10", 7" tablets and the rest...
    */

    Display mDisplay = getWindowManager().getDefaultDisplay();
    Point mScreenResolution = new Point();

    mDisplay.getRealSize(mScreenResolution);

    int mWidthPixels = mScreenResolution.x;
    int mHeightPixels = mScreenResolution.y;

    DisplayMetrics mMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(mMetrics);

    float mWidthDpi = mMetrics.xdpi;
    float mHeightDpi = mMetrics.ydpi;

    float mWidthInches = mWidthPixels / mWidthDpi;
    float mHeightInches = mHeightPixels / mHeightDpi;



    double mDiagonalInches = Math.sqrt(
            (mWidthInches * mWidthInches)
                    + (mHeightInches * mHeightInches));


    if (mDiagonalInches >= 9) {

        /*
            A tablet with 8" x 5" is called a 10", but it is in fact
            9.43398". Interesting that this kind of things happens in
            the world of informatics.... ;)
        */

        MyBaseAppCompatActivity.deviceType = TEN_INCH_TABLET;
    }

    else if (mDiagonalInches >= 7) {
        MyBaseAppCompatActivity.deviceType = SEVEN_INCH_TABLET;
    }

    else
    {
        MyBaseAppCompatActivity.deviceType = MOBILE_DEVICE;
    }


    setDeviceTypeDetermined();
}
// ---------------------------------------------------------------------------------------------


// ---------------------------------------------------------------------------------------------
void lockScreenOrientation() {

    /*  Screen rotation only for tablets. */
    if (deviceType < SEVEN_INCH_TABLET ) return;

    setRequestedOrientation(getResources().getConfiguration().orientation);
}
// ---------------------------------------------------------------------------------------------


// ---------------------------------------------------------------------------------------------
void unlockScreenOrientation() {

    /*  Screen rotation only for tablets. */
    if (deviceType < SEVEN_INCH_TABLET ) return;

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
// ---------------------------------------------------------------------------------------------

在你的活动中的onCreate方法中:

// ---------------------------------------------------------------------------------------------
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
protected void onCreate(Bundle savedInstanceState) {
    // -----------------------------------------------------------------------------------------
    super.onCreate(savedInstanceState);

    if (getDeviceType() >= SEVEN_INCH_TABLET) unlockScreenOrientation();

    setContentView(R.layout.main_activity);

    .........

// ---------------------------------------------------------------------------------------------

通过添加您所修复的内容的解释,此答案可以得到改进。 - Evan Weissburg

0

我也遇到了Android N设备的同样问题。为了支持平板电脑的两个方向和手机的纵向方向,我采用了以下技巧:

  1. 在清单文件中为每个活动设置纵向模式。虽然看起来不太好,但在这种情况下屏幕将不会自动旋转:android:screenOrientation="portrait"

  2. 在您的 BaseActivity 中设置:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (isTablet(){  
           setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
        }
    }
    
    protected boolean isTablet() {
       return getResources().getBoolean(R.bool.tablet);
    }
    

0

我已经找到了一个适合我的解决方案。 在你的清单文件中声明屏幕方向为竖屏。

<activity android:name=".activities.MainActivity"
        android:screenOrientation="portrait">

        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>

            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>

然后在您的基本活动中,在调用super.onCreate之前,如果设备是平板电脑,则将方向设置为未指定。

@Override
protected void onCreate(Bundle savedInstanceState) {
    if (!getResources().getBoolean(R.bool.portrait_only)) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    }
    super.onCreate(savedInstanceState);
}

来自文档:

未指定: 未指定任何偏好:让系统决定最佳方向。这将是下面活动选择的方向,或者如果此活动是任务底部,则为用户首选方向。如果用户通过设置显式关闭基于传感器的方向感知,则会忽略基于传感器的设备旋转。如果没有,默认情况下将考虑基于传感器的方向感知,并根据用户如何旋转设备来改变方向。

对我有用,到目前为止我还没有发现任何问题。我只在模拟器中测试过这个解决方案,在手机上似乎并不像其他解决方案那样从纵向开始旋转。无论如何,我更喜欢手机能够更好地工作,因为它们被使用得更多。

如果您发现此解决方案有任何问题,请联系我!


1
我尝试了一下,但是当我在平板电脑上以横向模式运行应用程序时,启动任何活动时,它首先以纵向模式打开,然后再旋转到横向模式:( 是否有解决方案/变通方法??? - Mahesh B ツ

0
这是一个使用资源和尺寸限定符的好方法。
将此布尔资源放在res/values中,命名为bools.xml或其他任何名称(文件名在此处不重要):
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="portrait_only">true</bool>
</resources>

将这个放在res/values-sw600dp和res/values-xlarge文件夹中:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="portrait_only">false</bool>
</resources>

请参考这个补充答案,了解如何在Android Studio中添加这些目录和文件。
然后,在您的活动的onCreate方法中,您可以这样做:
if(getResources().getBoolean(R.bool.portrait_only)){
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

在最小宽度方向上超过 600 dp 的设备,或者在 Android 3.2 之前的设备(基本上是平板电脑)上为 x-large 的设备将像正常情况一样运行,根据传感器和用户锁定旋转等。其他所有设备(基本上是手机)只能以纵向模式使用。

来源:原始答案


嗨,乔纳森,非常感谢你的回答。我尝试了你建议的解决方案,但我描述的问题仍然存在。我将我的项目推送到了一个Git存储库中。如果您愿意,请检查我的编辑。 - JJack_
嗨,JJack,我有空的时候会看一下!希望你能尽快得到答案。祝你好运! - Jonathan Aste

-3
在清单文件的所有活动标签中添加此行...
 android:screenOrientation="portrait"

不是一个选项,因为在平板电脑横向模式下会出现相同的问题。 - Huby

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