Este tutorial faz parte do curso Android avançado em Kotlin. Você obterá o máximo valor deste curso se trabalhar com os tutoriais em sequência, mas isso não é obrigatório. Todos os tutoriais do curso estão listados na página de destino Android avançado em Kotlin.
A animação é uma ferramenta poderosa para ajudar os usuários a entender uma tela potencialmente complexa e confusa de informações. Quando um único item é atualizado, animar essa mudança pode ajudar o usuário a entender o que aconteceu. Quando muitos itens mudam, as animações podem ajudar a fazer a transição do usuário de uma IU para a próxima, para que eles entendam o contexto e as implicações das mudanças.
Existem muitos tipos diferentes de animações que podem ser usados em interfaces de usuário. Os itens podem esmaecer à medida que aparecem, esmaecer quando desaparecem, mover-se para a tela ou fora dela, ou as formas geométricas podem se transformar de maneiras interessantes. As animações podem ser executadas sozinhas, fornecendo movimento a um único objeto à medida que ele muda de estado, ou podem ser executadas junto com outras animações, pois muitas alterações acontecem uma após a outra ou em paralelo.
O Android oferece muitos recursos para animar objetos de IU A abordagem que você usa e as APIs ou ferramentas usadas para criar essas animações dependem do efeito que você está tentando obter. Este tutorial irá mostrar como criar animações de propriedade, usando ObjectAnimator
, que são os blocos de construção básicos da maioria das animações Android. As animações de propriedade são usadas para animar (ou mudar ao longo do tempo) o valor de uma propriedade em um objeto, geralmente um objeto de IU como uma vista Android.
Neste tutorial, você vai construir um aplicativo que anime estrelas na tela alterando várias propriedades de View
que controlam a posição, tamanho, rotação e translucidez. Você começará com a IU básica do aplicativo, um conjunto de botões que, quando pressionados, animarão a estrela, como visto aqui.
Cada etapa do tutorial criará o código para ativar um dos botões na IU:
Ao longo do caminho, você aprenderá novas maneiras de fazer animações mais complexas, bem como conceitos em Kotlin para tornar o código mais elegante e conciso.
ObjectAnimator
para animar os elementos da IU.ObjectAnimator
para diferentes situações de animação da IU.AnimatorSet
para criar uma animação mais complexa de várias partes.AnimatorListeners
para configurar o estado inicial e final dos objetos que estão sendo animados (como remover vistas após esmaecimento).Este tutorial é focado na animação de propriedades. Os detalhes da IU já foram feitos para você, pois estão fora do escopo deste laboratório.
Nesta etapa, você baixa o código de todo o tutorial e executa um aplicativo de exemplo simples.
$ git clone https://github.com/googlecodelabs/android-kotlin-animation-property-animation
Alternativamente, você pode baixar o repositório como um arquivo Zip:
Como este laboratório se concentra em técnicas de animação, você não vai construir a IU que o aplicativo usa. Mas você deve saber o que foi feito para você.
activity_main.xml
. Encontre o recipiente de nível superior que é um ConstraintLayout
. Dentro desse contêiner, observe seis botões; você conectará esses botões para clicar nos ouvintes para iniciar as animações.FrameLayout
,
um ViewGroup
recipiente que contém um único ImageView
. Você pode pensar neste FrameLayout
como o fundo em branco (o céu noturno, se preferir) no qual pintará suas animações, usando ImageViews
. O ImageView
existe para conter o gráfico da estrela usado para demonstrar a maioria das animações neste tutorial.MainActivity.kt
no editor. Você pode ver que parte do código já foi escrito para você. Especificamente, existem variáveis lateinit
para manter as vistas que são referenciadas no código.lateinit var star: ImageView
lateinit var rotateButton: Button
lateinit var translateButton: Button
lateinit var scaleButton: Button
lateinit var fadeButton: Button
lateinit var colorizeButton: Button
lateinit var showerButton: Button
onCreate()
.star = findViewById(R.id.star)
rotateButton = findViewById<Button>(R.id.rotateButton)
translateButton = findViewById<Button>(R.id.translateButton)
scaleButton = findViewById<Button>(R.id.scaleButton)
fadeButton = findViewById<Button>(R.id.fadeButton)
colorizeButton = findViewById<Button>(R.id.colorizeButton)
showerButton = findViewById<Button>(R.id.showerButton)
rotater()
, translater()
, etc.). E você verá que essas funções estão vazias; é aqui que você escreverá seu código nas etapas a seguir.onCreate()
, no qual você configura ouvintes onClick
para cada um dos botões, chamando as funções (atualmente) vazias.rotateButton.setOnClickListener {
rotater()
}
translateButton.setOnClickListener {
translater()
}
scaleButton.setOnClickListener {
scaler()
}
fadeButton.setOnClickListener {
fader()
}
colorizeButton.setOnClickListener {
colorizer()
}
showerButton.setOnClickListener {
shower()
}
Nesta etapa, você implementará a função rotater()
para o tratador de clique rotateButton
, que fará com que a estrela gire em um círculo.
rotater()
, crie uma animação que gira a ImageView
contendo a estrela de um valor de -360 a 0. Isso significa que a vista e, portanto, a estrela dentro dele, girará em um círculo completo (360 graus) em torno de seu centro. val animator = ObjectAnimator.ofFloat(star, View.ROTATION, -360f, 0f)
Esta linha de código cria um ObjectAnimator
que atua na "estrela" de destino (a instância ImageView
que contém o gráfico da estrela). Ele executa uma animação na propriedade ROTATION
da estrela. Mudanças nessa propriedade farão com que a estrela gire em torno de seu centro. Existem duas outras propriedades de rotação (ROTATION_X
e ROTATION_Y
) que giram em torno dos outros eixos (em 3D), mas não são normalmente usadas em animações de IU, uma vez que IUs são normalmente 2D.
A animação vai de um valor inicial de -360 graus a um valor final de 0 graus, o que fará a estrela girar em uma única rotação em torno de seu centro. Observe que o início começa em 0 graus, antes de a animação começar, e então salta imediatamente para -360 graus. Mas, como -360 é visualmente igual a 0 graus, não há nenhuma mudança perceptível quando a animação começa.
animator
, adicione uma única linha que o inicie.animator.start()
Agora, se você executar o aplicativo novamente e clicar em ROTATE, verá que a estrela realmente gira em torno de seu centro. Mas isso é muito rápido. Na verdade, ele faz isso em 300 milissegundos, que é a duração padrão de todas as animações na plataforma. 300 milissegundos é um padrão decente para a maioria das animações, mas neste caso, seria bom ter mais tempo para desfrutar da animação.
duration
do animador para 1000 milissegundos, adicionando uma única linha de código entre as duas linhas anteriores.val animator = ObjectAnimator.ofFloat(star, View.ROTATION, -360f, 0f)
animator.duration = 1000
animator.start()
Você está quase lá. Se você executar o aplicativo, verá que tem uma bela animação que dura um segundo. Tudo bem, certo?
Bem... talvez você esteja impaciente, como eu, e executou a animação novamente antes que ela parasse. Você percebeu um salto ao clicar no botão? Isso ocorre porque você sempre redefine para -360 graus no início da animação, independentemente de a estrela estar ou não no meio da animação. Este movimento descontínuo é um exemplo do que é chamado de "jank"; isso causa um fluxo perturbador para o usuário, em vez da experiência suave que você gostaria.
Existem diferentes maneiras de lidar com essa situação (como iniciar a nova animação de qualquer que seja o valor atual). Mas para manter as coisas simples, você apenas impedirá que o usuário clique no botão enquanto a animação estiver em execução, para permitir que eles aproveitem totalmente a animação em processo primeiro.
Os animadores têm um conceito de ouvintes, que chamam de volta o código do usuário para notificar a aplicação de mudanças no estado da animação. Existem retornos de chamada para uma animação começando, terminando, pausando, retomando e repetindo. O que importa aqui são apenas os eventos de início e fim; você gostaria de desativar o botão ROTATE assim que a animação começar e, em seguida, reativá-lo quando a animação terminar.
AnimatorListenerAdapter
ao animador e substitua os métodos onAnimationStart()
e onAnimationEnd()
.val animator = ObjectAnimator.ofFloat(star, View.ROTATION, -360f, 0f)
animator.duration = 1000
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
rotateButton.isEnabled = false
}
override fun onAnimationEnd(animation: Animator?) {
rotateButton.isEnabled = true
}
})
animator.start()
Aqui, rotateButton
é desativado assim que a animação começa e reativado quando a animação termina. Dessa forma, cada animação é completamente separada de qualquer outra animação de rotação, evitando o problema de reiniciar no meio.
É isso para esta primeira tarefa; agora você tem um aplicativo que pode iniciar animações de rotação na estrela. Leve-o para dar uma volta!
Nesta tarefa, você conectará translateButton
a um ouvinte onClick
, o que fará com que a estrela se mova para frente e para trás. translateButton
chama a função translater()
, que está vazia no momento. Vamos preencher isso.
translater()
, crie uma animação que mova a estrela 200 pixels para a direita e a execute.val animator = ObjectAnimator.ofFloat(star, View.TRANSLATION_X, 200f)
animator.start()
repeatCount
na animação (que controla quantas vezes ela se repete após a primeira execução), bem como o tipo de repetição (REVERSE
ou RESTART
para repetir novamente de / para os mesmos valores).val animator = ObjectAnimator.ofFloat(star, View.TRANSLATION_X, 200f)
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.start()
star
e usa isso como seu valor inicial implícito, animando desse valor para 200. Então, quando você clica em TRANSLATE botão quando a animação está no meio do caminho, ele pega esse valor intermediário como o valor inicial para a nova animação e executa a animação em uma distância menor a partir daí até 200.Você vai consertar isso de forma semelhante a como fez para a animação de rotação, desativando o translateButton
durante a animação, de modo que a animação pare em 0 antes de poder ser executada novamente.
Como esta é a segunda vez que você está escrevendo um código muito semelhante (adicionando um ouvinte para habilitar / desabilitar um botão), você deve refatorar esse código em uma função separada que você usará em todos os lugares que precisar.
disableViewDuringAnimation()
, que recebe uma View
e um Animator
, e use o código que você já escreveu anteriormente em rotater()
para criar o corpo desta função.private fun disableViewDuringAnimation(view: View,
animator: ObjectAnimator) {
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
view.isEnabled = false
}
override fun onAnimationEnd(animation: Animator?) {
view.isEnabled = true
}
})
}
translater()
erotater()
para desativar seus botões durante suas respectivas animações. Além disso, remova o código que define o ouvinte de clique na função rotater()
.private fun rotater() {
val animator = ObjectAnimator.ofFloat(star, View.ROTATION,
-360f, 0f)
animator.duration = 1000
disableViewDuringAnimation(rotateButton, animator)
animator.start()
}
private fun translater() {
val animator = ObjectAnimator.ofFloat(star, View.TRANSLATION_X,
200f)
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
disableViewDuringAnimation(translateButton, animator)
animator.start()
}
View
desativada que você adicionou ao ouvinte do animador.Como uma etapa bônus, aproveite os recursos de linguagem do Kotlin usando funções de extensão.
disableViewDuringAnimation()
para ser uma função de extensão em ObjectAnimator
. Isso torna a função mais concisa para chamar, pois elimina um parâmetro. Também torna o código um pouco mais natural de ler, colocando a funcionalidade relacionada ao animador diretamente no ObjectAnimator
.private fun ObjectAnimator.disableViewDuringAnimation(view: View) {
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
view.isEnabled = false
}
override fun onAnimationEnd(animation: Animator?) {
view.isEnabled = true
}
})
}
translater()
para chamar esta função de extensão.private fun translater() {
val animator = ObjectAnimator.ofFloat(star, View.TRANSLATION_X,
200f)
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.disableViewDuringAnimation(translateButton)
animator.start()
}
rotater()
, chamando a nova função de extensão.private fun rotater() {
val animator = ObjectAnimator.ofFloat(star, View.ROTATION,
-360f, 0f)
animator.duration = 1000
animator.disableViewDuringAnimation(rotateButton)
animator.start()
}
Agora você vai preencher o corpo da função scaler()
. Desta vez, você animará duas propriedades em paralelo.
Nas duas etapas anteriores, você estava apenas animando uma propriedade, porque era tudo o que era necessário: girar em torno de um único eixo (o eixo "z", que é perpendicular à tela) e transladar ao longo de um único eixo (o "x" eixo, que corre da esquerda para a direita na tela).
Mas quando um objeto é dimensionado, ele geralmente é dimensionado em x e y simultaneamente, para evitar que pareça "esticado" ao longo de um dos eixos ("espelho divertido" geralmente não é o efeito de se empenhar em design de IU). Portanto, você deve criar uma animação que irá animar as propriedades SCALE_X
e SCALE_Y
ao mesmo tempo.
Existem algumas maneiras de fazer isso (incluindo usando um AnimatorSet
, que você verá na etapa final deste laboratório). Mas uma boa técnica para conhecer usa PropertyValuesHolder
, que é um objeto que contém informações sobre uma propriedade e os valores entre os quais essa propriedade deve animar.
PropertyValuesHolder
Nas tarefas anteriores, você forneceu informações para ObjectAnimator
sobre a propriedade a ser animada (como TRANSLATE_X
) junto com os valores de animação (por exemplo, o valor final de 200f para tradução). Mas, em vez disso, você pode usar um objeto intermediário chamado PropertyValuesHolder
para armazenar essas informações e, em seguida, criar um único ObjectAnimator
com vários objetos PropertyValuesHolder
. Esse único animador executará uma animação em dois ou mais desses conjuntos de propriedades / valores juntos.
PropertyValuesHolder
como as primeiras linhas em scaler()
.val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 4f)
val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 4f)
Escalar para um valor de 4f significa que a estrela será escalonada para 4 vezes seu tamanho padrão.
ObjectAnimator
, como antes, mas use os objetos scaleX
e scaleY
que você criou acima para especificar as informações de propriedade / valor.val animator = ObjectAnimator.ofPropertyValuesHolder(
star, scaleX, scaleY)
É semelhante aos animadores que você criou anteriormente, mas em vez de definir uma propriedade e um conjunto de valores, usa vários PropertyValuesHolder
s que já contêm todas essas informações. O uso de vários objetos PropertyValuesHolder
em um único animador fará com que todos sejam animados em paralelo.
Agora você pode concluir o método como fez nas tarefas anteriores, redefinindo o objeto de volta a um estado final razoável e evitando problemas com animações descontínuas.
translater()
, você deseja torná-la uma animação de repetição / reversão para deixar as propriedades SCALE_X
e SCALE_Y
da estrela em seu padrão valores (1,0) quando a animação é feita. Faça isso definindo os valores de repeatCount e repeatMode apropriados no animador.animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
disableViewDurationAnimation()
para desativar scaleButton
durante a animação. Adicionar o restante desse código resulta na versão final da função.private fun scaler() {
val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 4f)
val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 4f)
val animator = ObjectAnimator.ofPropertyValuesHolder(
star, scaleX, scaleY)
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.disableViewDuringAnimation(scaleButton)
animator.start()
}
Agora, para a fase final das animações básicas, você vai esmaecer a estrela para ficar completamente transparente e, em seguida, voltar a ficar totalmente opaca.
Itens esmaecidos podem ser uma forma muito útil de fazer a transição para dentro ou fora de uma IU. Por exemplo, ao remover um item de uma lista, você pode esmaecer o conteúdo primeiro, antes de fechar a lacuna que ele deixa. Ou, se novas informações aparecerem em uma IU, você pode aparecer gradualmente. Esse efeito não apenas evita o movimento descontínuo, com elementos da IU se encaixando e saindo na frente do usuário, mas ajuda a alertar o usuário de que há uma mudança acontecendo. de apenas remover ou adicionar itens e fazê-los adivinhar o que aconteceu.
O desvanecimento é feito usando a propriedade ALPHA
em View
.
fader()
para reduzir a vista a 0 e depois voltar ao seu valor inicial. Este código é essencialmente equivalente ao código de função translater()
que você escreveu antes, exceto com uma propriedade e valor final diferentes. Esta é a aparência da função quando é concluída.private fun fader() {
val animator = ObjectAnimator.ofFloat(star, View.ALPHA, 0f)
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.disableViewDuringAnimation(fadeButton)
animator.start()
}
Uma das coisas poderosas sobre o ObjectAnimator
é que ele pode animar qualquer coisa, desde que haja uma propriedade que o animador possa acessar.
Aqui está um exemplo simples de animação de uma única propriedade em um objeto. Desta vez, essa propriedade não é um objeto android.util.Property
, mas sim uma propriedade exposta por meio de um setter, View.setBackgroundColor(int)
. Já que você não pode se referir a um objeto android.util.Property
diretamente, como você fez antes com ALPHA
, etc., você usará a abordagem de passar o nome do propriedade como uma String
. O nome é então mapeado internamente para as informações de setter / getter apropriadas no objeto de destino.
Para este exemplo, você preencherá a função colorizer()
, que é chamada quando você clica em colorizeButton
. Nesta animação, você mudará a cor de fundo do campo estelar de preto para vermelho (e vice-versa).
Primeiro, você precisará de um ObjectAnimator
que pode atuar no tipo apropriado. Você poderia usar o método de fábrica ObjectAnimator.ofInt()
, uma vez que View.setBackgroundColor(int)
leva um int
, mas... isso faria dar resultados inesperados.
colorizer()
, crie e execute esse animador para ver o problema.var animator = ObjectAnimator.ofInt(star.parent,
"backgroundColor", Color.BLACK, Color.RED).start()
colorizeButton
. Essa demonstração não é chamativa? Na verdade, é um pouco chamativo demais, pois pisca entre muitas cores diferentes no caminho de BLACK
a RED
. Sem entrar muito em detalhes, o problema é que a animação está interpretando inteiros brutos como cores. A animação entre dois valores inteiros não produz necessariamente o mesmo resultado que a animação entre as cores que esses dois inteiros representam.
O que você precisa, em vez disso, é um animador que saiba como interpretar (e animar entre) os valores das cores, em vez dos inteiros que representam essas cores.
ObjectAnimator.ofArgb()
. Tente o código novamente, usando este método de fábrica.var animator = ObjectAnimator.ofArgb(star.parent,
"backgroundColor", Color.BLACK, Color.RED).start()
A outra coisa a notar sobre esta construção do ObjectAnimator
é a propriedade: em vez de especificar uma das propriedades de View
, como ALPHA
, você é simplesmente passando a string "backgroundColor"
. Quando você faz isso, o sistema procura setters e getters com a grafia exata usando reflexão. Ele armazena em cache as referências a esses métodos e os chama durante a animação, em vez de chamar as funções Property set / get como as animações anteriores faziam.
Atualmente, você tem a capacidade de animar do preto ao vermelho... e é aí que permanece. Se você clicar no botão novamente, ele será animado novamente, mas sempre termina em vermelho, porque a animação anima explicitamente de preto para um valor final de vermelho.
private fun colorizer() {
var animator = ObjectAnimator.ofArgb(star.parent,
"backgroundColor", Color.BLACK, Color.RED)
animator.setDuration(500)
animator.repeatCount = 1
animator.repeatMode = ObjectAnimator.REVERSE
animator.disableViewDuringAnimation(colorizeButton)
animator.start()
}
Isso é tudo que há para fazer. Esta animação é muito semelhante a todas as outras que você configurou neste laboratório, exceto pelo uso da string "backgroundColor"
para o nome da propriedade. Isso não parece muito diferente do que você fez antes, exceto que significa que você pode usar ObjectAnimator
para animar literalmente qualquer coisa que tenha um setter / getter. Por exemplo, você poderia ter uma View
personalizada com uma propriedade chamada lineLength
, que define o comprimento de algum segmento de linha em sua IU (talvez usando código de desenho personalizado em uma substituição onDraw()
). Passar lineLength
para o construtor do animador resultaria na animação desse comprimento de linha, porque o sistema mapeia essa string para o configurador de propriedade subjacente em seu código de vista personalizada.
Agora, para a etapa final, você criará uma animação um pouco mais envolvente, animando várias propriedades em vários objetos.
Para este efeito, um clique de botão resultará na criação de uma estrela de tamanho aleatório, que será adicionada ao container de fundo, fora da vista do topo daquele container. A estrela começará a cair na parte inferior da tela, acelerando conforme avança. Ao cair, ele girará.
Para esta etapa, você preencherá a função shower()
para conectar uma única animação de uma estrela cadente a um único clique do botão SHOWER. Existem alguns novos conceitos aqui, além do que você viu nas etapas anteriores.
Primeiro, você vai precisar de algumas variáveis locais para manter o estado de que precisará no código seguinte. Especificamente, você precisará de:
ViewGroup
(que é apenas o pai da vista atual da estrela). shower()
com o seguinte código.val container = star.parent as ViewGroup
val containerW = container.width
val containerH = container.height
var starW: Float = star.width.toFloat()
var starH: Float = star.height.toFloat()
View
para conter o gráfico da estrela. Como a estrela é um ativo VectorDrawable
, use um AppCompatImageView
, que tem a capacidade de hospedar esse tipo de recurso. Crie a estrela e adicione-a ao recipiente de fundo. val newStar = AppCompatImageView(this)
newStar.setImageResource(R.drawable.ic_star)
newStar.layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT)
container.addView(newStar)
Você ainda não disse a esta imagem onde ser posicionada no contêiner, então ela está posicionada em (0, 0) por padrão. Você corrigirá este posicionamento nesta etapa.
shower()
, defina o tamanho da estrela. Modifique a estrela para ter um tamanho aleatório, de 0,1x a 1,6x de seu tamanho padrão. Use este fator de escala para alterar os valores de largura / altura em cache, porque você precisará saber a altura / largura real do pixel para cálculos posteriores.newStar.scaleX = Math.random().toFloat() * 1.5f + .1f
newStar.scaleY = newStar.scaleX
starW *= newStar.scaleX
starH *= newStar.scaleY
Agora você armazenou em cache a altura / largura em pixels da estrela armazenada em starW e starH:
-starW / 2)
até a metade da tela à direita (com a estrela posicionada em (containerW - starW / 2
). O posicionamento vertical da estrela será tratado posteriormente no código de animação real. newStar.translationX = Math.random().toFloat() *
containerW - starW / 2
Você concluiu a configuração das informações iniciais da estrela; é hora de trabalhar na animação. A estrela deve girar enquanto cai. Você já viu uma maneira de animar duas propriedades juntas, usando PropertyValuesHolder
, na tarefa anterior de dimensionamento. Você poderia fazer algo semelhante aqui, exceto que haverá diferentes tipos de movimento, o que é chamado de "interpolação", nessas duas animações. Especificamente, a rotação usará um movimento linear suave (movendo-se a uma taxa constante ao longo de toda a animação de rotação), enquanto a animação de queda usará um movimento acelerado (simulando a gravidade puxando a estrela para baixo a uma taxa constantemente mais rápida). Portanto, você criará dois animadores e adicionará um interpolador a cada um.
val mover = ObjectAnimator.ofFloat(newStar, View.TRANSLATION_Y,
-starH, containerH + starH)
mover.interpolator = AccelerateInterpolator(1f)
val rotator = ObjectAnimator.ofFloat(newStar, View.ROTATION,
(Math.random() * 1080).toFloat())
rotator.interpolator = LinearInterpolator()
A animação mover
é responsável por fazer a estrela "cair". Ele anima a propriedade TRANSLATION_Y
, semelhante ao que você fez com TRANSLATION_X
na tarefa de tradução anterior, mas causando movimento vertical em vez de horizontal. O código é animado de -starH
a (containerH + starH
), o que efetivamente o coloca fora do recipiente na parte superior e o move até que esteja fora do recipiente na parte inferior, conforme mostrado aqui:
O AccelerateInterpolator
"interpolador" que você está definindo na estrela causa um movimento de aceleração suave.
Para a animação de rotação, a estrela irá girar em uma quantidade aleatória entre 0 e 1080 graus (três vezes ao redor). Para o movimento, use um Interpolador Linear, para que a rotação prossiga a uma taxa constante conforme a estrela cai.
AnimatorSet
Agora é hora de colocar esses dois animadores juntos em um único AnimatorSet
, que é útil para esta animação um pouco mais complexa envolvendo vários ObjectAnimator
s.. AnimatorSet
é basicamente um grupo de animações, junto com instruções sobre quando executar essas animações. Ele pode reproduzir animações em paralelo, como você fará aqui, ou sequencialmente (como você pode fazer no exemplo de esmaecimento de lista mencionado anteriormente, onde primeiro você esmaece uma vista e então anima a lacuna resultante fechada). Um AnimatorSet
também pode conter outros AnimatorSet
s, então você pode criar coreografias hierárquicas muito complexas agrupando animadores nesses conjuntos.
AnimatorSet
e adicione os animadores filhos a ele (junto com informações para reproduzi-los em paralelo). O tempo de animação padrão de 300 milissegundos é muito rápido para aproveitar as estrelas cadentes, então defina a duração para um número aleatório entre 500 e 2.000 milissegundos, para que as estrelas caiam em velocidades diferentes. val set = AnimatorSet()
set.playTogether(mover, rotator)
set.duration = (Math.random() * 1500 + 500).toLong()
newStar
cair da parte inferior da tela, deve ser removido do recipiente. Configure um ouvinte simples para aguardar o final da animação e remova-o. Em seguida, inicie a animação.set.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
container.removeView(newStar)
}
})
set.start()
Parabéns, você construiu com sucesso um aplicativo que executa vários tipos diferentes de animações de propriedade. Animar estrelas pode não ser o tipo de experiência de IU que você deseja em seus aplicativos, mas as ferramentas usadas neste laboratório são exatamente as ferramentas que você deve usar para animar elementos de IU em situações do mundo real. O
bjectAnimator
, AnimatorSet
, LinearInterpolator
, PropertyValuesHolder
são APIs boas para entender a fim de escrever animações em seu código.
Para obter links para outros tutoriais neste curso, consulte a página inicial de tutoriais do Android avançado em Kotlin.