Android: allow portrait and landscape for tablets, but force portrait on phone?

蹲街弑〆低调 提交于 2019-12-17 01:33:29

问题


I would like tablets to be able to display in portrait and landscape (sw600dp or greater), but phones to be restricted to portrait only. I can't find any way to conditionally choose an orientation. Any suggestions?


回答1:


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.




回答2:


You can try this way first get the screen size of the device

if ((getResources().getConfiguration().screenLayout &      Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE) {     
    Toast.makeText(this, "Large screen",Toast.LENGTH_LONG).show();

}
else if ((getResources().getConfiguration().screenLayout &      Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_NORMAL) {     
    Toast.makeText(this, "Normal sized screen" , Toast.LENGTH_LONG).show();

} 
else if ((getResources().getConfiguration().screenLayout &      Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_SMALL) {     
    Toast.makeText(this, "Small sized screen" , Toast.LENGTH_LONG).show();
}
else {
    Toast.makeText(this, "Screen size is neither large, normal or small" , Toast.LENGTH_LONG).show();
}

and then set orientation according to that

setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);



回答3:


Here's how I did it (inspired by http://androidblogger.blogspot.com/2011/08/orientation-for-both-phones-and-tablets.html ):

In AndroidManifest.xml , for each activity you want to be able to change between portrait and landscape (make sure you add screenSize - you didn't used to need this!) You don't need to set a screen orientation here. :

android:configChanges="keyboardHidden|orientation|screenSize"

Methods to add in each Activity:

public static boolean isXLargeScreen(Context context) {
    return (context.getResources().getConfiguration().screenLayout
    & Configuration.SCREENLAYOUT_SIZE_MASK)
    >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
} 

and: (if you don't override this method, the app will call onCreate() when changing orientations)

@Override
public void onConfigurationChanged (Configuration newConfig)
{       
    super.onConfigurationChanged(newConfig);

    if (!isXLargeScreen(getApplicationContext()) ) {            
        return; //keep in portrait mode if a phone      
    }

    //I set background images for landscape and portrait here
}

In onCreate() of each Activity :

if (!isXLargeScreen(getApplicationContext())) { //set phones to portrait; 
   setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);            
}
else {
  //I set background images here depending on portrait or landscape orientation 
}

The only thing I can't seem to figure out is how to to get the app to change layout files when switching from landscape to portrait or vice versa. I assume the answer is doing something similar to what the above link does, but I couldn't get that to work for me - it deleted all my data. But if you have a simple enough app that you have the same layout file for portrait and landscape, this should work.




回答4:


Supplement to the accepted answer

You can do the following steps in Android Studio to add the res/values-sw600dp and res/values-large directories with their bools.xml files.

values-sw600dp

First of all, from the Project tab select the Project (rather than Android) filter in the navigator.

Then right click the app/src/main/res directory. Choose New > Android Resource Directory.

Select Smallest Screen Width, and then press the >> button.

Type in 600 for the smallest screen width. The directory name will be automatically generated. Say OK.

Then right click on the newly created values-sw600dp file. Choose New > Values resource file. Type bools for the name.

values-large

Adding a values-large directory is only necessary if you are supporting pre Android 3.2 (API level 13). Otherwise you can skip this step. The values-large directory corresponds to values-sw600dp. (values-xlarge corresponds to values-sw720dp.)

To create the values-large directory, follow the same steps as above, but in this case choose Size rather than Smallest Screen Width. Select Large. The directory name will be automatically generated.

Right click the directory as before to create the bools.xml file.




回答5:


Following Ginny's answer, I think the most reliable way to do it is as follows:

As described here, put a boolean in resources sw600dp. It must have the prefix sw otherwise it won't work properly:

in res/values-sw600dp/dimens.xml

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

in res/values/dimens.xml

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

Then make a method to retrieve that boolean:

public class ViewUtils {
    public static boolean isTablet(Context context){
        return context.getResources().getBoolean(R.bool.isTablet);
    }
}

And a base activity to extend from the activities where you want this behaviour:

public abstract class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!ViewUtils.isTablet(this)) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
    }
}

So each activity would extend BaseActivity:

public class LoginActivity extends BaseActivity //....

Important: even if you extend from BaseActivity, you must add the line android:configChanges="orientation|screenSize" to each Activity in your AndroidManifest.xml:

    <activity
        android:name=".login.LoginActivity"
        android:configChanges="orientation|screenSize">
    </activity>



回答6:


Well, this is a bit of late but, here is an XML-Only but a hack solution which does not recreate an activity as setRequestedOrientation does if has to change orientation:

https://stackoverflow.com/a/27015879/1281930




回答7:


Old question I know. In order to run your app always in portrait mode even when orientation may be or is swapped etc (for example on tablets) I designed this function that is used to set the device in the right orientation without the need to know how the portrait and landscape features are organised on the device.

   private void initActivityScreenOrientPortrait()
    {
        // Avoid screen rotations (use the manifests android:screenOrientation setting)
        // Set this to nosensor or potrait

        // Set window fullscreen
        this.activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        DisplayMetrics metrics = new DisplayMetrics();
        this.activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);

         // Test if it is VISUAL in portrait mode by simply checking it's size
        boolean bIsVisualPortrait = ( metrics.heightPixels >= metrics.widthPixels ); 

        if( !bIsVisualPortrait )
        { 
            // Swap the orientation to match the VISUAL portrait mode
            if( this.activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT )
             { this.activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); }
            else { this.activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ); }
        }
        else { this.activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); }

    }

Works like a charm!

NOTICE: Change this.activity by your activity or add it to the main activity and remove this.activity ;-)

If you want to do the opposite, you must change the code to landscape (but I think it is clear how to this).




回答8:


Unfortunately, using the method setRequestedOrientation(...) will cause the activity to restart, so even if you call this in the onCreate method it will go through the activity lifecycle and then it will recreate the same activity in the requested orientation. So at @Brian Christensen's answer you should consider that the activity code might be called twice, this could have bad effects (not only visual, but also at network requests, analytics, etc.).

Furthermore, to set the configChanges attribute in the manifest is in my opinion a big trade-off, which could take massive refactoring cost. Android Devs are not recommending to change that attribute.

Finally, trying to set the screenOrientation somehow different (to avoid the restarting problem) is impossible, statically impossible due to the static manifest which can't be changed, programmatically it is only possible to call that method in the already started activity.

Summary: In my opinion, @Brian Christensen suggestion is the best trade-off, but be aware of the restarting activity issue.




回答9:


Other solutions didn't work for me. I still had some weird orientation problem with dialogs and recreation issues. My solution was to extend the Activity, forcing it as portrait in manifest.

Example:

public class MainActivityPhone extends MainActivity {}

manifest.xml:

        <activity
        android:screenOrientation="portrait"
        android:name=".MainActivityPhone"
        android:theme="@style/AppTheme.NoActionBar" />

in splashcreen activity:

    Intent i = null;
    boolean isTablet = getResources().getBoolean(R.bool.is_tablet);
    if (!isTablet)
        i = new Intent(this, MainActivityPhone.class);
    else
        i = new Intent(this, MainActivity.class);
    startActivity(i);


来源:https://stackoverflow.com/questions/9627774/android-allow-portrait-and-landscape-for-tablets-but-force-portrait-on-phone

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!