Android: consentire ritratti e paesaggi per tablet, ma forzare il ritratto sul telefono?


191

Vorrei che i tablet potessero essere visualizzati in verticale e in orizzontale (sw600dp o superiore), ma i telefoni dovrebbero essere limitati al solo ritratto. Non riesco a trovare alcun modo per scegliere condizionalmente un orientamento. Eventuali suggerimenti?


Un modo sarebbe NON progettare layout orizzontali per i telefoni, come nell'uso della cartella layout-landinterna res.
Ghost

12
Ciò provocherebbe solo la visualizzazione del layout verticale in orizzontale. In realtà non impedisce a un telefono di ruotare in orizzontale.
radley,

Risposte:


446

Ecco un buon modo per utilizzare risorse e qualificatori di dimensioni .

Inserisci questa risorsa bool in res / valori come bools.xml o altro (i nomi dei file non contano qui):

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

Metti questo in res / valori-sw600dp e res / valori-xlarge:

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

Vedi questa risposta supplementare per assistenza nell'aggiunta di queste directory e file in Android Studio.

Quindi, nel metodo onCreate delle tue attività puoi farlo:

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

I dispositivi che sono più di 600 dp nella direzione della larghezza più piccola, o di dimensioni x su dispositivi pre-Android 3.2 (tablet, in pratica) si comporteranno normalmente, in base al sensore e alla rotazione bloccata dall'utente, ecc . Tutto il resto (telefoni, praticamente) sarà solo in verticale.


1
Devo usare xlargeanche io o posso semplicemente usare sw600dp? Probabilmente non ci sono tablet che eseguono <3.2 in questi giorni.
theblang,

7
Bene, questo potenzialmente riavvia l'attività quando viene avviata in modalità orizzontale, vedi developer.android.com/reference/android/app/…
Bondax

1
@Bondax Quel commento si applica solo "Se l'attività è attualmente in primo piano o influisce in altro modo sull'orientamento dello schermo", il che non può essere il caso, poiché siamo su onCreate a questo punto.
Brian Christensen,

1
@BrianChristensen Ho osservato il riavvio di un'attività durante l'impostazione dell'orientamento richiesto onCreate(). Quando ho spostato la chiamata in setRequestedOrientation()un'altra posizione nel tempo e nel contesto, il riavvio non è più avvenuto.
Bondax,

8
se avvio l'app in modalità orizzontale, mostra comunque la modalità orizzontale per un breve momento.
最 白 目

29

Puoi provare in questo modo prima di ottenere le dimensioni dello schermo 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();
}

e quindi impostare l'orientamento in base a quello

setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

4
Quale metodo devo inserire il metodo setRequestedOrientation ()? Una volta avviato onCreate, ha già scelto quale orientamento desidera.
Kenny Wyland,

Kenny controllare questo penso che potrebbe aiutare stackoverflow.com/questions/2833474/...~~V~~singular~~2nd
Avi Kumar Manku

Vedo che questa risposta è stata annullata, ma non sono sicuro che risolva il problema. Configurationfornisce screenLayoutinformazioni - DPI. Considerando che la domanda è relativa alle dimensioni dello schermo del dispositivo fisico - telefono VS tablet. Significa che puoi avere un Configuration.SCREENLAYOUT_SIZE_NORMALe sarebbe un tablet MDPI.
risolto il problema la notte del


10

Supplemento alla risposta accettata

Puoi eseguire le seguenti operazioni in Android Studio per aggiungere le directory res/values-sw600dpe res/values-largecon le lorobools.xml file.

Valori-sw600dp

Prima di tutto, dalla scheda Progetto selezionare il filtro Progetto (anziché Android) nel navigatore.

inserisci qui la descrizione dell'immagine

Quindi fare clic con il tasto destro sulla app/src/main/resdirectory. Scegli Nuovo > Directory risorse Android .

Seleziona Larghezza schermo più piccola , quindi premi il pulsante >> .

inserisci qui la descrizione dell'immagine

Digitare 600per la larghezza dello schermo più piccola. Il nome della directory verrà generato automaticamente. Dì OK.

inserisci qui la descrizione dell'immagine

Quindi fare clic con il tasto destro sul values-sw600dpfile appena creato . Scegli Nuovo > File delle risorse dei valori . Digitare boolsper il nome.

Valori-grandi

L'aggiunta di una values-largedirectory è necessaria solo se si supporta pre Android 3.2 (livello API 13). Altrimenti puoi saltare questo passaggio. La values-largedirectory corrisponde a values-sw600dp. ( values-xlargecorrisponde avalues-sw720dp .)

Per creare la values-largedirectory, segui gli stessi passaggi precedenti, ma in questo caso scegli Dimensioni anziché Larghezza schermo più piccola. Seleziona Grande . Il nome della directory verrà generato automaticamente.

inserisci qui la descrizione dell'immagine

Fare clic con il tasto destro sulla directory come prima per creare il bools.xmlfile.


2
Fammi sapere se questo non funziona e lo riparerò. Ha funzionato per me. Ho ricevuto un downvote ma nessun commento, quindi non so cosa risolvere.
Suragch,

9

Ecco come l'ho fatto (ispirato a http://androidblogger.blogspot.com/2011/08/orientation-for-both-phones-and-tablets.html ):

In AndroidManifest.xml, per ogni attività che vuoi poter cambiare tra ritratto e paesaggio (assicurati di aggiungere screenSize - non ne avevi bisogno!) Non devi impostare un orientamento dello schermo qui. :

android:configChanges="keyboardHidden|orientation|screenSize"

Metodi da aggiungere in ogni attività:

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

e: (se non si ignora questo metodo, l'app chiamerà onCreate () quando si cambiano gli orientamenti)

@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 () di ogni attività:

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

L'unica cosa che non riesco a capire è come far sì che l'app cambi i file di layout quando si passa da orizzontale a verticale o viceversa. Suppongo che la risposta stia facendo qualcosa di simile a quello che fa il link sopra, ma non sono riuscito a farlo funzionare per me: ha eliminato tutti i miei dati. Ma se hai un'app abbastanza semplice da avere lo stesso file di layout per verticale e orizzontale, questo dovrebbe funzionare.


2
metti layout orizzontali nella cartella layout-land
radley

Otterrai un'eccezione se non chiami super onConfigurationChanged
RominaV

8

Seguendo la risposta di Ginny , penso che il modo più affidabile per farlo sia il seguente:

Come descritto qui , inserisci un valore booleano nelle risorse sw600dp. Deve avere il prefisso sw altrimenti non funzionerà correttamente:

in res / valori-sw600dp / dimens.xml

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

in res / valori / dimens.xml

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

Quindi crea un metodo per recuperare quel booleano:

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

E un'attività di base che si estende dalle attività in cui si desidera questo comportamento:

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

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

Quindi ogni attività estenderebbe BaseActivity:

public class LoginActivity extends BaseActivity //....

Importante : anche se estendi da BaseActivity, devi aggiungere la linea android:configChanges="orientation|screenSize"a ciascuno Activitynel tuo AndroidManifest.xml:

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

5

Bene, questo è un po 'tardi, ma qui c'è solo una soluzione XML ma una soluzione di hacking che non ricrea un'attività come setRequestedOrientationse dovesse cambiare orientamento:

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


1
Non funziona, i tablet prendono sempre l'orientamento dei telefoni. Apparentemente android: screenOrientation viene impostato esattamente una volta per tutte le attività nel manifest, prima che vengano applicati eventuali modificatori di risorse.
0101100101,

2

Dopo la risposta accettata , sto aggiungendo il file kotlin con la soluzione spero che possa aiutare qualcuno

Inserisci questa risorsa bool in res/valuescome bools.xml o altro (i nomi dei file non contano qui):

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

Metti questo res/values-sw600dpe res/values-sw600dp-land:

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

Quindi, aggiungilo nelle righe seguenti nella tua attività o fragmentvity

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

1

Altre soluzioni non hanno funzionato per me. Avevo ancora qualche strano problema di orientamento con dialoghi e problemi ricreativi. La mia soluzione era quella di estendere l'attività, forzandola come ritratto in manifest.

Esempio:

public class MainActivityPhone extends MainActivity {}

manifest.xml:

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

in attività splash screen:

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

0

Vecchia domanda che conosco. Al fine di eseguire la tua app sempre in modalità verticale anche quando l'orientamento può essere o viene scambiato ecc. (Ad esempio su tablet) ho progettato questa funzione che viene utilizzata per impostare il dispositivo con l'orientamento corretto senza la necessità di sapere come ritratto e paesaggio le funzionalità sono organizzate sul 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); }

    }

Funziona come un fascino!

AVVISO: modifica in this.activitybase alla tua attività o aggiungilo all'attività principale e rimuovi this.activity;-)

Se vuoi fare il contrario, devi cambiare il codice in orizzontale (ma penso che sia chiaro come farlo).


0

Sfortunatamente, usando il metodo setRequestedOrientation (...) farà ripartire l'attività, quindi anche se lo chiami nel metodo onCreate passerà attraverso il ciclo di vita delle attività e quindi ricrea la stessa attività nell'orientamento richiesto. Quindi alla risposta di @Brian Christensen dovresti considerare che il codice di attività potrebbe essere chiamato due volte, questo potrebbe avere effetti negativi (non solo visivi, ma anche su richieste di rete, analisi, ecc.).

Inoltre, impostare l'attributo configChanges nel manifest è a mio avviso un grande compromesso, che potrebbe richiedere enormi costi di refactoring. Gli sviluppatori Android non consigliano di modificare tale attributo .

Infine, provare a impostare ScreenOrientation in qualche modo diverso (per evitare il problema del riavvio) è impossibile, staticamente impossibile a causa del manifest statico che non può essere modificato, a livello di programmazione è possibile chiamare quel metodo solo nell'attività già avviata.

Riepilogo: a mio avviso, il suggerimento di @Brian Christensen è il miglior compromesso, ma sii consapevole del problema relativo al riavvio dell'attività.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.