Desenhar uma IU é apenas uma parte da criação de uma visualização personalizada. Você também precisa fazer com que sua visualização responda à entrada do usuário de uma forma parecida com a uma ação real que você está imitando.
Faça com que os objetos no seu app ajam como objetos reais. Por exemplo, não deixe as imagens no seu aplicativo saem da existência e reaparecem em outro lugar, porque os objetos no mundo real não façam isso. Em vez disso, mova as imagens de um lugar para outra.
Os usuários percebem até mesmo comportamentos sutis ou sentem em uma interface e reagem melhor a sutilezas que imitam o mundo real. Por exemplo, quando os usuários arrastam um objeto da interface, dão a eles uma sensação de inércia no início que atrasa o movimento. No fim do movimento, dão a elas uma sensação de impulso que carrega o objeto além do deslizamento rápido.
Esta página demonstra como usar recursos do framework do Android para adicionar esses comportamentos reais à sua visualização personalizada.
Você pode encontrar informações adicionais relacionadas em Visão geral dos eventos de entrada e Animação de propriedades geral.
Processar gestos de entrada
Como muitos outras frameworks de IU, o Android é compatível com um modelo de evento de entrada. Usuário
se transformam em eventos que acionam callbacks, e você pode substituir
para personalizar a resposta do app ao usuário. A entrada mais comum
no sistema Android é o toque, que aciona
onTouchEvent(android.view.MotionEvent)
Substitua esse método para processar o evento da seguinte maneira:
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return super.onTouchEvent(event) }
Java
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
Os eventos de toque por si só não são muito úteis. Interfaces de toque modernas
definir interações em termos de gestos, como tocar, puxar, empurrar,
movimentar e aplicar zoom. Para converter eventos de toque brutos em gestos, o Android
oferece
GestureDetector
:
Crie um GestureDetector
transmitindo uma instância de uma classe
que implementa
GestureDetector.OnGestureListener
.
Se quiser processar apenas alguns gestos, você pode estender
GestureDetector.SimpleOnGestureListener
em vez de implementar o GestureDetector.OnGestureListener
interface gráfica do usuário. Por exemplo, esse código cria uma classe que estende
GestureDetector.SimpleOnGestureListener
e substituições
onDown(MotionEvent)
.
Kotlin
private val myListener = object : GestureDetector.SimpleOnGestureListener() { override fun onDown(e: MotionEvent): Boolean { return true } } private val detector: GestureDetector = GestureDetector(context, myListener)
Java
class MyListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } } detector = new GestureDetector(getContext(), new MyListener());
Quer você use ou não GestureDetector.SimpleOnGestureListener
,
sempre implemente uma
onDown()
que retorna true
. Isso é necessário porque todos os gestos
comece com uma mensagem onDown()
. Se você devolver false
de onDown()
, conforme
GestureDetector.SimpleOnGestureListener
faz, o sistema presume que
deve ignorar o resto do gesto, e os outros métodos de
GestureDetector.OnGestureListener
não sejam chamadas. Só devolver
false
de onDown()
se você quiser ignorar uma mensagem inteira.
gesto.
Depois de implementar GestureDetector.OnGestureListener
e criar
uma instância de GestureDetector
, é possível usar
GestureDetector
para interpretar os eventos de toque recebidos no
onTouchEvent()
Kotlin
override fun onTouchEvent(event: MotionEvent): Boolean { return detector.onTouchEvent(event).let { result -> if (!result) { if (event.action == MotionEvent.ACTION_UP) { stopScrolling() true } else false } else true } }
Java
@Override public boolean onTouchEvent(MotionEvent event) { boolean result = detector.onTouchEvent(event); if (!result) { if (event.getAction() == MotionEvent.ACTION_UP) { stopScrolling(); result = true; } } return result; }
Quando você transmite a onTouchEvent()
um evento de toque que ele não
reconhecer como parte de um gesto, ela retorna false
. Em seguida, você pode executar
seu próprio código personalizado de detecção de gestos.
Criar um movimento fisicamente plausível
Os gestos são uma maneira poderosa de controlar dispositivos touchscreen, mas podem ser contraintuitiva e difícil de lembrar, a menos que produza resultados plausíveis.
Por exemplo, suponha que você queira implementar um gesto de deslize rápido horizontal que define o item desenhado na visualização girando em torno de seu eixo vertical. Este gesto faz sentido se a interface responde movendo-se rapidamente na direção da rolagem, e, em seguida, desacelerar, como se o usuário empurrasse uma roda e fizesse girar.
A documentação sobre como
animar uma rolagem
gesto fornece uma explicação detalhada sobre como implementar sua própria atividade
do seu modelo. Mas simular a sensação de um volante não é trivial. Muita física
e matemática é necessária para fazer um modelo de volante funcionar corretamente. Felizmente,
O Android oferece classes auxiliares para simular esse e outros comportamentos. A
Scroller
é a base para lidar com gestos de rolagem rápida no estilo de volante.
Para iniciar uma rolagem rápida, chame
fling()
com a velocidade inicial e os valores mínimos e máximos x e y
valores da rolagem rápida. Para o valor da velocidade, você pode usar o valor calculado pela
GestureDetector
:
Kotlin
fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { scroller.fling( currentX, currentY, (velocityX / SCALE).toInt(), (velocityY / SCALE).toInt(), minX, minY, maxX, maxY ) postInvalidate() return true }
Java
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY); postInvalidate(); return true; }
A chamada para fling()
configura o modelo de física para a rolagem
gesto. Em seguida, atualize o Scroller
chamando
Scroller.computeScrollOffset()
em intervalos regulares. computeScrollOffset()
atualiza o
estado interno do objeto Scroller
lendo a hora atual e
usando o modelo de física para calcular as posições x e y
tempo de resposta. Ligação
getCurrX()
e
getCurrY()
para recuperar esses valores.
A maioria das visualizações transmite os valores x e y do objeto Scroller
.
posições diretamente
scrollTo()
.
Este exemplo é um pouco diferente: ele usa a posição de rolagem x atual.
para definir o ângulo de rotação da visualização.
Kotlin
scroller.apply { if (!isFinished) { computeScrollOffset() setItemRotation(currX) } }
Java
if (!scroller.isFinished()) { scroller.computeScrollOffset(); setItemRotation(scroller.getCurrX()); }
A classe Scroller
calcula as posições de rolagem, mas
não aplica essas posições automaticamente à sua visualização. Aplicar novas coordenadas
com frequência suficiente para fazer com que a animação de rolagem pareça suave. Há duas maneiras de
faça isto:
- Force um novo desenho chamando
postInvalidate()
depois de chamarfling()
. Essa técnica exige que você computar deslocamentos de rolagem emonDraw()
e chamarpostInvalidate()
sempre que o deslocamento de rolagem mudanças. - Configure um
ValueAnimator
para animar a duração do deslize rápido e adicionar um listener para processar é atualizada chamandoaddUpdateListener()
. Essa técnica permite animar propriedades de umView
:
Suavizar as transições
Os usuários esperam que uma interface moderna faça a transição entre estados com facilidade: elementos da interface aparecem e desaparecem, e os movimentos começam a aparecer e terminando de maneira suave, em vez de começar e parar abruptamente. O Android animação de propriedade framework, que facilita as transições.
Para usar o sistema de animação, sempre que uma propriedade alterar o que afeta seu
da visualização, não altere a propriedade diretamente. Em vez disso, use
ValueAnimator
para fazer a mudança. No exemplo a seguir,
a modificação do componente filho selecionado na visualização faz com que todo o
gire a visualização para que o ponteiro de seleção fique centralizado.
ValueAnimator
muda a rotação ao longo de um período de várias centenas
milésimos de segundo, em vez de definir imediatamente o novo valor de rotação.
Kotlin
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply { setIntValues(targetAngle) duration = AUTOCENTER_ANIM_DURATION start() }
Java
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0); autoCenterAnimator.setIntValues(targetAngle); autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); autoCenterAnimator.start();
Se o valor que você quer mudar for um dos View
de base
propriedades, fazer a animação é ainda mais fácil, porque as visualizações têm uma função
ViewPropertyAnimator
otimizado para animação simultânea de várias propriedades, como no
exemplo a seguir:
Kotlin
animate() .rotation(targetAngle) .duration = ANIM_DURATION .start()
Java
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();