我希望平板电脑能够显示肖像和景观(sw600dp或更大),但手机仅限于肖像。我找不到任何有条件地选择方向的方法。有什么建议吗?


当前回答

你应该在你的manifest中声明你的屏幕朝向,值为'nosensor':

方向的确定不需要参考物理方向传感器。传感器被忽略,所以显示器不会根据用户移动设备的方式旋转。

然后旋转将是“自然的”设备行为(手机:纵向/平板电脑:横向)。

优点是你可以混合每个活动的不同旋转,例如,如果你有一个显示数据库记录的活动,那么你可以将这个活动的方向设置为“fullUser”,而其他活动则使用“nosensor”。"fullUser"活动将根据用户选择旋转(启用旋转:使用传感器,否则使用用户偏好)。

<activity android:name=".scanner.ScannerPropertiesActivity" 
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/title_activity_scanner_properties"
android:screenOrientation="${screenOrientation}"/>

${screenOrientation}是一个manifest占位符,所以我可以很容易地切换所有的活动,我希望测试之间的所有取向行为使用这个:

//module's build.gradle
defaultConfig {
//...
    manifestPlaceholders = [applicationName:appName,screenOrientation:'nosensor']
//...
}

如果你需要更复杂的功能(例如:只允许平板电脑屏幕旋转,并将手机方向固定为竖屏),那么你应该使用locked:

android: screenOrientation = "锁定" 我注意到,如果你强迫方向为纵向,而传感器发送的手机是在横向旋转,你的活动将开始在横向,然后旋转到纵向一旦你的代码被执行,几乎任何其他方向。 锁定保持以前的旋转,所以你可能已经锁定在纵向从你的上一个活动。那么你必须知道什么时候一个设备是平板电脑或手机:为了做到这一点,我建议你使用这个:

//normal tablet_detection file content:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="isTablet" type="bool">false</item>
</resources>
//sw600dp tablet_detection file content:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="isTablet" type="bool">true</item>
</resources>
//Now in your code you can detect if it's a tablet or not by calling following function in your Activity's **onCreate** and apply screen orientation by code:
protected  void updateScreenOrientation(){
boolean isTablet =context.getResources().getBoolean(R.bool.isTablet);
//And select the orientation you wish to set:
int defaultOrientation=isTablet? ActivityInfo.SCREEN_ORIENTATION_FULL_USER:ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        if(getRequestedOrientation()== ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
            //Apply this only on Activities where you set the orientation to 'locked' in the manifest
            this.setRequestedOrientation(defaultOrientation);
        }
}

其他回答

建议的方法是不锁定任何窗口维度的方向,但您可以通过遵循这里的指南来实现。

在步骤中,要做的第一件事是解锁舱单上的方向

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

接下来,你可以使用Jetpack窗口管理器来确定应用程序在屏幕上有多少空间:

/** Determines whether the device has a compact screen. **/
fun compactScreen(): Boolean {
    val screenMetrics = WindowMetricsCalculator
                        .getOrCreate()
                        .computeMaximumWindowMetrics(this)
    val shortSide = min(screenMetrics.bounds.width(),
                        screenMetrics.bounds.height())
    return shortSide / resources.displayMetrics.density < 600
}

最后,当空间变小时,锁定方向,通常在手机上:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
}

以下是接受的答案,我正在添加解决方案的kotlin文件,希望它能帮助到某人

将这个bool资源放在res/values中,作为bools.xml或其他文件(文件名在这里不重要):

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

把这个放在res/values-sw600dp和res/values-sw600dp-land中:

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

然后,将其添加到您的活动或碎片的以下行中

class MyActivity : Activity() {

    @SuppressLint("SourceLockedOrientationActivity")
    override fun onCreate(savedInstanceState: Bundle?) {
        if (resources.getBoolean(R.bool.portrait_only)) {
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }
        super.onCreate(savedInstanceState)
    }

    @SuppressLint("SourceLockedOrientationActivity")
    override fun onConfigurationChanged(newConfig: Configuration) {
        if (resources.getBoolean(R.bool.portrait_only)) {
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }
        super.onConfigurationChanged(newConfig)
    }
}

根据Ginny的回答,我认为最可靠的方法如下:

如本文所述,在资源sw600dp中放入一个布尔值。它必须有前缀sw,否则它将无法正常工作:

在res / values-sw600dp / dimens.xml

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

在res /价值/ dimens.xml

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

然后创建一个方法来检索布尔值:

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

和一个base activity来扩展你想要的行为:

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

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

因此每个活动都将扩展BaseActivity:

public class LoginActivity extends BaseActivity //....

重要:即使你从BaseActivity扩展,你必须添加行android:configChanges="orientation|screenSize"到每个活动在你的AndroidManifest.xml:

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

I recently came across this requirement but didn't want to use any of these solutions. All of them have in common that they call setRequestedOrientation programmatically. The problem with this is it sets the orientation too late and causes slight UI glitches. If your device is in landscape when you launch the app it loads in landscape then rotates. This is particularly noticeable if you use a theme to create a splash screen effect as you will see the background image in the wrong orientation. This can also have a side effect on how your app is shown in the recent apps and issues with configuration change as noted in other answers comments.

需要在清单中设置正确的方向,这样系统就可以在不启动应用程序的情况下知道方向。

这里有一个解决方案(这是一个相当大的努力,但你会睡得更好)

首先将清单中的方向设置为占位符

<activity
      android:screenOrientation="${screenOrientation}"
     ...
</activity>

然后我们需要添加一个normal/tablet flavor来设置app/build.gradle中的值。(如果你已经在使用味道,可以用新的构建类型来实现)

android {
    ...
    productFlavors {
        normal {
            manifestPlaceholders = [screenOrientation: "portrait"]
        }
        tablet {
            manifestPlaceholders = [screenOrientation: "unspecified"]
        }
    }
}

现在我们需要告诉平板电脑版本,它只适用于大型设备。这可以通过添加只使用平板电脑的清单来与默认清单合并来实现。在->添加一个新的清单文件

app
 src
  tablet
   AndroidManifest.xml

下面是这个清单所需的全部内容,因为它与默认的清单合并为->

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

    <supports-screens
        android:smallScreens="false"
        android:normalScreens="false"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:requiresSmallestWidthDp="600"
        />

</manifest>

最后一个技巧是我们需要通过版本代码来区分构建,因为Playstore不能有两次带有匹配代码的上传。我们希望确保平板电脑(更严格的构建)具有更高的代码。一个简单的方法是取一个基本代码,然后平板电脑的基本* 2和普通的基本* 2 - 1。我使用CI与基本代码构建数字,但很容易在你的口味硬编码。

现在制作两种口味

 app-normal.apk/aab (v1.0.0, version code 1)
 app-tablet.apk/aab (v1.0.0, version code 2)

将它们以多个apk/aab的形式上传到Playstore,然后如果在平板电脑上下载,Playstore会提供旋转的apk,如果在手机上下载,则只提供一个纵向的apk。

注:仅适用于通过谷歌Playstore/Amazon Kindle分发

进一步阅读https://developer.android.com/google/play/publishing/multiple-apks https://developer.amazon.com/docs/fire-tablets/ft-screen-layout-and-resolution.html

以下是我的做法(灵感来自http://androidblogger.blogspot.com/2011/08/orientation-for-both-phones-and-tablets.html):

在AndroidManifest.xml中,对于每个你想要在纵向和横向之间改变的活动(确保你添加了screenSize -你不需要这个!)你不需要在这里设置屏幕方向。:

android:configChanges="keyboardHidden|orientation|screenSize"

在每个活动中添加的方法:

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

and:(如果你不重写这个方法,应用程序将在改变方向时调用onCreate())

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

在每个Activity的onCreate()中:

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

我唯一不明白的是如何让应用程序改变布局文件时,从横向到纵向或反之亦然。我假设答案是做类似于上面的链接,但我不能让它为我工作-它删除了我所有的数据。但如果你有一个足够简单的应用,你有相同的纵向和横向布局文件,这应该是可行的。