Android: ¿permitir vertical y horizontal para tabletas, pero forzar vertical en el teléfono?

191

Me gustaría que las tabletas se puedan mostrar en vertical y horizontal (sw600dp o superior), pero los teléfonos se restringirán solo a vertical. No puedo encontrar ninguna manera de elegir condicionalmente una orientación. ¿Alguna sugerencia?

Kenny Wyland
fuente
Una forma sería NO diseñar diseños horizontales para teléfonos, como al usar layout-landuna rescarpeta interna .
Fantasma
12
Eso solo causaría que el diseño del retrato se muestre en el paisaje. En realidad, no evitará que un teléfono gire hacia el paisaje.
radley

Respuestas:

446

Aquí hay una buena manera de usar recursos y calificadores de tamaño .

Ponga este recurso bool en res / values ​​como bools.xml o lo que sea (los nombres de archivo no importan aquí):

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

Ponga este en res / values-sw600dp y res / values-xlarge:

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

Consulte esta respuesta complementaria para obtener ayuda para agregar estos directorios y archivos en Android Studio.

Luego, en el método onCreate de sus Actividades, puede hacer esto:

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

Dispositivos que son más de 600 dp en la dirección de la anchura más pequeña, o x-grande en dispositivos pre-Android 3.2 (comprimidos, básicamente) se comportarán como normal, sobre la base de sensor y rotación de enganche de usuario, etc . Todo lo demás (teléfonos, más o menos) será solo retrato.

Brian Christensen
fuente
1
¿Necesito usar xlargetambién o puedo usar sw600dp? Probablemente no hay ninguna tableta con <3.2 en estos días.
theblang
77
Bueno, esto potencialmente reinicia la actividad cuando se inicia en horizontal, consulte developer.android.com/reference/android/app/…
Bondax
1
@Bondax Ese comentario solo se aplica "Si la actividad está actualmente en primer plano o si afecta la orientación de la pantalla", lo cual no puede ser el caso ya que estamos en onCreate en este momento.
Brian Christensen
1
@BrianChristensen Observé reiniciar una actividad al configurar la orientación solicitada en onCreate(). Cuando moví la llamada a setRequestedOrientation()una posición diferente en tiempo y contexto, el reinicio ya no sucedió.
Bondax
8
si inicio la aplicación en modo horizontal, todavía muestra el modo horizontal por un breve momento.
最 白 目
29

Puede intentar de esta manera primero obtener el tamaño de pantalla del dispositivo

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

y luego establecer la orientación de acuerdo a eso

setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Avi Kumar Manku
fuente
44
¿Qué método debo poner el método setRequestedOrientation ()? Una vez que onCreate ha comenzado, ya ha elegido la orientación que desea.
Kenny Wyland
kenny revisa esto, creo que podría ayudarte a stackoverflow.com/questions/2833474/…
Avi Kumar Manku
Veo que esta respuesta fue votada, pero no estoy seguro de que resuelva el problema. Configurationproporciona screenLayoutinformación - DPI. Mientras que la pregunta está relacionada con el tamaño de la pantalla del dispositivo físico - teléfono VS tableta. Es decir, puede tener una Configuration.SCREENLAYOUT_SIZE_NORMALy sería una tableta MDPI.
arreglado por la noche el
Me
Aditya Vyas-Lakhan
10

Suplemento a la respuesta aceptada

Puede realizar los siguientes pasos en Android Studio para agregar los directorios res/values-sw600dpy res/values-largecon sus bools.xmlarchivos.

valores-sw600dp

En primer lugar, desde la pestaña Proyecto, seleccione el filtro Proyecto (en lugar de Android) en el navegador.

ingrese la descripción de la imagen aquí

Luego haga clic derecho en el app/src/main/resdirectorio. Elija Nuevo > Directorio de recursos de Android .

Seleccione Ancho de pantalla más pequeño y luego presione el botón >> .

ingrese la descripción de la imagen aquí

Escriba 600para el ancho de pantalla más pequeño. El nombre del directorio se generará automáticamente. Decir ok.

ingrese la descripción de la imagen aquí

Luego haga clic derecho en el values-sw600dparchivo recién creado . Elija Nuevo > Archivo de recursos de valores . Escriba boolspara el nombre.

valores grandes

Agregar un values-largedirectorio solo es necesario si es compatible con Android 3.2 anterior (API nivel 13). De lo contrario, puede omitir este paso. El values-largedirectorio corresponde a values-sw600dp. ( values-xlargecorresponde a values-sw720dp)

Para crear el values-largedirectorio, siga los mismos pasos que anteriormente, pero en este caso elija Tamaño en lugar de Ancho de pantalla más pequeño. Seleccione grande . El nombre del directorio se generará automáticamente.

ingrese la descripción de la imagen aquí

Haga clic derecho en el directorio como antes para crear el bools.xmlarchivo.

Suragch
fuente
2
Avíseme si esto no funciona y lo arreglaré. Funcionó para mi. Recibí un voto negativo pero no hice ningún comentario, así que no sé qué arreglar.
Suragch
9

Así es como lo hice (inspirado en http://androidblogger.blogspot.com/2011/08/orientation-for-both-phones-and-tablets.html ):

En AndroidManifest.xml, para cada actividad que desee poder cambiar entre vertical y horizontal (asegúrese de agregar screenSize, ¡no solía necesitar esto!) No necesita establecer una orientación de pantalla aquí. :

android:configChanges="keyboardHidden|orientation|screenSize"

Métodos para agregar en cada Actividad:

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

y: (si no anula este método, la aplicación llamará a onCreate () cuando cambie de orientación)

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

En onCreate () de cada actividad:

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

Lo único que parece que no puedo entender es cómo hacer que la aplicación cambie los archivos de diseño al cambiar de horizontal a vertical o viceversa. Supongo que la respuesta es hacer algo similar a lo que hace el enlace anterior, pero no pude hacer que eso funcione para mí: eliminó todos mis datos. Pero si tiene una aplicación lo suficientemente simple como para tener el mismo archivo de diseño para retrato y paisaje, esto debería funcionar.

Ginny
fuente
2
poner diseños de paisaje en su carpeta de diseño-tierra
radley
Obtendrá una excepción si no llama a super onConfigurationChanged
RominaV
8

Siguiendo la respuesta de Ginny , creo que la forma más confiable de hacerlo es la siguiente:

Como se describe aquí , coloque un valor booleano en los recursos sw600dp. Debe tener el prefijo sw; de lo contrario, no funcionará correctamente:

en res / values-sw600dp / dimens.xml

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

en res / values ​​/ dimens.xml

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

Luego haga un método para recuperar ese booleano:

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

Y una actividad base para extender desde las actividades donde desea este comportamiento:

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

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

Entonces, cada actividad ampliaría BaseActivity:

public class LoginActivity extends BaseActivity //....

Importante : incluso si se extiende desde BaseActivity, debe agregar la línea android:configChanges="orientation|screenSize"a cada uno Activityen su AndroidManifest.xml:

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

Bueno, esto es un poco tarde, pero aquí hay una solución de pirateo solo para XML que no recrea una actividad como setRequestedOrientationsi tuviera que cambiar de orientación:

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

guness
fuente
1
No funciona, las tabletas siempre toman la orientación de los teléfonos. Aparentemente, android: screenOrientation se establece exactamente una vez para todas las actividades en el manifiesto, antes de que se apliquen los modificadores de recursos.
0101100101
2

Después de la respuesta aceptada , estoy agregando el archivo kotlin con la solución espero que ayude a alguien

Ponga este recurso bool res/valuescomo bools.xml o lo que sea (los nombres de archivo no importan aquí):

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

Pon este res/values-sw600dpy res/values-sw600dp-land:

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

Luego, agréguelo en las líneas de abajo en su actividad o fragmentación

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)
    }
}
Rahul
fuente
1

Otras soluciones no funcionaron para mí. Todavía tenía un extraño problema de orientación con diálogos y problemas de recreación. Mi solución fue extender la Actividad, forzándola como retrato en manifiesto.

Ejemplo:

public class MainActivityPhone extends MainActivity {}

manifest.xml:

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

en actividad de pantalla de bienvenida:

    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);
mengoni
fuente
0

Antigua pregunta que sé. Para ejecutar su aplicación siempre en modo vertical, incluso cuando la orientación puede o no se intercambia, etc. (por ejemplo, en tabletas), diseñé esta función que se usa para configurar el dispositivo en la orientación correcta sin la necesidad de saber cómo es vertical y horizontal Las funciones están organizadas en el dispositivo.

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

    }

¡Funciona de maravilla!

AVISO: Cambie this.activitypor su actividad o agréguelo a la actividad principal y elimine this.activity;-)

Si desea hacer lo contrario, debe cambiar el código a horizontal (pero creo que está claro cómo hacerlo).

Codebeat
fuente
0

Desafortunadamente, el uso del método setRequestedOrientation (...) hará que la actividad se reinicie, por lo que incluso si llama a esto en el método onCreate, pasará por el ciclo de vida de la actividad y luego recreará la misma actividad en la orientación solicitada. Entonces, en la respuesta de @Brian Christensen, debe considerar que el código de actividad podría llamarse dos veces, esto podría tener efectos negativos (no solo visuales, sino también en solicitudes de red, análisis, etc.).

Además, establecer el atributo configChanges en el manifiesto es, en mi opinión, una gran compensación, lo que podría tener un costo de refactorización masivo. Los desarrolladores de Android no recomiendan cambiar ese atributo .

Finalmente, tratar de configurar la pantalla La orientación de alguna manera diferente (para evitar el problema de reinicio) es imposible, estáticamente imposible debido al manifiesto estático que no se puede cambiar, programáticamente solo es posible llamar a ese método en la actividad ya iniciada.

Resumen: En mi opinión, la sugerencia de @Brian Christensen es la mejor solución de compromiso, pero tenga en cuenta el problema de la actividad de reinicio.

mathew11
fuente