Android: deshabilita temporalmente los cambios de orientación en una actividad

116

Mi actividad principal tiene un código que realiza algunos cambios en la base de datos que no deben interrumpirse. Estoy haciendo el trabajo pesado en otro hilo y usando un diálogo de progreso que establecí como no cancelable. Sin embargo, noté que si giro mi teléfono, se reinicia la actividad, lo cual es REALMENTE malo para el proceso que se estaba ejecutando, y obtengo un cierre forzado.

Lo que quiero hacer es deshabilitar programáticamente los cambios de orientación de la pantalla hasta que se complete mi proceso, momento en el que se habilitan los cambios de orientación.

Christopher Perry
fuente
Dado que nadie parece mencionar esta parte, querrá importar android.content.pm.ActivityInfo para usar el identificador ActivityInfo.
zsalwasser
1
Consulte: stackoverflow.com/a/32885911/2673792 para obtener la mejor solución
Sudhir Sinha

Respuestas:

165

Como explica Chris en su auto-respuesta , llamando

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

y entonces

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

realmente funciona como un encanto ... ¡en dispositivos reales!

No crea que está roto al probar en el emulador, el atajo ctrl + F11 SIEMPRE cambia la orientación de la pantalla, sin emular movimientos de sensores.

EDITAR: esta no fue la mejor respuesta posible. Como se explica en los comentarios, existen problemas con este método. La verdadera respuesta está aquí .

Kevin Gaudin
fuente
No pude localizar esas constantes. Gracias por eso.
Christopher Perry
41
Hay un problema con estos métodos ... Parece que si llamas a setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); cuando el dispositivo no está en su uso de orientación predeterminada, la orientación de la actividad se cambia inmediatamente (se destruye y se vuelve a crear) a la orientación predeterminada del dispositivo. Por ejemplo, en un teléfono, si lo sostiene en orientación horizontal, la actividad cambia a vertical y vuelve a horizontal al reactivar los sensores. El mismo problema opuesto con un Archos A5 IT: usarlo en retrato hace que la actividad cambie a paisaje y vuelva a retrato.
Kevin Gaudin
1
La respuesta real a la pregunta original está ahí: stackoverflow.com/questions/3821423/…
Kevin Gaudin
2
Esto no funcionó para mí. Sin embargo, este funcionó: stackoverflow.com/a/10488012/1369016 Tuve que llamar a setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); o setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); basado en la orientación actual recuperada de getResources (). getConfiguration (). Orientación.
Tiago
ActivityInfo.SCREEN_ORIENTATION_SENSORno respeta el bloqueo de orientación nativo de Android. Restableciendo la orientación a ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIEDhace.
tvkanters
43

Ninguna de las otras respuestas funcionó perfectamente para mí, pero esto es lo que encontré que hace.

Bloquear la orientación a la actual ...

if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

Cuando se deba volver a permitir el cambio de orientación, vuelva a establecer el valor predeterminado ...

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
Pilot_51
fuente
9
El problema con esto es que Configuration.ORIENTATION_PORTRAITse devolverá en ambos modos de paisaje (es decir, "normal" y al revés). Entonces, si el teléfono está en orientación horizontal invertida y lo configura, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPEse volteará boca abajo. En API 9, ActivityInfo introduce SCREEN_ORIENTATION_REVERSE_LANDSCAPEconstante, pero no veo una forma de detectar dicha orientación a través de la Configurationclase.
Błażej Czapp
1
Esto funcionó. La respuesta a la preocupación anterior se encuentra en esta respuesta. stackoverflow.com/a/10453034/1223436
Zack
También funcionó como un encanto para mis necesidades, gracias brillantes
user2029541
39

Aquí hay una solución más completa y actualizada que funciona para API 8+, funciona para retrato y paisaje inverso, y funciona en una pestaña Galaxy donde la orientación "natural" es horizontal (llame activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)para desbloquear la orientación):

@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
public static void lockActivityOrientation(Activity activity) {
    Display display = activity.getWindowManager().getDefaultDisplay();
    int rotation = display.getRotation();
    int height;
    int width;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2) {
        height = display.getHeight();
        width = display.getWidth();
    } else {
        Point size = new Point();
        display.getSize(size);
        height = size.y;
        width = size.x;
    }
    switch (rotation) {
    case Surface.ROTATION_90:
        if (width > height)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        break;
    case Surface.ROTATION_180:
        if (height > width)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        break;          
    case Surface.ROTATION_270:
        if (width > height)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        break;
    default :
        if (height > width)
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        else
            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
}
Roy
fuente
Me funcionó muy bien con tabletas y teléfonos.
ScruffyFox
La única respuesta correcta que funciona en todo tipo de dispositivo para mí.
amdev
¡Definitivamente la mejor respuesta! Puede hacer este método staticy agregarlo Activity activitycomo parámetro.
caw
18

Para administrar también los modos de orientación inversa, he usado ese código para corregir la orientación de la actividad:

int rotation = getWindowManager().getDefaultDisplay().getRotation();

    switch(rotation) {
    case Surface.ROTATION_180:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        break;
    case Surface.ROTATION_270:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);         
        break;
    case  Surface.ROTATION_0:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        break;
    case Surface.ROTATION_90:
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        break;
    }

Y para permitir nuevamente la orientación:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
Ivan BASART
fuente
17

Úselo setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);para bloquear la orientación actual, ya sea horizontal o vertical.

Úselo setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);para desbloquear la orientación.

Sudhir Sinha
fuente
La mejor solución para un bloqueo temporal corto. No jugar con la orientación actual del sensor.
El increíble
2
funciona en Build.VERSION.SDK_INT> = 18, tdjprog da una respuesta más completa en esta página stackoverflow.com/a/41812971/5235263
bastami82
14

Encontré la respuesta. Para hacer esto, en una Actividad puedes llamar setRequestedOrientation(int)con uno de los valores especificados aquí: http://developer.android.com/reference/android/R.attr.html#screenOrientation

Antes de iniciar mi hilo, llamé setRequestedOrientation(OFF)(OFF = sensor de nariz) y cuando terminó el hilo llamé setRequestedOrientation(ON)(ON = sensor). Funciona de maravilla.

Christopher Perry
fuente
11

Gracias a todos. Modifiqué la solución de Pilot_51 para asegurarme de restaurar al estado anterior. También agregué un cambio para admitir pantallas que no son horizontales ni verticales (pero no lo he probado en una pantalla de este tipo).

prevOrientation = getRequestedOrientation();
if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}

Luego para restaurarlo

setRequestedOrientation(prevOrientation);
ProyectoJourneyman
fuente
Cosas buenas, no estoy seguro de por qué no usaste un switchaunque.
Olvidé limpiar y cambiar a un interruptor después de agregar la tercera opción.
ProjectJourneyman
Encontré que esto funciona sin tener que obtener la configuración actual si no tiene acceso al objeto de actividad, sino solo al contexto ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
max4ever
8
protected void setLockScreenOrientation(boolean lock) {
    if (Build.VERSION.SDK_INT >= 18) {
        setRequestedOrientation(lock?ActivityInfo.SCREEN_ORIENTATION_LOCKED:ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
        return;
    }

    if (lock) {
        switch (getWindowManager().getDefaultDisplay().getRotation()) {
            case 0: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); break; // value 1
            case 2: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); break; // value 9
            case 1: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); break; // value 0
            case 3: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); break; // value 8
        }
    } else
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); // value 10
}
tdjprog
fuente
¿Podría agregar alguna explicación a su respuesta?
slfan
cuando tenga algunos trabajos en segundo plano, simplemente llame a setLockScreenOrientation (verdadero) para bloquear la orientación y evitar destruir la actividad actual para recrearla. cuando se asegure de que estos trabajos hayan finalizado, llame a setLockScreenOrientation (false).
tdjprog
2
Esta es la mejor respuesta !
Fakher
7

Aquí hay una solución que funciona todo el tiempo y conserva la orientación actual (usando Activity.Info.SCREEN_ORIENTATION_PORTRAITconjuntos a 0 ° por ejemplo, pero el usuario puede tener una orientación de 180 ° como la actual).

// Scope: Activity

private void _lockOrientation() {
    if (super.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
        super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
    } else {
        super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
    }
}

private void _unlockOrientation() {
    super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
Adrien Cadet
fuente
2
Vale la pena mencionar: API 18+ solamente
Dmitry Zaytsev
1

Úselo ActivityInfo.SCREEN_ORIENTATION_USERsi desea rotar la pantalla solo si está habilitado en el dispositivo.

Pawan Maheshwari
fuente
1

Esto funciona perfecto para mí. Resuelve el problema con la diferente "orientación natural" de la tableta / teléfono;)

int rotation = getWindowManager().getDefaultDisplay().getRotation();

        Configuration config = getResources().getConfiguration();
        int naturalOrientation;

        if (((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) &&
                config.orientation == Configuration.ORIENTATION_LANDSCAPE)
                || ((rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) &&
                config.orientation == Configuration.ORIENTATION_PORTRAIT)) {
            naturalOrientation = Configuration.ORIENTATION_LANDSCAPE;
        } else {
            naturalOrientation = Configuration.ORIENTATION_PORTRAIT;
        }

        // because getRotation() gives "rotation from natural orientation" of device (different on phone and tablet)
        // we need to update rotation variable if natural orienation isn't 0 (mainly tablets)
        if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE)
            rotation = ++rotation % 4;

        switch (rotation) {
            case Surface.ROTATION_0: //0
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                break;
            case Surface.ROTATION_90: //1
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                break;
            case Surface.ROTATION_180: //2
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                break;
            case Surface.ROTATION_270: //3
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                break;
        }
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    }
Naxos
fuente
0

Se me ocurrió una solución que depende de la rotación de la pantalla y luego decide la orientación del dispositivo. Al conocer la orientación, podemos bloquear la orientación y liberarla más tarde cuando sea necesario. Esta solución también puede determinar si el dispositivo está en modo horizontal inverso .

private void lockOrientation(){
    switch (((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation()) {


        // Portrait
        case Surface.ROTATION_0:
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            break;


        //Landscape     
        case Surface.ROTATION_90: 
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            break;


        // Reversed landscape
        case Surface.ROTATION_270:
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);             
            break;
    }
}

Luego, más tarde, si necesitamos liberar la orientación, podemos llamar a este método:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
Mansour Fahad
fuente
0

Creo que este código es más fácil de leer.

private void keepOrientation() {

    int orientation = getResources().getConfiguration().orientation;
    int rotation = getWindowManager().getDefaultDisplay().getRotation();

    switch (rotation) {
        case Surface.ROTATION_0:
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
            break;
        case Surface.ROTATION_90:
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            }
            break;
        case Surface.ROTATION_180:
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
            }
            break;
        default:
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
            }
    }
}
nor_miya
fuente
0

He descubierto que se necesita una combinación de valores de rotación / orientación existentes para cubrir las cuatro posibilidades; están los valores de retrato / paisaje y la orientación natural del dispositivo. Digamos que la orientación natural de los dispositivos tendrá un valor de rotación de 0 grados cuando la pantalla esté en su orientación vertical u horizontal "natural". De manera similar, habrá un valor de rotación de 90 grados cuando esté en horizontal o vertical (observe que es opuesto a la orientación a 0 grados). Por lo tanto, los valores de rotación que no sean 0 o 90 grados implicarán una orientación "inversa". Ok, aquí hay un código:

public enum eScreenOrientation 
{
PORTRAIT (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT),
LANDSCAPE (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE),
PORTRAIT_REVERSE (ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT),
LANDSCAPE_REVERSE (ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE),
UNSPECIFIED_ORIENTATION (ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);

    public final int activityInfoValue;

    eScreenOrientation ( int orientation )
    {
        activityInfoValue = orientation;
    }
}



public eScreenOrientation currentScreenOrientation ( )
{
    final int rotation = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();

    final int orientation = getResources().getConfiguration().orientation;
    switch ( orientation ) 
    {
    case Configuration.ORIENTATION_PORTRAIT:
        if ( rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90 )
            return eScreenOrientation.PORTRAIT;
        else
            return eScreenOrientation.PORTRAIT_REVERSE;
    case Configuration.ORIENTATION_LANDSCAPE:
        if ( rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90 )
            return eScreenOrientation.LANDSCAPE;
        else
            return eScreenOrientation.LANDSCAPE_REVERSE;
    default:
        return eScreenOrientation.UNSPECIFIED_ORIENTATION;
    }
}

public void lockScreenOrientation ( )
    throws UnsupportedDisplayException
{
    eScreenOrientation currentOrientation = currentScreenOrientation( );
    if ( currentOrientation == eScreenOrientation.UNSPECIFIED_ORIENTATION )
        throw new UnsupportedDisplayException("Unable to lock screen - unspecified orientation");
    else
        setRequestedOrientation( currentOrientation.activityInfoValue );
}

public void unlockScreenOrientation (  )
{
    setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED );
}
Pellegrin de manos mágicas
fuente
0

No me gustó la mayoría de las respuestas aquí, ya que en el desbloqueo lo configuraron como SIN ESPECIFICAR en comparación con el estado anterior. ProjectJourneyman lo tuvo en cuenta, lo cual fue genial, pero preferí el código de bloqueo de Roy. Entonces, mi recomendación sería una combinación de las dos:

private int prevOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;

private void unlockOrientation() {
    setRequestedOrientation(prevOrientation);
}

@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private void lockOrientation() {
    prevOrientation = getRequestedOrientation();
    Display display = getWindowManager().getDefaultDisplay();
    int rotation = display.getRotation();
    int height;
    int width;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2) {
        height = display.getHeight();
        width = display.getWidth();
    } else {
        Point size = new Point();
        display.getSize(size);
        height = size.y;
        width = size.x;
    }
    switch (rotation) {
        case Surface.ROTATION_90:
            if (width > height)
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            else
                setRequestedOrientation(9/* reversePortait */);
            break;
        case Surface.ROTATION_180:
            if (height > width)
                setRequestedOrientation(9/* reversePortait */);
            else
                setRequestedOrientation(8/* reverseLandscape */);
            break;
        case Surface.ROTATION_270:
            if (width > height)
                setRequestedOrientation(8/* reverseLandscape */);
            else
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            break;
        default :
            if (height > width)
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            else
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
}
Gadzair
fuente
0

Puedes usar

public void swapOrientaionLockState(){
    try{
        if (Settings.System.getInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION) == 1) {
            Display defaultDisplay = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.USER_ROTATION, defaultDisplay.getRotation());
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0);
        } else {
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 1);
        }

        Settings.System.putInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, !orientationIsLocked() ? 1 : 0);

    } catch (Settings.SettingNotFoundException e){
        e.printStackTrace();
    }
}

public boolean orientationIsLocked(){
    if(canModifiSetting(mContext)){
        try {
            return Settings.System.getInt(mContext.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION) == 0;
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }
    }
    return false;
}

public static boolean canModifiSetting(Context context){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        return Settings.System.canWrite(context);
    } else {
        return true;
    }
}
moviehay0032
fuente
-1

usa esa línea de código

this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);  

en nuestro método de actividad oncreate

Zeeshan Akhter
fuente