【问题标题】:Android: allow portrait and landscape for tablets, but force portrait on phone?Android:允许平板电脑纵向和横向,但在手机上强制纵向?
【发布时间】:2012-03-26 12:47:35
【问题描述】:

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

【问题讨论】:

  • 一种方法是不要为手机设计横向布局,例如在 res 文件夹中使用 layout-land
  • 这只会导致纵向布局以横向显示。它实际上不会阻止手机旋转到横向。

标签: android tablet screen-orientation


【解决方案1】:

你可以试试这个方法先获取设备的屏幕尺寸

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

然后根据那个设置方向

setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

【讨论】:

  • setRequestedOrientation()方法应该放什么方法?一旦 onCreate 开始,它就已经选择了它想要的方向。
  • kenny 看看这个我想它可能对你有帮助stackoverflow.com/questions/2833474/…
  • 我看到这个答案被赞成,但我不确定它是否能解决问题。 Configuration 提供 screenLayout 信息 - DPI。而问题与物理设备屏幕尺寸有关 - 手机VS平板电脑。意思是,您可以拥有一个Configuration.SCREENLAYOUT_SIZE_NORMAL,它将是一个 MDPI 平板电脑。
【解决方案2】:

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

在 AndroidManifest.xml 中,对于您希望能够在纵向和横向之间进行更改的每个活动(确保添加 screenSize - 您以前不需要这个!)您不需要在此处设置屏幕方向. :

android:configChanges="keyboardHidden|orientation|screenSize"

在每个Activity中添加的方法:

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

并且:(如果你不重写这个方法,应用会在改变方向时调用 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 
}

我似乎唯一想不通的是如何让应用在从横向切换到纵向时更改布局文件,反之亦然。我认为答案与上面的链接所做的类似,但我无法让它为我工作——它删除了我的所有数据。但是,如果您有一个足够简单的应用程序,并且具有相同的纵向和横向布局文件,那么这应该可以工作。

【讨论】:

  • 将横向布局放入您的 layout-land 文件夹
  • 不调用super onConfigurationChanged会报异常
【解决方案3】:

这是使用resourcessize qualifiers 的好方法。

将此 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 中添加这些目录和文件的帮助,请参阅 this supplemental answer

然后,在您的活动的 onCreate 方法中,您可以这样做:

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

在最小宽度方向上超过 600 dp 的设备,或在 Android 3.2 之前的设备(基本上是平板电脑)上的 x-large 将表现得像正常,based on sensor and user-locked rotation, etc。其他一切(手机,几乎)都只是肖像。

【讨论】:

  • 我也需要使用xlarge 还是只使用sw600dp?这些天可能没有任何平板电脑运行
  • 好吧,这可能会在横向启动时重新启动活动,请参阅developer.android.com/reference/android/app/…
  • @Bondax 该评论仅适用于“如果活动当前处于前台或以其他方式影响屏幕方向”,因为我们此时处于 onCreate 中,所以情况并非如此。
  • 如果我以横向模式启动应用程序,它仍会短暂显示横向模式。
  • @dan 我通过在清单中的每个活动中添加 'android:screenOrientation="behind"' 找到了解决此问题的方法。在这里查看我的完整答案:stackoverflow.com/questions/42936007/…
【解决方案4】:

我知道的老问题。为了始终以纵向模式运行您的应用程序,即使方向可能或已交换等(例如在平板电脑上),我设计了此功能,用于将设备设置为正确的方向,而无需知道纵向和横向功能在设备上进行组织。

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

    }

像魅力一样工作!

注意: 通过您的活动更改 this.activity 或将其添加到主要活动并删除 this.activity ;-)

如果你想做相反的事情,你必须将代码改为横向(但我认为如何做到这一点很清楚)。

【讨论】:

    【解决方案5】:

    好吧,这有点晚了,但是,这是一个 XML-Only 的黑客解决方案,它不会像 setRequestedOrientation 那样重新创建活动,如果必须改变方向:

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

    【讨论】:

    • 不起作用,平板电脑总是采用手机的方向。显然,在应用任何资源修饰符之前,清单中的所有活动都只设置了一次 android:screenOrientation。
    【解决方案6】:

    补充accepted answer

    您可以在 Android Studio 中执行以下步骤,以添加 res/values-sw600dpres/values-large 目录及其 bools.xml 文件。

    值-sw600dp

    首先,从 Project 选项卡中选择导航器中的 Project(而不是 Android)过滤器。

    然后右键单击app/src/main/res 目录。选择新建 > Android资源目录

    选择最小屏幕宽度,然后按>>按钮。

    输入600 以获得最小的屏幕宽度。目录名会自动生成。说好。

    然后右键单击新创建的values-sw600dp 文件。选择新建 > 值资源文件。键入 bools 作为名称。

    值-大

    仅当您支持 Android 3.2(API 级别 13)之前的版本时,才需要添加 values-large 目录。否则你可以跳过这一步。 values-large 目录对应于values-sw600dp。 (values-xlarge 对应于values-sw720dp。)

    要创建values-large 目录,请按照与上述相同的步骤进行操作,但在这种情况下,请选择Size 而不是 Smallest Screen Width。选择。目录名会自动生成。

    和之前一样右键目录创建bools.xml文件。

    【讨论】:

      【解决方案7】:

      按照Ginny's answer,我认为最靠谱的做法如下:

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

      在 res/values-sw600dp/dimens.xml 中

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

      在 res/values/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);
          }
      }
      

      还有一个基础活动,可从您想要此行为的活动扩展:

      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

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

      【讨论】:

        【解决方案8】:

        不幸的是,使用 setRequestedOrientation(...) 方法会导致 Activity 重新启动,因此即使您在 onCreate 方法中调用它,它也会经历 Activity 生命周期,然后它会以请求的方向重新创建相同的 Activity。因此,在@Brian Christensen 的回答中,您应该考虑到活动代码可能会被调用两次,这可能会产生不良影响(不仅是视觉上的,而且在网络请求、分析等方面也是如此)。

        此外,在我看来,在清单中设置 configChanges 属性是一个很大的权衡,这可能需要大量的重构成本。 Android Devs are not recommending to change that attribute.

        最后,尝试以某种方式设置 screenOrientation 不同(以避免重新启动问题)是不可能的,由于静态清单无法更改,因此静态不可能,以编程方式只能在已经启动的活动中调用该方法.

        总结:在我看来,@Brian Christensen 的建议是最好的权衡,但请注意重新启动活动的问题。

        【讨论】:

          【解决方案9】:

          其他解决方案对我不起作用。我仍然有一些奇怪的对话和娱乐问题的方向问题。我的解决方案是扩展 Activity,强制它作为 manifest 中的肖像。

          例子:

          public class MainActivityPhone extends MainActivity {}
          

          manifest.xml:

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

          在启动画面活动中:

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

          【讨论】:

            【解决方案10】:

            接受回答之后,我正在添加带有解决方案的 kotlin 文件,希望它对某人有所帮助

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

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

            把这个放在res/values-sw600dpres/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)
                }
            }
            

            【讨论】:

              【解决方案11】:

              我最近遇到了这个要求,但不想使用这些解决方案中的任何一个。它们都有一个共同点,就是以编程方式调用setRequestedOrientation。这样做的问题是它设置的方向太晚并导致轻微的 UI 故障。如果您的设备在您启动应用程序时处于横向状态,它会以横向方式加载然后旋转。如果您使用主题创建闪屏效果,这一点尤其明显,因为您会看到背景图像的方向错误。这也可能对您的应用在最近的应用中的显示方式以及其他答案 cmets 中指出的配置更改问题产生副作用。

              结论需要在清单中设置正确的方向,以便系统在不启动您的应用的情况下知道方向。

              这里有一个解决方法(虽然很费力,但你会睡得更好)

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

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

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

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

              现在我们需要告诉平板电脑版本它仅适用于大型设备。这可以通过添加仅平板电脑清单与默认清单合并来完成。在 ->

              处添加一个新的清单文件
              app
               src
                tablet
                 AndroidManifest.xml
              

              下面是这个manifest需要的所有东西,因为它与默认的合并是->

              <?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 不能有两个具有匹配代码的上传。我们要确保平板电脑(更严格的构建)具有更高的代码。一种简单的方法是获取基本代码,然后用于平板电脑base * 2 和普通base * 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,如果在手机上下载,则只有一个肖像。

              注意:仅在通过 Google 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

              【讨论】:

              • 产品口味去哪儿了?你错过了一些上下文。
              • @wesleyfranks 添加了更多上下文。您可能需要阅读有关风味 developer.android.com/studio/build/build-variants 的内容,因为答案确实假设对它们有一些经验,因为取决于您的应用程序当前设置可能需要调整。这个概念是使用 gradle 在构建时配置清单,而不是在运行时配置的其他答案。
              • 这个解决方案对我有用:stackoverflow.com/a/60381441/7826494
              【解决方案12】:

              在清单文件中添加以下代码:

              android:screenOrientation="fullSensor"
              

              layout-land文件夹下创建XML文件

              【讨论】:

                【解决方案13】:

                我对提供的解决方案有疑问,最后这对我有用:

                1. AndroidManifest.xml 中将 Activity 的方向设置为“锁定”:
                <activity
                    android:name="com.whatever.YourActivity"
                    android:screenOrientation="locked"
                    ... />
                
                1. 在 Activity 的 OnCreate(...) 方法中,使用以下内容:
                @SuppressLint("SourceLockedOrientationActivity")
                override fun onCreate(savedInstanceState: Bundle?) {
                    if (isTablet(resources)) {
                        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
                    } else if (resources.configuration.orientation != Configuration.ORIENTATION_PORTRAIT) {
                        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                    }
                    super.onCreate(savedInstanceState)
                }
                

                请注意,您不需要在资源中使用带有布尔值的 hacky 解决方案,您可以使用 DeviceProperties 类中的 isTablet(resources) 方法,该方法除了检查 smallestScreenWidthDp &gt;= 600 之外的其他内容。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2012-03-08
                  • 2017-11-24
                  • 1970-01-01
                  • 2011-12-01
                  • 1970-01-01
                  • 2014-03-14
                  • 2018-11-08
                  • 1970-01-01
                  相关资源
                  最近更新 更多