Activity 方向在 Android 上自动更改

Posted

技术标签:

【中文标题】Activity 方向在 Android 上自动更改【英文标题】:Activity orientation changes automatically on Android 【发布时间】:2017-03-21 19:01:55 【问题描述】:

我正在使用minSdkVersion=15 开发基于android 的移动应用程序。我想同时支持平板电脑的方向和智能手机的纵向。一切都像一个魅力,但我遇到了一个让我发疯的小错误。 当智能手机处于横向模式并且我尝试触发一个新的 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,因为这样我就能够支持平板电脑的所有界面方向。 我错过了什么吗?

编辑

我决定应用乔纳森回答中建议的最佳实践,但我描述的问题仍然存在。这是我在 github 上的仓库:https://github.com/giacmarangoni/Android-Orientation-Test

【问题讨论】:

嗨,我遇到了同样的问题。它只发生在牛轧糖设备上。你有运气吗? 【参考方案1】:

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

我仍然在 onCreate 方法中使用 setRequestedOrientation,但我已经为清单中的每个活动添加了这个:

android:screenOrientation="behind"

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

【讨论】:

你用的时候有没有副作用?【参考方案2】:

我找到了解决这个问题的正确方法:

android:screenOrientation="locked"

在清单中声明的​​每个活动中都有此问题。

并继续以编程方式使用setRequestedOrientation() 在 onCreate() 方法中定义是横向还是纵向,

顺便说一句,在你的 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>

希望对你也有帮助:)

【讨论】:

你是英雄。我通常不会仅仅为了表扬而发表评论,但我想向其他遇到此问题的人强调这是正确的解决方案。它应该是公认的答案,特别是因为它允许任何方向锁定逻辑与标准 setRequestedOrientation 方法一起使用。 这是最好的答案..【参考方案3】:

这是使用资源和大小限定符的好方法。

将此 bool 资源作为 bools.xml 或其他任何内容放入 res/values 中(文件名在这里无关紧要):

<?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 将表现得像正常一样,基于传感器和用户锁定的旋转等。其他一切(手机,几乎)将只是肖像。

来源:Original answer

【讨论】:

您好乔纳森,非常感谢您的回答。我尝试应用您建议的解决方案,但我描述的问题仍然存在。我将我的项目推送到 git repo 上。如果需要,请检查我的编辑。 嗨,JJack,有空我会去看看!希望你能尽快得到答复。祝你好运!【参考方案4】:

我有这个解决方案适合我。 在您的清单中声明屏幕方向纵向

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

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

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

来自文档:

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

这对我有用,到目前为止我还没有发现任何缺点。我只在模拟器中对此进行了测试,并且该应用程序似乎并没有以纵向开始,然后像手机上的其他解决方案那样旋转。无论如何,我宁愿手机比平板电脑工作得更好,因为它们使用得更多。

如果您发现任何不适用于此解决方案的内容,请联系我!

【讨论】:

我试过了,但是当我在平板电脑上以横向模式运行应用程序时,然后在启动任何 Activity 时,它首先以纵向模式打开,然后旋转到横向 :( 任何解决方案/解决方法?? ?【参考方案5】:

我在 Android N 设备上也遇到了同样的问题。因此,为了支持平板电脑的两种方向和手机的纵向,我做了以下技巧:

    为清单中的每个活动设置肖像。看起来很糟糕,但在这种情况下屏幕不会自动旋转: android:screenOrientation="portrait"

    在您的 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);
    
    

【讨论】:

【参考方案6】:

在你的基础活动中,你可以这样写:

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

    .........

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

【讨论】:

可以通过添加关于您修复的内容的解释来改进此答案。【参考方案7】:

在清单文件的所有活动标签中添加此行...

 android:screenOrientation="portrait"

【讨论】:

不是一个选项,因为那样你在横向的平板电脑上也会遇到同样的问题

以上是关于Activity 方向在 Android 上自动更改的主要内容,如果未能解决你的问题,请参考以下文章

避免Android在方向更改时自动重新添加我的片段

Android Fragment 和 Activity 在方向更改时的行为

如何改变Activity的显示方向

Xamarin Android在方向改变上保持内容

两个方向的 Android Activity 处理

Android 锁定屏幕放心