Android: permitir retrato e paisagem para tablets, mas forçar o retrato no telefone?

191

Gostaria que os tablets pudessem ser exibidos em retrato e paisagem (sw600dp ou superior), mas os telefones devem ser restritos apenas a retrato. Não consigo encontrar uma maneira de escolher uma orientação condicionalmente. Alguma sugestão?

Kenny Wyland
fonte
Uma maneira seria NÃO projetar layouts de paisagem para telefones, como usar a pasta layout-landinterna res.
Ghost
12
Isso apenas faria com que o layout do retrato fosse exibido na paisagem. Na verdade, não impede que um telefone gire para a paisagem.
radley

Respostas:

446

Aqui está uma boa maneira de usar qualificadores de recursos e tamanho .

Coloque esse recurso bool em res / values ​​como bools.xml ou o que for (nomes de arquivos não importam aqui):

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

Coloque este em res / values-sw600dp e res / values-xlarge:

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

Veja esta resposta suplementar para obter ajuda sobre como adicionar esses diretórios e arquivos no Android Studio.

Em seguida, no método onCreate de suas atividades, você pode fazer isso:

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

Os dispositivos que são mais do que 600 dp na direcção da largura mais pequena, ou x larga em dispositivos de pré-Android 3.2 (comprimidos, basicamente) irá comportar-se como o normal, baseado no sensor e rotação bloqueado pelo utilizador, etc . Todo o resto (telefones, basicamente) será apenas retrato.

Brian Christensen
fonte
1
Preciso usar xlargetambém ou posso usar sw600dp? Provavelmente, não há tablets executando <3,2 nos dias de hoje.
theblang
7
Bem, isso potencialmente reinicia a atividade quando iniciada no modo paisagem, consulte developer.android.com/reference/android/app/…
Bondax
1
@Bondax Esse comentário se aplica apenas "Se a atividade estiver em primeiro plano ou impactando a orientação da tela", o que não pode ser o caso, pois estamos no onCreate neste momento.
18715 Brian
1
@BrianChristensen Observei o reinício de uma atividade ao definir a orientação solicitada onCreate(). Quando mudei a chamada para setRequestedOrientation()uma posição diferente no tempo e no contexto, o reinício não aconteceu mais.
Bondax #
8
se eu iniciar o aplicativo no modo paisagem, ele ainda mostrará o modo paisagem por um breve momento.
最白目
29

Você pode tentar dessa maneira primeiro obter o tamanho da tela do 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 depois defina a orientação de acordo com

setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Avi Kumar Manku
fonte
4
Que método devo colocar o método setRequestedOrientation ()? Uma vez iniciado, o onCreate já escolheu qual orientação deseja.
Kenny Wyland
Kenny verificar isso eu acho que pode ajudá-lo a stackoverflow.com/questions/2833474/...
Avi Kumar Manku
Vejo que esta resposta foi aprovada, mas não tenho certeza de que resolva o problema. Configurationfornece screenLayoutinformações - DPI. Considerando que a questão está relacionada ao tamanho da tela do dispositivo físico - telefone VS tablet. Ou seja, você pode ter um Configuration.SCREENLAYOUT_SIZE_NORMALe seria um tablet MDPI.
fixado em 15/03/16
Estou enfrentando problema stackoverflow.com/questions/42172864/…
Aditya Vyas-Lakhan,
10

Suplemento à resposta aceita

Você pode executar as seguintes etapas no Android Studio para adicionar os diretórios res/values-sw600dpe res/values-largecom seusbools.xml arquivos.

values-sw600dp

Primeiro, na guia Projeto, selecione o filtro Projeto (em vez do Android) no navegador.

insira a descrição da imagem aqui

Em seguida, clique com o botão direito do mouse no app/src/main/resdiretório Escolha Novo > Diretório de Recursos do Android .

Selecione Menor largura da tela e pressione o botão >> .

insira a descrição da imagem aqui

Digite 600para a menor largura da tela. O nome do diretório será gerado automaticamente. Diga OK.

insira a descrição da imagem aqui

Em seguida, clique com o botão direito do mouse no values-sw600dparquivo recém-criado . Escolha Novo > Arquivo de Recurso Valores . Tipobools para o nome.

valores-grandes

A adição de um values-largediretório é necessária apenas se você estiver suportando o pré-Android 3.2 (nível 13 da API). Caso contrário, você pode pular esta etapa. O values-largediretório corresponde a values-sw600dp. ( values-xlargecorresponde a values-sw720dp.)

Para criar o values-largediretório, siga as mesmas etapas acima, mas, neste caso, escolha Tamanho em vez de Menor largura da tela. Selecione grande . O nome do diretório será gerado automaticamente.

insira a descrição da imagem aqui

Clique com o botão direito do mouse no diretório como antes para criar o bools.xmlarquivo.

Suragch
fonte
2
Deixe-me saber se isso não funcionar e eu vou corrigi-lo. Funcionou para mim. Eu recebi um voto negativo, mas nenhum comentário, então não sei o que consertar.
Suragch
9

Aqui está como eu fiz isso (inspirado em http://androidblogger.blogspot.com/2011/08/orientation-for-both-phones-and-tablets.html ):

No AndroidManifest.xml, para cada atividade que você deseja alterar entre retrato e paisagem (adicione screenSize - você não costumava precisar disso!). Você não precisa definir uma orientação de tela aqui. :

android:configChanges="keyboardHidden|orientation|screenSize"

Métodos para adicionar em cada atividade:

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

e: (se você não substituir esse método, o aplicativo chamará onCreate () ao alterar as orientações)

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

Em onCreate () de cada atividade:

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

A única coisa que não consigo entender é como fazer com que o aplicativo altere os arquivos de layout ao mudar de paisagem para retrato ou vice-versa. Presumo que a resposta esteja fazendo algo semelhante ao que o link acima faz, mas não consegui que isso funcionasse para mim - ele excluiu todos os meus dados. Mas se você tiver um aplicativo simples o suficiente para ter o mesmo arquivo de layout para retrato e paisagem, isso funcionará.

Ginny
fonte
2
colocar layouts paisagem na sua pasta de layout-land
radley
Você receberá uma exceção se você não chamar super-onConfigurationChanged
RominaV
8

Seguindo a resposta de Ginny , acho que a maneira mais confiável de fazer isso é a seguinte:

Conforme descrito aqui , coloque um booleano nos recursos sw600dp. Ele deve ter o prefixo sw, caso contrário não funcionará corretamente:

em res / values-sw600dp / dimens.xml

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

em res / values ​​/ dimens.xml

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

Em seguida, crie um método para recuperar esse booleano:

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

E uma atividade base a ser estendida das atividades em que você deseja esse 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);
        }
    }
}

Portanto, cada atividade estenderia BaseActivity:

public class LoginActivity extends BaseActivity //....

Importante : mesmo que você estenda de BaseActivity, você deve adicionar a linha android:configChanges="orientation|screenSize"a cada Activityno seu AndroidManifest.xml:

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

Bem, isso é um pouco tarde, mas, aqui está uma solução Somente XML, mas uma hack, que não recria uma atividade como setRequestedOrientationfaz se for necessário alterar a orientação:

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

sem armas
fonte
1
Não funciona, os tablets sempre seguem a orientação dos telefones. Aparentemente, o android: screenOrientation é definido exatamente uma vez para todas as atividades no manifesto, antes que qualquer modificador de recurso seja aplicado.
0101100101
2

Após a resposta aceita , estou adicionando o arquivo kotlin com a solução espero que ajude alguém

Coloque esse recurso bool res/valuescomo bools.xml ou o que for (nomes de arquivos não importam aqui):

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

Coloque este aqui res/values-sw600dpe res/values-sw600dp-land:

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

Em seguida, adicione-o nas linhas abaixo da sua atividade ou fragmentidade

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
fonte
1

Outras soluções não funcionaram para mim. Eu ainda tinha algum problema de orientação estranho com diálogos e questões de recreação. Minha solução foi estender a Atividade, forçando-a como retrato em manifesto.

Exemplo:

public class MainActivityPhone extends MainActivity {}

manifest.xml:

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

na atividade de tela inicial:

    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
fonte
0

Velha pergunta eu sei. Para executar seu aplicativo sempre no modo retrato, mesmo quando a orientação pode ser ou é trocada, etc. (por exemplo, em tablets), projetei essa função que é usada para definir o dispositivo na orientação correta sem a necessidade de saber como o retrato e a paisagem recursos estão organizados no 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 como um encanto!

AVISO: Altere de acordo this.activitycom sua atividade ou adicione-o à atividade principal e remova this.activity;-)

Se você quiser fazer o oposto, deve alterar o código para paisagem (mas acho que fica claro como fazer isso).

Codebeat
fonte
0

Infelizmente, usando o método setRequestedOrientation (...) fará com que a atividade reinicie; portanto, mesmo que você chame isso no método onCreate, ele passará pelo ciclo de vida da atividade e, em seguida, recriará a mesma atividade na orientação solicitada. Portanto, na resposta de @Brian Christensen, você deve considerar que o código de atividade pode ser chamado duas vezes, isso pode ter efeitos negativos (não apenas visuais, mas também em solicitações de rede, análises, etc.).

Além disso, definir o atributo configChanges no manifesto é, na minha opinião, uma grande desvantagem, o que pode levar um enorme custo de refatoração. Os desenvolvedores do Android não recomendam alterar esse atributo .

Por fim, tentar definir a ScreenOrientation de alguma forma diferente (para evitar o problema de reinicialização) é impossível, estaticamente impossível devido ao manifesto estático que não pode ser alterado, programaticamente, só é possível chamar esse método na atividade já iniciada.

Resumo: na minha opinião, a sugestão de @Brian Christensen é a melhor opção, mas lembre-se do problema de reiniciar a atividade.

mathew11
fonte