Criar uma IU flexível

Ao projetar o aplicativo para ser compatível com uma grande variedade de tamanhos de tela, você poderá reutilizar os fragmentos em diferentes configurações de layout para otimizar a experiência do usuário com base no espaço de tela disponível.

Por exemplo, em um dispositivo móvel, pode ser apropriado exibir somente um fragmento por vez para uma interface do usuário de painel simples. Por outro lado, em tablets que têm uma tela maior para exibir mais informações ao usuário, é melhor arranjar fragmentos lado a lado.

Figura 1. Dois fragmentos exibidos em diferentes configurações para a mesma atividade em diferentes tamanhos de tela. Em uma tela grande, ambos os fragmentos se encaixam lado a lado. No entanto, em um dispositivo menor, somente um fragmento se encaixa por vez. Assim, os fragmentos devem substituir-se mutuamente à medida que o usuário navega.

A classe FragmentManager oferece métodos que permitem a adição, a remoção e a substituição de fragmentos em uma atividade no momento da execução para criar uma experiência dinâmica.

Para mais informações sobre como implementar fragmentos, consulte os recursos a seguir.

Adicionar um fragmento a uma atividade no momento da execução

Em vez de definir os fragmentos para uma atividade no arquivo de layout, como mostrado na lição anterior com o elemento <fragment>, você pode adicionar um fragmento à atividade durante o tempo de execução dela. Isso é necessário se você planeja alterar fragmentos durante a vida de uma atividade.

Para realizar uma transação como adicionar ou remover um fragmento, você precisa usar o FragmentManager para criar uma FragmentTransaction, que oferece APIs para adicionar, remover, substituir e realizar outras transações de fragmentos.

Caso sua atividade permita que os fragmentos sejam removidos e substituídos, adicione os fragmentos iniciais à atividade durante o método onCreate().

Uma regra importante ao lidar com fragmentos, especialmente ao adicioná-los no momento da execução, é que o layout da atividade precisa incluir uma View de contêiner na qual você pode inserir o fragmento.

O layout a seguir é uma alternativa ao layout que exibe somente um fragmento por vez, mostrado na lição anterior. Para substituir um fragmento por outro, o layout da atividade inclui um FrameLayout vazio, que age como o contêiner do fragmento.

O nome do arquivo é o mesmo que o do arquivo de layout da lição anterior, mas o diretório de layout não tem o qualificador large. Portanto, esse layout é usado quando a tela do dispositivo é menor que grande porque ela não exibe dois fragmentos ao mesmo tempo.

res/layout/news_articles.xml:

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    

Dentro da atividade, chame getSupportFragmentManager() para conseguir um FragmentManager usando as APIs da Biblioteca de Suporte. Em seguida, chame beginTransaction() para criar uma FragmentTransaction e add() para adicionar um fragmento.

Você pode realizar várias transações de fragmentos para a atividade usando a mesma FragmentTransaction. Quando estiver pronto para realizar as mudanças, será necessário chamar commit().

Por exemplo, veja como adicionar um fragmento ao layout anterior:

Kotlin

    import android.os.Bundle
    import android.support.v4.app.FragmentActivity

    class MainActivity : FragmentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.news_articles)

            // Check that the activity is using the layout version with
            // the fragment_container FrameLayout
            if (findViewById(R.id.fragment_container) != null) {

                // However, if we're being restored from a previous state,
                // then we don't need to do anything and should return or else
                // we could end up with overlapping fragments.
                if (savedInstanceState != null) {
                    return;
                }

                // Create a new Fragment to be placed in the activity layout
                val firstFragment = HeadlinesFragment()

                // In case this activity was started with special instructions from an
                // Intent, pass the Intent's extras to the fragment as arguments
                firstFragment.arguments = intent.extras

                // Add the fragment to the 'fragment_container' FrameLayout
                supportFragmentManager.beginTransaction()
                        .add(R.id.fragment_container, firstFragment).commit()
            }
        }
    }
    

Java

    import android.os.Bundle;
    import android.support.v4.app.FragmentActivity;

    public class MainActivity extends FragmentActivity {
        @Override
        public void onCreate(Bundle savedInstanceState?) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.news_articles);

            // Check that the activity is using the layout version with
            // the fragment_container FrameLayout
            if (findViewById(R.id.fragment_container) != null) {

                // However, if we're being restored from a previous state,
                // then we don't need to do anything and should return or else
                // we could end up with overlapping fragments.
                if (savedInstanceState != null) {
                    return;
                }

                // Create a new Fragment to be placed in the activity layout
                HeadlinesFragment firstFragment = new HeadlinesFragment();

                // In case this activity was started with special instructions from an
                // Intent, pass the Intent's extras to the fragment as arguments
                firstFragment.setArguments(getIntent().getExtras());

                // Add the fragment to the 'fragment_container' FrameLayout
                getSupportFragmentManager().beginTransaction()
                        .add(R.id.fragment_container, firstFragment).commit();
            }
        }
    }
    

Como o fragmento foi adicionado ao contêiner FrameLayout no momento da execução, em vez de definido no layout da atividade com um elemento <fragment>, a atividade pode remover o fragmento e substituí-lo por um diferente.

Substituir um fragmento por outro

O procedimento para substituir um fragmento é semelhante ao de adicionar um, mas requer o método replace(), em vez de add().

Lembre-se de que, ao realizar transações de fragmento, como substituição ou remoção, é apropriado permitir que o usuário retorne e "desfaça" a alteração. Para permitir que o usuário retorne em transações de fragmento, você precisa chamar addToBackStack() antes de acionar a FragmentTransaction.

Observação: ao remover ou substituir um fragmento e adicionar a transação à pilha de retorno, o fragmento removido será pausado (não destruído). Se o usuário navegar de volta para restaurar o fragmento, ele será reiniciado. Se você não adicionar a transação à pilha de retorno, o fragmento será destruído quando for removido ou substituído.

Exemplo de substituição de um fragmento por outro:

Kotlin

    // Create fragment and give it an argument specifying the article it should show
    val newFragment = ArticleFragment()
    Bundle args = Bundle()
    args.putInt(ArticleFragment.ARG_POSITION, position)
    newFragment.arguments = args

    val transaction = supportFragmentManager.beginTransaction().apply {
      // Replace whatever is in the fragment_container view with this fragment,
      // and add the transaction to the back stack so the user can navigate back
      replace(R.id.fragment_container, newFragment)
      addToBackStack(null)
    }

    // Commit the transaction
    transaction.commit();
    

Java

    // Create fragment and give it an argument specifying the article it should show
    ArticleFragment newFragment = new ArticleFragment();
    Bundle args = new Bundle();
    args.putInt(ArticleFragment.ARG_POSITION, position);
    newFragment.setArguments(args);

    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

    // Replace whatever is in the fragment_container view with this fragment,
    // and add the transaction to the back stack so the user can navigate back
    transaction.replace(R.id.fragment_container, newFragment);
    transaction.addToBackStack(null);

    // Commit the transaction
    transaction.commit();
    

O método addToBackStack() tem um parâmetro de string opcional que especifica um nome único para a transação. O nome não é necessário a menos que você planeje realizar operações de fragmento avançadas usando as APIs FragmentManager.BackStackEntry.