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 Fragments 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 celular, pode ser apropriado exibir somente um Fragment por vez para uma interface de 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 Fragments lado a lado.

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

A classe FragmentManager fornece métodos que permitem a adição, a remoção e a substituição de Fragments em uma atividade em tempo de execução para criar uma experiência dinâmica.

Para saber mais sobre como implementar Fragments, consulte os recursos a seguir.

Adicionar um Fragment a uma atividade em tempo de execução

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

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

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

Uma regra importante ao lidar com Fragments — especialmente ao adicioná-los em tempo de execução — é que o layout da atividade deve incluir uma View de contêiner em que você possa inserir o Fragment.

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

Observe que 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, e esse layout é usado quando a tela do dispositivo é menor que grande porque a tela não exibe dois Fragments 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 obter um FragmentManager usando as APIs da Biblioteca de suporte. Em seguida, chame beginTransaction() para criar uma FragmentTransaction e add() para adicionar um Fragment.

Você pode realizar diversas transações de Fragments para a atividade usando a mesma FragmentTransaction. Quando estiver pronto para fazer as mudanças, você deverá chamar commit().

Por exemplo, veja como adicionar um Fragment 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();
        }
    }
}

Em virtude de o Fragment ter sido adicionado ao contêiner FrameLayout em tempo de execução — em vez de ser definido no layout da atividade com um elemento <fragment> — a atividade pode remover o Fragment e substituí-lo por um diferente.

Substituir um Fragment por outro

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

Tenha em mente que, ao realizar transações de Fragment, como substituição ou remoção, é apropriado permitir que o usuário retorne e “desfaça” a mudança. Para permitir que o usuário retorne em transações de Fragment, você precisa chamar addToBackStack() antes de acionar a FragmentTransaction.

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

Exemplo de substituição de um Fragment 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 transações de Fragment avançadas usando as APIs FragmentManager.BackStackEntry.