Activity orientation changes automatically on Andr

2019-02-09 15:07发布

问题:

I'm developing a mobile application based on Android with minSdkVersion=15. I would like to support both orientations for tablets and only portrait for smartphones. Everything works like a charm but I'm experiencing a little bug that is driving me crazy. When smartphone is in landscape mode and I try to trigger a new Activity, it opens in landscape mode for a while and then autorotates to portrait. Each one of my activities extend a GeneralActivity class:

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);
        }
    }
}

I detect tablets with this function:

public class Helper {
    public static boolean isTablet(Context context){
        Configuration config = context.getResources().getConfiguration()
        return config.smallestScreenWidthDp >= 600;
    }
}

I choose not to specify android:screenOrientation inside Manifest.xml because in that way I'm able to support all interface orientation for tablets. Am I missing something?

EDIT

I decided to apply the best practice suggested in the answer by Jonathan, but the issue I described is still here. Here's my repo on github: https://github.com/giacmarangoni/Android-Orientation-Test

回答1:

I had the same problem on Android N & O and found a solution.

I still use setRequestedOrientation in the onCreate method, but I've added this for every activity in the manifest:

android:screenOrientation="behind"

This makes sure that the activity launches with the same orientation as the previous activity. The setRequestedOrientation afterwards will override it, but if it's the same as the previous activity you don't see anything change as the user.



回答2:

I found the right way to solve this issue by using:

android:screenOrientation="locked"

on every activity declared in manifest with this issue.

And keep using setRequestedOrientation() programatically to define if landscape or portrait orientation within onCreate() method,

Btw, in your GitHub project, I just changed the manifest adding locked orientation like this and it finally works:

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

Hope it helps you too :)



回答3:

Here's a good way using resources and size qualifiers.

Put this bool resource in res/values as bools.xml or whatever (file names don't matter here):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="portrait_only">true</bool>
</resources>

Put this one in res/values-sw600dp and res/values-xlarge:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="portrait_only">false</bool>
</resources>

See this supplemental answer for help adding these directories and files in Android Studio.

Then, in the onCreate method of your Activities you can do this:

if(getResources().getBoolean(R.bool.portrait_only)){
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

Devices that are more than 600 dp in the smallest width direction, or x-large on pre-Android 3.2 devices (tablets, basically) will behave like normal, based on sensor and user-locked rotation, etc. Everything else (phones, pretty much) will be portrait only.

Source: Original answer



回答4:

I have this solution working for me. In your manifest declare screen orientations portrait

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

Then in your base activity in your onCreate before you call super.onCreate if the device is a tablet set the orientation to unspecified.

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

From the docs:

Unspecified: No preference specified: let the system decide the best orientation. This will either be the orientation selected by the activity below, or the user's preferred orientation if this activity is the bottom of a task. If the user explicitly turned off sensor based orientation through settings sensor based device rotation will be ignored. If not by default sensor based orientation will be taken into account and the orientation will changed based on how the user rotates the device.

This worked for me and so far i haven't found any drawbacks. I have only tested this in emulators and it doesn't seem that the app is starting in portrait and then rotating like it happened with the other solutions on phones. Anyway, i'd prefer phones to be working better than tablets just because they're used more.

If you find anything not working with this solution hit me up!



回答5:

I also faced same problem for Android N devices. So to support both orientations for tablets and only portrait for phones I did the following trick:

  1. Set portrait to each activity in Manifest. Looks bad, but in this case screen will not autorotate: android:screenOrientation="portrait"

  2. In your BaseActivity set:

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

In your base activity you can put something like this:

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

And in your activities in the 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:

Add this line in all activity tags in menifest file...

 android:screenOrientation="portrait"