Layouts adaptáveis

1. Antes de começar

Existem dispositivos Android de várias formas, tamanhos e formatos. Projete o app para rodar nesses diferentes tipos de dispositivos, desde os de tela pequena até os de tela maior. Os desenvolvedores que criam apps prontos para produção podem oferecer suporte ao Android Wear, ao Android Auto e ao Android TV, mas esses tópicos estão fora do escopo deste curso. Quando seu app oferece suporte a uma maior variedade de telas, ele pode ser usado por mais usuários com dispositivos diferentes.

O app precisa ter um layout flexível. Em vez de definir o layout com dimensões rígidas para uma determinada proporção e tamanho da tela, o layout precisa ser adaptável a vários tamanhos e orientações. O mesmo princípio se aplica quando o app é executado em um dispositivo dobrável, no qual o tamanho e a proporção da tela podem mudar. Ao final deste codelab, você vai ver uma breve introdução aos dispositivos dobráveis.

aecb59fc49fb4abf.png

Pré-requisitos

  • Como fazer o download e executar o código no Android Studio.
  • Estar familiarizado com os componentes da arquitetura do Android, ViewModel e LiveData.
  • Noções básicas sobre os componentes de navegação.

O que você vai aprender

  • Como adicionar o SlidingPaneLayout ao app.

O que você vai criar

  • Atualizar o app "Sports" para se adaptar a telas grandes.

Pré-requisitos

  • Um computador com o Android Studio instalado.
  • O código inicial do app Sports.

Fazer o download do código inicial para este codelab

Este codelab oferece um código inicial para você estender com os recursos ensinados. O código inicial pode conter um código que você já conheceu em codelabs anteriores e também trechos novos que serão apresentados nos próximos codelabs.

Para fazer o download do código deste codelab no GitHub e abrir no Android Studio, siga as etapas abaixo.

  1. Inicie o Android Studio.
  2. Na janela Welcome to Android Studio, clique em Get from VCS.

61c42d01719e5b6d.png

  1. Na caixa de diálogo Get from Version Control, verifique se a opção Git está selecionada no menu Version control.

9284cfbe17219bbb.png

  1. Cole o URL do código fornecido na caixa URL.
  2. Você pode mudar o campo Directory para algo diferente do padrão sugerido, se preferir.

5ddca7dd0d914255.png

  1. Clique em Clone. O Android Studio começará a buscar o código.
  2. Aguarde o Android Studio abrir.
  3. Selecione o módulo correto para o código inicial, o app ou a solução do codelab.

2919fe3e0c79d762.png

  1. Clique no botão Run 8de56cba7583251f.png para criar e executar o código.

2. Assistir ao vídeo de orientações (opcional)

Se você quiser acompanhar a conclusão deste codelab por um dos instrutores do curso, assista ao vídeo abaixo.

Recomendamos abrir o vídeo em tela cheia usando o ícone Símbolo com quatro cantos destacados em um quadrado para indicar o modo de tela cheia. no canto inferior direito da reprodução para ter uma visualização melhor do Android Studio e do código.

Esta etapa é opcional. Você pode pular o vídeo e começar a seguir as instruções do codelab.

3. Visão geral do app inicial

O app Sports consiste em duas telas. A primeira tela exibe a lista de esportes. O usuário pode selecionar um item de um esporte específico, em seguida, a segunda tela vai ser exibida. A segunda tela é uma tela de detalhes que exibe as notícias relacionadas ao esporte selecionado. A tela de detalhes exibe o texto marcador de posição para simplificar a implementação.

Tutorial em etapas do código inicial

O código inicial que foi transferido por download tem os layouts da tela de lista e da tela de detalhes pré-criados para você. Nesse programa de aprendizagem, você vai se concentrar apenas em adaptar o app a telas grandes. Você vai usar o SlidingPaneLayout para aproveitar a tela grande. Confira abaixo um breve tutorial sobre alguns dos arquivos para começar.

fragment_sports_list.xml

  • Abra res/layout/fragment_sports_list.xml na visualização Design.
  • Esse arquivo contém o layout da primeira tela do app, que é a lista de esportes.
  • Esse layout consiste em uma Recyclerview que mostra uma lista de notícias esportivas.

f50d3e7b41fcb338.png

d9af155f87ddbcdf.png

sports_list_item.xml

  • Abra res/layout/sports_list_item.xml na visualização Design.
  • Ele contém o layout de cada item na Recyclerview.
  • Esse layout tem uma imagem de miniatura do esporte, o título "News" (Notícias) e um texto marcador de posição para uma breve notícia sobre esportes.

b19fd0e779c1d7c3.png

fragment_sports_news.xml

  • Abra res/layout/fragment_sports_news.xml na visualização Design.
  • Esse arquivo contém o layout da segunda tela do app. A tela é exibida quando o usuário seleciona um esporte na Recyclerview.
  • Esse layout inclui um banner da imagem do esporte e um texto marcador de posição para as notícias relacionadas.

c2073b1752342d97.png

main_activity.xml e content_main.xml

Esses dois elementos definem o layout da atividade principal com um único fragmento.

O gráfico de navegação contém dois destinos, um para listas de esportes e outro para notícias esportivas.

Pasta res/values

Você já conhece os arquivos de recurso nesta pasta.

  • O arquivo colors.xml contém as cores de tema usadas no app.
  • O arquivo strings.xml contém todas as strings que o app precisa.
  • O arquivo themes.xml contém a personalização da IU para o app.

MainActivity.kt

Esse é o arquivo do código padrão gerado pelo modelo para definir a visualização do conteúdo da atividade como main_activity.xml. O método onSupportNavigateUp() foi substituído para processar a navegação padrão "Para cima" na barra de apps.

model/Sport.kt

Essa é uma classe de dados que define os dados a serem exibidos em cada linha da lista de esportes da Recyclerview.

data/SportsData.kt

Esse arquivo contém uma função chamada getSportsData(), que retorna uma ArrayList pré-preenchida com dados de esporte fixados no código.

SportsViewModel.kt

Este é o ViewModel compartilhado do app. O ViewModel é compartilhado pelo SportsListFragment, que é a primeira tela com a lista de esportes e o NewsDetailsFragment, que é a segunda com notícias detalhadas sobre esportes.

  • A propriedade _currentSport é do tipo MutableLiveData, que armazena o esporte atual selecionado pelo usuário. A propriedade currentSport é a propriedade de apoio para _currentSport e é exposta como a versão pública somente leitura de outras classes.
  • A propriedade _sportsData contém a lista de dados sobre esportes. Semelhante à propriedade anterior, sportsData é a versão pública somente leitura dessa propriedade.
  • O bloco init{} do inicializador inicializa _currentSport e _sportsData. O _sportsData é inicializado com toda a lista de esportes do data/SportsData.kt. O _currentSport é inicializado com o primeiro item da lista.
  • A função updateCurrentSport() usa uma instância de Sports e atualiza _currentSport com o valor transmitido.

SportsAdapter.kt

Esse é o adaptador para a RecyclerView. No construtor, o listener de clique é transmitido. A maior parte do código neste arquivo é código boilerplate que você já conheceu em codelabs anteriores.

SportsListFragment.kt

Esse é o primeiro fragmento de tela, em que a lista de esportes é exibida.

  • A função onCreateView() infla o XML do layout fragment_sports_list usando o objeto de vinculação.
  • A função onViewCreated() configura o adaptador RecyclerView. Ela atualiza o esporte selecionado pelo usuário como o esporte atual no ViewModel compartilhado, o SportsViewModel. Ela navega até a tela de detalhes com notícias esportivas e envia a lista de esportes para o adaptador que será mostrado usando submitList(List).

NewsDetailsFragment.kt

Essa é a segunda tela do app, onde é exibido o texto marcador de posição para as notícias esportivas.

  • A função onCreateView() infla o XML do layout fragment_sports_news usando o objeto de vinculação.
  • A função onViewCreated() anexa um observador na propriedade currentSport do SportsViewModel para atualizar a interface automaticamente quando os dados mudam. No observador, o título, a imagem e as notícias dos esportes são atualizados.

Compilar e executar o aplicativo

  1. Crie e execute o app em um emulador ou dispositivo. Selecione qualquer item na lista de esportes e o app navegará para a segunda tela com o texto marcador de posição das notícias.

4. Padrão de detalhes de lista

O app inicial atual não aproveita ao máximo o espaço da tela em dispositivos maiores, como tablets. Para resolver esse problema, você vai exibir a IU do app usando o padrão de detalhes de lista, que vai aprender neste codelab.

Executar o app em um tablet

Nesta tarefa, você vai criar um emulador com um perfil para tablet. Depois de criar o emulador, você vai executar o código inicial do app de esportes e observará a IU.

  1. No Android Studio, acesse Tools > AVD Manager.
  2. A janela Android Virtual Device Manager vai ser exibida. Clique em + Create New Virtual Device... que é mostrado na parte de baixo.
  3. A janela Virtual Device Configuration é exibida. Aqui, você vai configurar o hardware e o SO do emulador. Clique em Tablet no painel à esquerda. Selecione Pixel C ou qualquer outro perfil de hardware semelhante no painel do meio.

8303f9b3e70321eb.png

  1. Clique em Next.
  2. Selecione a imagem mais recente do sistema; no momento da criação deste codelab é R (nível 30 da API).
  3. Clique em Next.
  4. É possível renomear o dispositivo virtual agora. Isso é opcional.
  5. Clique em Finish.
  6. Você vai voltar à janela Android Virtual Device Manager. Clique no ícone de inicialização 38752506de85d293.png ao lado do dispositivo virtual recém-criado.
  7. O emulador com perfil para tablet será iniciado. Isso pode levar algum tempo.
  8. Feche a janela do Android Virtual Device Manager.
  9. Execute o app de esportes no emulador recém-criado.

200e209de7a2f0ad.png

Observe que em dispositivos grandes, o app não usa a tela inteira. Os detalhes e listas são mais eficientes em uma tela grande do que uma lista. Um padrão de detalhes do item, também conhecido como padrão mestre, mostra uma lista de itens em um lado do layout e os detalhes ao lado dele quando você toca em um item. Normalmente, essas visualizações são exibidas somente em telas grandes, como tablets, porque elas têm mais espaço para exibir mais conteúdo.

As imagens abaixo são um exemplo de padrão de detalhe e listas:

71698910dd129a91.png

Os padrões de detalhes e listas acima mostram uma lista de itens à esquerda e detalhes do item selecionado à direita.

Da mesma forma, se você usar o padrão acima no app de esportes, o fragmento de notícias vai ser a tela de detalhes.

51c9542717d2f875.png

Neste codelab, você vai aprender a implementar a interface de detalhes e listas usando SlidingPaneLayout.

5. Padrão SlidingPaneLayout

Uma interface de detalhes e listas pode precisar se comportar de maneira diferente dependendo do tamanho da tela. Em telas grandes, há espaço suficiente para exibir a lista e os painéis de detalhes lado a lado. Clicar em um item da lista mostra os detalhes no painel de detalhes. No entanto, em telas pequenas, elas aparecem agrupadas. Em vez de apresentar os dois painéis de uma vez, é melhor os mostrar um de cada vez. Inicialmente, o painel de lista preenche a tela. Tocar em um item substitui o painel da lista pelo painel de detalhes desse item, que também preenche a tela.

Você vai aprender a usar um SlidingPaneLayout para gerenciar a lógica a fim de selecionar a experiência do usuário apropriada com base no tamanho da tela atual.

b0a205de3494e95d.gif

Observe como o painel de detalhes desliza sobre o painel de lista em telas menores.

Confira abaixo imagens que ilustram como o SlidingPaneLayout é mostrado em uma tela menor. Observe como o painel de detalhes se sobrepõe ao painel da lista quando um item da lista é selecionado. Portanto, os dois painéis estão sempre presentes.

e26f94d9579b6121.png

471b0b38d4dfa95a.png

Dessa forma, o SlidingPaneLayout oferece suporte à exibição de dois painéis lado a lado em dispositivos maiores, enquanto se adapta automaticamente para mostrar apenas um painel por vez em dispositivos menores, como smartphones.

6. Adicionar dependências de biblioteca

  1. Abra o build.gradle (Module: Sports.app)
  2. Na seção de dependencies, inclua a dependência abaixo para usar o SlidingPaneLayout no app:
dependencies {
...
    implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0-beta01"
}

7. Configurar o XML de fragmento da lista de esportes

Nesta tarefa, você converte o layout raiz de fragment_sports_list para SlidingPaneLayout. Como você já sabe, o SlidingPaneLayout oferece um layout de dois painéis horizontais para uso no nível mais alto de uma IU. Esse layout usa o primeiro painel como uma lista de conteúdo ou um navegador, subordinado a uma visualização de detalhes principal para exibir conteúdo no outro painel.

No app Sports, o primeiro painel é a RecyclerView exibindo a lista de esportes e o segundo painel exibe as notícias sobre esportes.

Adicionar o SlidingPaneLayout

  1. Abra o fragment_sports_list.xml Observe que o layout raiz é um FrameLayout.
  2. Mude FrameLayout para androidx.slidingpanelayout.widget.SlidingPaneLayout..
<androidx.slidingpanelayout.widget.SlidingPaneLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".SportsListFragment">

   <androidx.recyclerview.widget.RecyclerView...>
</androidx.slidingpanelayout.widget.SlidingPaneLayout>
  1. Adicione um atributo android:id ao SlidingPaneLayout e atribua a ele o valor @+id/sliding_pane_layout.
<androidx.slidingpanelayout.widget.SlidingPaneLayout
   ...
   android:id="@+id/sliding_pane_layout"
   ...>

Adicionar um segundo painel ao SlidingPaneLayout

Nesta tarefa, você vai adicionar um segundo filho ao SlidingPaneLayout. Ele é exibido como o painel de conteúdo à direita.

  1. Em fragment_sports_list.xml, abaixo da RecyclerView, adicione um segundo filho, androidx.fragment.app.FragmentContainerView.
  2. Adicione os atributos obrigatórios, layout_height e layout_width, à FragmentContainerView. Dê a eles o valor de match_parent. Esses valores serão atualizados mais tarde.
<androidx.fragment.app.FragmentContainerView
   android:layout_height="match_parent"
   android:layout_width="match_parent"/>
  1. Adicione um atributo android:id à FragmentContainerView e atribua a ele o valor @+id/detail_container.
android:id="@+id/detail_container"
  1. Adicione NewsDetailsFragment à FragmentContainerView usando o atributo android:name.
android:name="com.example.android.sports.NewsDetailsFragment"

Atualizar o atributo layout_width

O SlidingPaneLayout usa a largura dos dois painéis para determinar se eles vão ser mostrados lado a lado. Por exemplo, se o painel da lista for medido para ter um tamanho mínimo de 300dp e o painel de detalhes precisar de 400dp, o SlidingPaneLayout mostra automaticamente os dois painéis lado a lado, contanto que haja, pelo menos, 700dp de largura disponível.

As visualizações filhas vão se sobrepor se a largura combinada exceder a disponível no SlidingPaneLayout. Nesse caso, as visualizações filhas se expandem para preencher a largura disponível no SlidingPaneLayout.

Para determinar a largura das visualizações filhas, é necessário ter algumas informações básicas sobre as larguras da tela do dispositivo. A tabela abaixo mostra a lista de pontos de interrupção opinativos para você desenvolver e testar em layouts redimensionáveis de aplicativos. Eles foram escolhidos para equilibrar a simplicidade do layout com a flexibilidade de otimizar o app para casos únicos.

Largura

Ponto de interrupção

Representação do dispositivo

Largura compacta

< 600 dp

99,96% dos smartphones no modo retrato

Largura média

600 dp+

93,73% dos tablets no modo portraitLarge com as telas internas desdobradas no modo retrato

Largura expandida

840 dp+

97,22% dos tablets no modo landscapeLarge com as telas internas desdobradas no modo paisagem

a247a843310d061a.png

No app Sports, você quer exibir um único painel, a lista de esportes em smartphones, destinados a dispositivos com largura inferior a 600dp. Para exibir os dois painéis em tablets, a largura combinada precisa ser maior que 840dp. Você pode usar uma largura de 550dp para o primeiro elemento filho, a visualização de reciclagem, e 300dp para o segundo filho, a FragmentContainerView.

  1. Em fragment_sports_list.xml, mude a largura do layout da RecyclerView para 550dp e a da FragmentContainerView para 300dp.
<androidx.recyclerview.widget.RecyclerView
   ...
   android:layout_width="550dp"
   .../>

<androidx.fragment.app.FragmentContainerView
   ...
   android:layout_width="300dp"
   .../>
  1. Execute o app no emulador com o perfil de tablet e um emulador com o perfil do smartphone.

ad148a96d7487e66.png

Observe que dois painéis são mostrados no tablet. Você vai corrigir a largura do segundo painel no tablet na próxima etapa.

  1. Execute o app no emulador com o perfil do smartphone.

a6be6d199d2975ac.png

Adicionar layout_weight

Nesta tarefa, você vai corrigir a IU no tablet e fazer o segundo painel ocupar todo o espaço restante.

O SlidingPaneLayout permite definir como o espaço restante é dividido após a medição usando o parâmetro de layout layout_weight em visualizações filhas se elas não se sobrepuserem. Esse parâmetro se aplica somente à largura.

  1. No arquivo fragment_sports_list.xml, adicione layout_weight à FragmentContainerView e atribua o valor 1 a ela. Agora, o segundo painel se expande para preencher o espaço restante depois que o painel da lista for medido.
android:layout_weight="1"
  1. Execute o app.

ce3a93fe501ee5dc.png

Parabéns! Você adicionou SlidingPaneLayout com sucesso. Você ainda não terminou. Você precisa implementar a navegação de retorno e atualizar o segundo painel quando um item é selecionado na lista. Isso será implementado em uma tarefa futura.

8. Trocar o painel de detalhes

Execute o app no emulador com o perfil do tablet. Selecione uma opção na lista de esportes. O app navega até o painel de detalhes.

8fedee8d4837909.png

Nesta tarefa, você vai corrigir esse problema. No momento, o conteúdo do painel duplo está sendo atualizado com o esporte selecionado e, em seguida, o app navega para o NewsDetailsFragment.

  1. No arquivo SportsListFragment, na função onViewCreated(), localize as linhas abaixo que acessam a tela de detalhes.
// Navigate to the details screen
val action = SportsListFragmentDirections.actionSportsListFragmentToNewsFragment()
this.findNavController().navigate(action)
  1. Substitua as linhas acima por este código:
binding.slidingPaneLayout.openPane()

Chame openPane() no SlidingPaneLayout para trocar o primeiro painel pelo segundo. Isso não será notado se os dois painéis estiverem visíveis, como em um tablet.

  1. Execute o app no emulador de tablet e smartphone. Observe que o conteúdo do painel duplo está sendo atualizado corretamente.

b0d3c8c263be15f8.png

Na próxima tarefa, você vai adicionar ao app a navegação de retorno personalizada.

9. Adicionar navegação de retorno personalizada

Em dispositivos menores em que os painéis de lista e de detalhes se sobrepõem, garanta que o botão "Voltar" do sistema leve o usuário do painel de detalhes de volta ao painel de listas. Para fazer isso, ofereça uma navegação de retorno personalizada e conecte um OnBackPressedCallback ao estado atual do SlidingPaneLayout.

Navegação de retorno

A navegação de retorno é a forma como os usuários se movem para trás no histórico de telas que visitaram anteriormente. Todos os dispositivos Android oferecem um botão "Voltar" para esse tipo de navegação. Dependendo do dispositivo Android do usuário, esse botão pode ser um botão físico ou de software.

Navegação de retorno personalizada

O Android mantém uma backstack de destinos à medida que o usuário navega no app. Isso normalmente permite que o Android navegue corretamente para destinos anteriores quando o botão "Voltar" é pressionado. No entanto, há casos em que o app precisa implementar um comportamento de retorno próprio para oferecer a melhor experiência do usuário possível.

Por exemplo, ao usar uma WebView, como um navegador Chrome, talvez você queira modificar o comportamento padrão do botão "Voltar" para permitir que o usuário navegue pelo histórico de navegação na Web em vez das telas anteriores do app.

Da mesma forma, você precisa fornecer uma navegação de retorno personalizada para o SlidingPaneLayout e navegar do app do painel de detalhes até o painel da lista.

Implementar a navegação de retorno personalizada

Para implementar a navegação de retorno personalizada no app Sports, você precisa:

  • Definir um callback personalizado para substituir o OnBackPressedCallback quando o botão "Voltar" for pressionado.
  • Registrar e adicionar a instância de callback.

Primeiro, defina o callback personalizado.

  1. No arquivo SportsListFragment, adicione uma nova classe abaixo da definição da classe SportsListFragment. Dê o nome SportsListOnBackPressedCallback a ela.
  2. Transmita uma instância private do SlidingPaneLayout como um parâmetro do construtor.
class SportsListOnBackPressedCallback(
   private val slidingPaneLayout: SlidingPaneLayout
)
  1. Estenda a classe do OnBackPressedCallback. A classe OnBackPressedCallback processa callbacks onBackPressed. Você vai corrigir o erro do parâmetro do construtor em breve.
class SportsListOnBackPressedCallback(
   private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback()

O construtor de OnBackPressedCallback aceita um valor booleano para o estado inicial ativado. Somente quando um callback estiver ativado (isto é, isEnabled() retornar verdadeiro), o agente vai chamar o método handleOnBackPressed() do callback para processar o evento do botão "Voltar".

  1. Transmita slidingPaneLayout.isSlideable* && slidingPaneLayout.isOpen* como parâmetro do construtor para OnBackPressedCallback. O booleano isSlideable só será verdadeiro se o segundo painel for deslizável, ou seja, mostrado em uma tela menor, e um único painel estiver sendo exibido. O valor de isOpen será true quando o segundo painel (o painel de conteúdo) estiver completamente aberto.
class SportsListOnBackPressedCallback(
   private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen)

Esse código garante que o callback seja ativado somente nos dispositivos de tela menores e quando o painel de conteúdo estiver aberto.

  1. Para corrigir o erro sobre o método não implementado, clique na lâmpada vermelha 5fdf362480bfe665.png e selecione Implement members.
  2. Clique em "ok" no pop-up Implement members para substituir o método handleOnBackPressed.

A classe ficará assim:

class SportsListOnBackPressedCallback(
   private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen) {
   /**
    * Callback for handling the [OnBackPressedDispatcher.onBackPressed] event.
    */
   override fun handleOnBackPressed() {
       TODO("Not yet implemented")
   }
}
  1. Na função handleOnBackPressed(), exclua a instrução TODO e adicione o código abaixo para fechar o painel de conteúdo e voltar ao painel da lista.
slidingPaneLayout.closePane()

Monitorar os eventos do SlidingPaneLayout

Além de lidar com eventos de pressionar, você precisa detectar e monitorar eventos relacionados ao painel deslizante. À medida que o painel de conteúdo é deslizado, o callback precisa ser ativado ou desativado. Você vai usar o PanelSlideListener para fazer isso.

A interface SlidingPaneLayout.PanelSlideListener contém três métodos abstratos: onPanelSlide(), onPanelOpened() e onPanelClosed(). Esses métodos são chamados quando o painel de detalhes desliza, é aberto, e fechado, respectivamente.

  1. Estenda a classe SportsListOnBackPressedCallback de SlidingPaneLayout.PanelSlideListener.
  2. Para resolver o erro, implemente os três métodos. Clique na lâmpada vermelha e selecione Implement members no Android Studio.

ad52135eecbee09f.png

  1. A classe SportsListOnBackPressedCallback precisa ser semelhante a esta:
class SportsListOnBackPressedCallback(
   private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen),
  SlidingPaneLayout.PanelSlideListener{

   override fun handleOnBackPressed() {
       slidingPaneLayout.closePane()
   }

   override fun onPanelSlide(panel: View, slideOffset: Float) {
       TODO("Not yet implemented")
   }

   override fun onPanelOpened(panel: View) {
       TODO("Not yet implemented")
   }

   override fun onPanelClosed(panel: View) {
       TODO("Not yet implemented")
   }
}
  1. Remova as instruções TODO.
  2. Ative o callback OnBackPressedCallback quando o painel de detalhes estiver aberto (visível). Para isso, chame a função setEnabled() e transmita true. Coloque o código abaixo no método onPanelOpened():
setEnabled(true)
  1. O código acima pode ser simplificado usando a sintaxe de acesso a propriedades.
override fun onPanelOpened(panel: View) {
   isEnabled = true
}
  1. Da mesma forma, defina isEnabled como false quando o painel de detalhes estiver fechado.
override fun onPanelClosed(panel: View) {
   isEnabled = false
}
  1. A etapa final para concluir o callback é adicionar a classe de listener SportsListOnBackPressedCallback à lista de listeners que serão notificados nos eventos de deslize do painel de detalhes. Adicione um bloco init à classe SportsListOnBackPressedCallback. No bloco init, faça uma chamada para slidingPaneLayout.addPanelSlideListener() transmitindo this.
init {
   slidingPaneLayout.addPanelSlideListener(this)
}

A classe SportsListOnBackPressedCallback concluída é parecida com esta:

class SportsListOnBackPressedCallback(
   private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen),
  SlidingPaneLayout.PanelSlideListener{

   init {
       slidingPaneLayout.addPanelSlideListener(this)
   }

   override fun handleOnBackPressed() {
       slidingPaneLayout.closePane()
   }

   override fun onPanelSlide(panel: View, slideOffset: Float) {
   }

   override fun onPanelOpened(panel: View) {
       isEnabled = true
   }

   override fun onPanelClosed(panel: View) {
       isEnabled = false
   }
}

Registrar o callback

Para conferir o callback em ação, registre-o usando o agente OnBackPressedDispatcher.

A classe de base de FragmentActivity permite controlar o comportamento do botão "Voltar" usando o OnBackPressedDispatcher. O OnBackPressedDispatcher controla como os eventos do botão "Voltar" são enviados para um ou mais objetos OnBackPressedCallback.

Adicione o callback usando o método addCallback(). Esse método recebe um LifecycleOwner. Isso garante que o OnBackPressedCallback seja adicionado somente quando o LifecycleOwner for Lifecycle.State.STARTED. A atividade ou o fragmento também remove callbacks registrados quando o LifecycleOwner associado é destruído, o que evita vazamentos de memória e o torna adequado para uso em fragmentos ou outros proprietários de ciclo de vida que tenham um ciclo mais curto.

O método addCallback() também recebe a instância da classe de callback como o segundo parâmetro. Para registrar o callback, siga estas etapas:

  1. No arquivo SportsListFragment, na função onViewCreated(), logo abaixo da declaração da variável de vinculação, crie uma instância para o SlidingPaneLayout e atribua o valor de binding.slidingPaneLayout a ela.
val slidingPaneLayout = binding.slidingPaneLayout
  1. No arquivo SportsListFragment, dentro da função onViewCreated(), logo abaixo da declaração do slidingPaneLayout, adicione este código:
// Connect the SlidingPaneLayout to the system back button.
requireActivity().onBackPressedDispatcher.addCallback(
   viewLifecycleOwner,
   SportsListOnBackPressedCallback(slidingPaneLayout)
)

O código acima usa addCallback(), transmitindo o viewLifecycleOwner e uma instância do SportsListOnBackPressedCallback. Esse callback só fica ativo durante o ciclo de vida do fragmento.

  1. É hora de executar o app em um emulador com um perfil de smartphone e ver a funcionalidade personalizada do botão "Voltar" em ação.

33967fa8fde5b902.gif

10. Modo de bloqueio

Quando a lista e os painéis de detalhes se sobrepõem em telas menores, como em smartphones, os usuários podem deslizar nas duas direções por padrão, alternando livremente entre os painéis, mesmo quando não estão usando a navegação por gestos. Você pode bloquear ou desbloquear o painel de detalhes definindo o modo de bloqueio do SlidingPaneLayout.

  1. No emulador com o perfil de telefone, tente deslizar o painel de detalhes para fora da tela.
  2. Você também pode deslizar no painel de detalhes. Tente fazer isso por conta própria.
  3. Esse não é um recurso desejável no app Sports. É recomendável bloquear o SlidingPaneLayout para impedir que os usuários deslizem para dentro ou fora usando gestos. Para implementar essa opção, defina o lockMode como LOCK_MODE_LOCKED abaixo da definição do slidingPaneLayout no método onViewCreated():
slidingPaneLayout.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED

Para saber mais sobre os outros modos de bloqueio, consulte a documentação.

  1. Execute o app mais uma vez e observe que o painel de detalhes agora está bloqueado.

Parabéns por adicionar o SlidingPaneLayout ao app.

11. Código da solução

O código da solução para este codelab está no projeto e no módulo mostrados abaixo.

  1. Navegue até a página do repositório do GitHub fornecida para o projeto.
  2. Verifique se o nome da ramificação corresponde ao especificado no codelab. Por exemplo, na captura de tela a seguir, o nome da ramificação é main.

1e4c0d2c081a8fd2.png

  1. Na página do GitHub do projeto, clique no botão Code, que vai mostrar uma janela pop-up.

1debcf330fd04c7b.png

  1. Na janela pop-up, clique no botão Download ZIP para salvar o projeto no seu computador. Aguarde a conclusão do download.
  2. Localize o arquivo no computador. Geralmente ele é salvo na pasta Downloads.
  3. Clique duas vezes para descompactar o arquivo ZIP. Isso cria uma nova pasta com os arquivos do projeto.

Abrir o projeto no Android Studio

  1. Inicie o Android Studio.
  2. Na janela Welcome to Android Studio, clique em Open.

d8e9dbdeafe9038a.png

Observação: caso o Android Studio já esteja aberto, selecione a opção File > Open.

8d1fda7396afe8e5.png

  1. No navegador de arquivos, vá até a pasta descompactada do projeto, que provavelmente está na pasta Downloads.
  2. Clique duas vezes nessa pasta do projeto.
  3. Aguarde o Android Studio abrir o projeto.
  4. Clique no botão Run 8de56cba7583251f.png para criar e executar o app. Confira se ele é criado da forma esperada.

12. Saiba mais