Gerenciamento de gestos: lidando com conflitos de gestos. Parte 3

Uma tradução do artigo foi preparada antes do lançamento de um curso avançado e básico de desenvolvimento para Android.



Este é o terceiro artigo de uma série sobre controle de gestos. Você pode se familiarizar com as traduções da primeira e da segunda partes, se as perdeu.

Nos artigos anteriores, abordamos o tópico de preenchimento do espaço da tela de ponta a ponta. Neste artigo, veremos como lidar com quaisquer conflitos que surjam entre os gestos do seu aplicativo e os novos gestos do sistema no Android 10 .

O que queremos dizer com conflitos de gestos? Vejamos um exemplo. Digamos que temos um music player que permite ao usuário rolar a música atual arrastando e soltando SeekBar.



Infelizmente, SeekBarestá muito perto da área do gesto de retornar à tela inicial, pelo que o gesto de alternar rapidamente para o aplicativo anterior (QuickSwitch) começa, o que incomoda o usuário.

O mesmo pode acontecer em qualquer extremidade da tela onde as áreas de gestos estão localizadas. Existem muitos exemplos comuns que podem causar conflitos, como: gavetas de navegação ( DrawerLayout), carrosséis ( ViewPager ), controles deslizantes ( SeekBar), deslize o dedo nas listas.

O que nos leva à pergunta "como podemos corrigir isso?" Para facilitar essa pergunta, criamos um fluxograma que oferece uma resposta com base na situação.


Você pode encontrar uma versão em PDF do fluxograma aqui .

Espero que as perguntas não exijam explicação, mas, para o caso, analisarei cada uma delas:

1. O aplicativo precisa ocultar as barras de navegação e status?


A primeira pergunta é se o principal caso de uso do seu aplicativo requer ocultar as barras de navegação e / ou status. Ao ocultar, queremos dizer que esses painéis do sistema não são visíveis. Isso não significa que você implementou o conceito de "borda a borda" ou algo semelhante em seu aplicativo.

Possíveis razões para responder "sim" a esta pergunta:


Exemplos comuns de aplicativos que devem responder “sim” a essa pergunta são jogos, reprodutores de vídeo, visualizadores de fotos, aplicativos de desenho.

2. O cenário principal para o uso da interface do usuário envolve furtos na área de gestos / em torno dela?


Esta pergunta descobre se a sua interface do usuário contém algum elemento em / próximo às áreas de gestos ("voltar" e "casa") que o usuário deve deslizar.

Os jogos costumam dizer sim devido ao fato de que

  • Os controles na tela geralmente estão localizados perto da borda esquerda / direita e na parte inferior da tela.
  • Em alguns jogos, você precisa deslizar sobre os elementos que podem estar em qualquer lugar na tela.

Além dos jogos, exemplos comuns de UIs que devem dizer sim são:

  • Corte fotos da interface do usuário em que os quadros arrastáveis ​​também estejam próximos às bordas esquerda e direita da tela.
  • Um aplicativo de desenho no qual o usuário pode desenhar em uma tela que cobre a tela inteira.

3. Visualização freqüentemente usada em / ao redor da área de gestos?


Espero que esta seja uma pergunta bastante simples. Isso também inclui visualizações, que cobrem a área dos gestos e depois se estendem a uma grande parte da tela, por exemplo, DrawerLayoutou a uma grande ViewPager.

4. A visualização envolve deslizar / arrastar?


Mudamos um pouco as táticas e começamos a observar as visões individuais. Para qualquer uma das opiniões às quais você respondeu afirmativamente à terceira pergunta, fazemos um pequeno esclarecimento: o usuário deve deslizar / arrastar?

Há muitos exemplos onde você tem que responder "sim»: SeekBars, BottomSheet ou mesmo PopupMenu(você tem que arrastar para aberto).

5. A vista está totalmente / geralmente localizada sob as áreas de gestos?


Com base na quarta pergunta, esclarecemos agora se a vista está total ou principalmente localizada na área do gesto.

Se sua visualização estiver em um contêiner rolável como este RecyclerView, pense sobre essa questão de maneira um pouco diferente: a visualização totalmente / basicamente expandida fica abaixo da área do gesto em todas as posições de rolagem ? Se o usuário puder rolar a exibição por baixo da área de gestos, não será necessário fazer nada.

No diagrama acima, você pode observar uma tela cheia do carrossel ( ViewPager) como exemplo de resposta negativa e se perguntar por que esse caso não precisa ser tratado. Isso ocorre porque as áreas de gestos esquerda / direita são relativamente pequenas em largura (padrão: 20dpcada) em comparação com a largura da visualização. Típicaa largura da tela do telefone na orientação retrato é de ~ 360dp, deixando ~ 320dp de espaço livre no qual o usuário não tem dificuldade (isso representa quase 90% da tela). Mesmo com margens / recuos internos, o usuário ainda pode rolar confortavelmente pelo carrossel.

6. A borda da vista se sobrepõe a qualquer área de gesto necessária?


A última pergunta esclarece se a visualização está em alguma das áreas de gesto necessárias. Se você se lembrar do nosso artigo anterior , lembrará que as áreas obrigatórias dos gestos do sistema são áreas da tela em que os gestos do sistema sempre têm precedência.

No Android 10, existe apenas uma área de gesto obrigatória, localizada na parte inferior da tela, que permite ao usuário voltar para casa ou abrir seus aplicativos mais recentes. Isso pode mudar em versões futuras da plataforma, mas agora só precisamos trabalhar com a visualização na parte inferior da tela.

Exemplos típicos são:

  • Modal BottomSheet , uma vez que tendem a entrar em colapso em uma pequena vista arrastar e soltar na parte inferior do ecrã.
  • Um carrossel de rolagem horizontal na parte inferior da tela, por exemplo, uma interface com adesivos.

Agora que resolvemos as perguntas, esperamos que você possa encontrar uma das soluções. Vamos analisar cada uma delas com mais detalhes.

Nenhum conflito a ser tratado


Vamos começar com a "solução" mais simples, apenas não faça nada !

Claro, talvez ainda haja espaço para otimizações que você pode fazer (consulte a seção abaixo), mas felizmente não há problemas sérios ao usar o aplicativo com o modo de navegação por gestos ativado.

Se a programação trouxe você aqui, mas você ainda sente que há um problema, informe- nos . Talvez tenhamos perdido alguma coisa.

Movendo a visão das áreas de gestos


Conforme aprendemos no artigo anterior , existem inserções para informar ao seu aplicativo onde as zonas de gestos do sistema estão na tela. Um dos métodos que podemos usar para resolver conflitos de gestos é mover quaisquer visualizações conflitantes das áreas de gestos. Isso é especialmente importante para a visualização na parte inferior da tela, pois essa área é a zona de gestos obrigatórios e os aplicativos não podem usar as APIs de exclusão de gestos lá.

Vamos dar uma olhada em um exemplo novamente. Temos uma interface de player de música que mostramos acima. Ele contém SeekBar, localizado na parte inferior da tela, permitindo ao usuário rolar a música.


UI music player com uma barra de busca na parte inferior da tela

Mas quando um usuário tenta pular uma música, isso acontece:


Gravando um gesto do sistema que entra em conflito com o SeekBar

Isso ocorre porque a área inferior do gesto se sobrepõe ao SeekBar, de modo que o gesto de volta para casa tem prioridade. Aqui está a visualização das zonas de gestos:



Uma solução simples


A solução mais simples aqui é adicionar um recuo / margem extra para que o SeekBar se mova da área de gestos. Algo assim:



Se arrastarmos a SeekBar neste exemplo, você verá que não ativamos mais o gesto de regresso a casa: a


SeekBar não entra mais em conflito com o gesto do sistema inferior.

Para implementar isso, precisamos usar as novas inserções de gesto do sistema disponíveis na API 29 e na biblioteca Jetpack Core v1.2.0 (atualmente em alfa ). No exemplo, aumentamos o recuo inferior SeekBarpara corresponder ao valor da inserção inferior do gesto :

ViewCompat.setOnApplyWindowInsetsListener(seekBar) { view, insets ->
     //      view ,    system gesture insets
    view.updatePadding(
        bottom = insets.systemGestureInsets.bottom
    )
    insets
}

Se você estiver interessado em aprender a facilitar o trabalho WindowInsets, leia nosso outro artigo sobre este tópico:

WindowInsets - Layout de ouvintes

Ações futuras


Nesse ponto, você pode decidir que o trabalho já está concluído e, para alguns layouts, isso pode muito bem ser a solução final. Porém, em nosso exemplo, a interface do usuário regrediu visualmente com muito espaço perdido abaixo SeekBar. Assim, em vez de simplesmente aumentar a visualização, podemos redesenhar o layout para evitar a perda de espaço:


SeekBarmovido para a parte superior do painel de reprodução.

Aqui fomos SeekBarpara a parte superior do painel de reprodução, completamente fora da área de gestos. Isso significa que não precisamos mais aumentar artificialmente a altura do painel para acomodar SeekBar.

, . « ».

API


Em nosso artigo anterior, mencionamos que "os aplicativos têm a capacidade de excluir gestos do sistema para determinadas partes da tela" . Os aplicativos fazem isso usando as APIs de exclusão de gestos, que apareceram pela primeira vez no Android 10.

Existem duas funções diferentes que o sistema fornece para excluir áreas de gestos: View.setSystemGestureExclusionRects()e Window.setSystemGestureExclusionRects(). O que você deve usar depende do aplicativo: se você usa o Android View, o sistema prefere a API de visualização, caso contrário, use a WindowAPI.

A principal diferença entre as duas APIs é que a API do Windowsespera que quaisquer retângulos estejam no espaço de coordenadas da janela. Se você usar a vista, normalmente trabalhará no espaço de coordenadas da vista. A API View cuida da transformação entre os espaços de coordenadas, ou seja, você precisa raciocinar apenas em termos do conteúdo da exibição.

Vejamos um exemplo. Vamos usar nosso exemplo de um reprodutor de música novamente, SeekBarlocalizado em toda a largura da tela. Corrigimos o conflito SeekBarcom o gesto de retornar à tela inicial na seção anterior, mas ainda temos as áreas esquerda e direita dos gestos que precisamos cuidar.

Vamos ver o que acontece quando um usuário tenta pular uma música quando o "slider"SeekBar(arrasto circular) está localizado próximo a uma das bordas: a


SeekBar está em conflito com a área do gesto de costas.

Como o controle deslizante está sob a área de gesto correta, o sistema acredita que o usuário está tentando voltar usando o gesto, portanto, mostra a seta de trás. Isso é inconveniente para os usuários, pois eles provavelmente não desejavam voltar no momento. Podemos corrigir isso usando as APIs de exclusão de gestos mencionadas acima para excluir as bordas do controle deslizante.

As APIs de exceção por gesto geralmente são chamadas de dois lugares: onDraw()quando sua exibição é renderizada, e onLayout()caso contrário. Sua visão passaList<Rect>contendo todos os retângulos a serem excluídos. Como mencionado anteriormente, esses retângulos devem estar em seu próprio sistema de coordenadas de exibição.

Geralmente você cria uma função como essa que será chamada de onLayout()e / ou onDraw():

private val gestureExclusionRects = mutableListOf<Rect>()
private fun updateGestureExclusion() {
   //   ,      Android 10+
   if (Build.VERSION.SDK_INT < 29) return
  // -,     
   gestureExclusionRects.clear()
      //   ,    .  SeekBar    .
   thumb?.also { t ->
       gestureExclusionRects += t.copyBounds()
   }
   //            view,       ,     
   // ,       
   systemGestureExclusionRects = gestureExclusionRects
}

Um exemplo completo pode ser encontrado aqui .

Depois de adicionarmos isso, rebobinar próximo às bordas funcionará conforme o esperado:


SeekBar trabalhando na área de gestos para trás

Observe no exemplo acima. SeekBarjá faz isso automaticamente para você no Android 10, então não é necessário fazer isso sozinho. Aqui, fazemos apenas um exemplo para mostrar o esboço geral.

Limitações


Embora as APIs de exclusão de gestos possam parecer a solução perfeita para resolver todos os conflitos de gestos, esse não é realmente o caso. Usando as APIs de exclusão de gestos, você declara que o gesto do seu aplicativo é mais importante que as ações de retorno do sistema. Esta é uma afirmação forte, portanto, essa API foi projetada para ser uma saída de emergência quando você não pode fazer mais nada.

Usando as APIs de exclusão de gestos, você declara que o gesto do seu aplicativo é mais importante que a ação do sistema de retroceder.

Como o comportamento que a API fornece pode violar uma experiência confortável do usuário, o sistema limita seu uso: os aplicativos podem excluir apenas até 200dp por borda.

Aqui estão algumas perguntas comuns que os desenvolvedores têm quando ouvem isso:

Por que a restrição é necessária? Espero que a explicação acima já tenha levado você a um motivo. Acreditamos que é muito importante que o usuário possa voltar consistentemente a partir do furto lateral. Consistentemente em todo o dispositivo, não em um aplicativo. Essa restrição pode parecer muito restritiva, mas apenas um aplicativo, excluindo toda a borda da tela, é suficiente para causar transtornos ao usuário, o que leva à remoção do aplicativo ou a algo mais radical.

Em outras palavras, o sistema de navegação sempre deve ser consistente e fácil de usar.

Por que 200dp?O argumento para 200dp é bastante direto. Como mencionamos anteriormente, as APIs de exclusão de gestos devem ser usadas como último recurso; portanto, esse limite foi calculado como um múltiplo de vários destinos importantes de toque. O tamanho mínimo recomendado para um alvo sensorial é 48dp0,4 alvos sensoriais × 48dp = 192dp. Adicione um pouco mais de indentação e obteremos o valor 200dp.

E se eu precisar excluir mais de 200dp por borda? O sistema excluirá apenas os 200dp mais baixos que você solicitou.


O sistema permite uma solicitação de uma altura total de 200 dp, contando da borda inferior do

My view fora da tela, isso é considerado um limite?Não, o sistema considera apenas retângulos excluídos que estão dentro da tela. Da mesma forma, se a vista estiver parcialmente na tela, apenas a parte visível do retângulo solicitado será levada em consideração.

Mergulhe no próximo post


Talvez depois de ler isso, você esteja se perguntando por que não consideramos o lado direito do fluxograma. As soluções abaixo foram projetadas especificamente para aplicativos que usam a tela inteira na renderização. Falaremos sobre eles na próxima parte.






All Articles