O artigo anterior escreveu sobre Exibir animação e animação de quadro. . . . .
Então, este artigo será uma onda de animação de atributos. . . . .
Todos nós devemos saber que a animação da vista altera apenas a posição do desenho da vista, e não altera as propriedades da vista, como as coordenadas dos quatro vértices. Ou seja, você move uma Visualização de sua posição original A para uma nova posição B e permanece em B. Se você clicar na posição B, não haverá resposta ao evento de clique. Por exemplo, está disponível apenas no ponto A. . . .
Portanto, se uma animação tem um movimento de posição, mas também deve lidar com o evento de clique, a animação de visualização não pode atender à demanda. . .
Mas a animação da propriedade pode compensar essa deficiência de visualização da animação! ! ! ! ! !
A animação de atributos foi introduzida pelo Android 3.0. Pode-se dizer que é uma expansão da animação de Visualização. É uma versão aprimorada da animação de Visualização ou pode substituir quase completamente a animação de Visualização, e a animação de atributo pode criar qualquer objeto, não apenas Visão. . . .
Como ele só pode ser usado após a 3.0, se você quiser usar a versão anterior à 3.0, pode usar a biblioteca de código aberto: http://nineoldandroids.com/
A animação de propriedade é na verdade duas classes, ValueAnimator e ObjectAnimator
ValueAnimator :
Defina o valor inicial e o valor final, e há um tempo total. Obtenha o valor excessivo de cada nó de tempo durante este tempo, e use o valor para controlar o alvo da animação.Na verdade, este tipo de cena é pouco utilizado. . . . . . .
ObjectAnimator :
Este é muito usado, e basicamente é usado para controlar a animação. Você pode usar diretamente o objeto de tarefa como o objeto de animação. . . .
============ 【ValueAnimator 】 ============
Deixe-me falar sobre ValueAnimator
Use: ValueAnimator.ofFloat ()
/**
* ValueAnimator
* */
private fun valueAnimator() {
// 从 0 过渡到 1
val valueAnimator = ValueAnimator.ofFloat(0F, 1F)
// 时间是 100毫秒
valueAnimator.duration = 100
// 监听进度
valueAnimator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
override fun onAnimationUpdate(animation: ValueAnimator?) {
//获取动画进度值,通过这个值,去控制动画目标
//比如 一个View背景颜色的百分比
val animatedValue = animation?.animatedValue as Float
Log.d(TAG, "进度:$animatedValue")
}
})
valueAnimator.start()
}
Olhe o registro:
A partir dos resultados, podemos ver que esse progresso de transição foi chamado de volta muitas vezes. . . . .
Vejamos o código-fonte de ValueAnimator.ofFloat ()
Você pode passar vários parâmetros, então:
Se ValueAnimator.ofFloat (0F, 1F) representa a transição de 0 para 1,
Se ValueAnimator.ofFloat (0F, 50F, 3F, 100F) representa a transição de 0 para 50, então de 50 para 3 e então de 3 para 100
No código:
/**
* ValueAnimator
* */
private fun valueAnimator() {
// 从 0 过渡到 1
val valueAnimator = ValueAnimator.ofFloat(0F, 50F,3F,100F)
// 时间是 100毫秒
valueAnimator.duration = 1000
// 监听进度
valueAnimator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
override fun onAnimationUpdate(animation: ValueAnimator?) {
//获取动画进度值,通过这个值,去控制动画目标
//比如 一个View背景颜色的百分比
val animatedValue = animation?.animatedValue as Float
Log.d(TAG, "进度:$animatedValue")
}
})
valueAnimator.start()
}
Veja o resultado:
进度:0.0
进度:0.0
进度:0.09472668
进度:0.40268898
进度:0.9233728
进度:1.655303
进度:2.535273
进度:3.670764
进度:5.009651
进度:6.4521832
进度:8.17451
进度:10.087408
进度:12.056963
进度:14.323733
进度:16.763512
进度:19.211622
进度:21.966991
进度:24.87359
进度:27.73996
进度:30.916098
进度:34.217968
进度:37.43199
进度:40.950714
进度:44.566513
进度:48.049103
进度:48.285706 // 接近50时,开始从 50 往 3F去过度
进度:44.67541
进度:41.22999
进度:37.528618
进度:33.79583
进度:30.263401
进度:26.500002
进度:22.736603
进度:19.204176
进度:15.471386
进度:11.770042
进度:8.324593
进度:4.7142982 // 接近3时,开始从 3 往 100F去过度
进度:6.3578963
进度:13.540961
进度:20.555607
进度:27.381924
进度:33.617188
进度:40.022766
进度:46.184475
进度:51.745235
进度:57.384045
进度:62.423447
进度:67.47879
进度:72.21197
进度:76.36032
进度:80.43042
进度:84.14144
进度:87.29662
进度:90.28128
进度:92.878716
进度:94.96303
进度:96.78872
进度:98.20865
进度:99.17078
进度:99.79256
进度:100.0
Além de ValueAnimator.ofFloat, os mais comumente usados são: ValueAnimator.ofInt ()
O princípio é o mesmo. Não vou mostrar a eles um por um. . . . . .
Além disso, também podemos ligar para
método setStartDelay () para definir o tempo de atraso da animação,
setRepeatCount () define o número de vezes que a animação é reproduzida em um loop
setRepeatMode () é o modo de reprodução de loop. Os modos de loop incluem RESTART e REVERSE, que significam, respectivamente, replay e reprodução reversa.
Esses métodos são muito simples e não serão explicados em detalhes.
============ 【ObjectAnimator 】 ==========
ObjectAnimator é diferente.Você pode animar qualquer objeto, como a cor de fundo da visualização e o valor alfa.
Do código-fonte, ObjectAnimator herda ValueAnimator,
Portanto, na análise final, a animação é operada da maneira que ValueAnimator define o valor. Portanto, ValueAnimator é o núcleo de toda a animação de propriedade.
Assim , o método ValueAnimator , em ObjectAnimator quase todo o uso de. . . . Portanto, ObjectAnimator pode substituir quase completamente ValueAnimator . . . .
Na verdade, o uso de ObjectAnimator é semelhante, olhe para o código ----
Nós olhamos para a transparência (alfa)
private fun objectAnimator() {
/**
* 参数1 tv_animator_view 目标View
* 参数2 需要进行动画的属性 比如:alpha ,这个参数字符乱传会怎样? 后面会说到
* 参数3... 这里可以传进多个参数,现在代码设置就是:0F, 1F, 0F, 1F
* 就是从 0(完全透明)过渡到 1 (完全不透明) 再到 0(完全透明) 再到 1 (完全不透明)
* */
val objectAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
objectAnimator.duration = 4000
objectAnimator.start()
}
OK, execute o código, tv_animator_view é realmente exibido conforme mencionado acima:
Transição de completamente transparente para completamente opaco, depois completamente transparente e depois completamente opaco
aprender por analogia,
Olhamos rotação (rotação)
val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view2, "rotation", 0F, -90F,90F,0F)
rotationAnimator.duration = 10000
rotationAnimator.start()
Lembre-se do que eu disse no artigo anterior, a configuração de rotação
Números positivos representam o método horário,
Os números negativos representam a abordagem no sentido anti-horário.
Portanto, o efeito da operação acima é: primeiro gire da posição original para 90 graus no sentido anti-horário, depois gire da posição atual para 90 graus no sentido horário e, em seguida, retorne à posição original.
Não vou postar as renderizações de animação em tempo de execução. . . . problema. . .
Vejamos a tradução (translationX ou translationY) :
//获取当前 View X方向偏量 如果在原始位置,XY轴的偏移量都是0
val originX = tv_animator_view3.translationX
//获取 View 的宽度,返回值单位 px像素
val width = tv_animator_view3.width
/**
* 参数1 tv_animator_view3 目标View
* 参数2 translationX X轴的平移 正为往右 负数为左,单位为像素
* 参数3 这里可以传进多个参数,
* 运行结果:从原始位置 往左移动 当前View的宽度,然后再回到原始位置
* */
val translationAnimator =
ObjectAnimator.ofFloat(tv_animator_view3, "translationX", originX, width.toFloat(), originX)
translationAnimator.duration = 10000
translationAnimator.start()
O resultado da operação é o que escrevi no comentário: mova a largura da View atual da posição original para a esquerda e, em seguida, retorne à posição original
Observe as unidades no método:
translationX a tradução do eixo X é positiva para a direita, negativa para a esquerda, a unidade é o pixel
translaçãoY A translação do eixo Y é para baixo, o negativo é para cima, a unidade é o pixel
Vejamos em escala (scaleX ou scaleY) :
val translationAnimator =
ObjectAnimator.ofFloat(tv_animator_view3, "scaleX", 0F, 1F)
translationAnimator.duration = 10000
translationAnimator.start()
Olhando para o código, você pode ver que os inteligentes estão ampliando de 0 a 1 (tamanho original) na direção do eixo X.
Se você mudar para scaleY , o zoom será feito na direção vertical. . . . .
Tão longe. As quatro animações mais comumente usadas (alfa, rotação, translaçãoX e escalaY) de animação de atributo são mostradas.
Mas assim como a pergunta do topo, o segundo parâmetro do método, o que mais pode ser preenchido, eu responderei para você: qualquer caractere.
você está brincando comigo? Não, eu não estou brincando.
Ao projetar o Android, não há limite para apenas View, que pode ser qualquer subclasse de Object.
Portanto, se você passar abd em ObjectAnimator.ofFloat (tv_animator_view3, " abd ", 0F, 1F), não haverá efeito de animação nesta Visualização.
Claro, não vai ser carsh ...
Você pode entender desta forma. Depois que um "scaleX" é transmitido, a animação continuará a procurar o destino enquanto a animação está em andamento. O destino atualmente transmitido tem o método setScaleX () .
Bata no quadro-negro! Nota! ! É o método correspondente
Se houver, ele chamará esse método de acordo com o progresso. Caso contrário, não será processado.
E o que é passado no início é TextView, que tem um método setScaleX () , e a animação vai alterá-lo.
Mas se o que eu passo for " abd ", TextView não tem nenhum método setAbd (), então não mudará nada. . . . Sem erro
O método setScaleX () de TextView está,na verdade, em sua classe pai View. O código-fonte é o seguinte:
Então, ele chama isso de setScaleX () of View
Por analogia, você que é inteligente também vai pensar que na Visão definitivamente haverá:
Os métodos setRotation (), getRotation (), setTranslationX (), getTranslationX (), setScaleY (), getScaleY (). . . . .
Quanto a como o mecanismo ObjectAnimator encontra o método set () correspondente. . . Na verdade, este é o caso, ObjectAnimator é dependente do thread atual Looper, se o thread atual não tiver Looper, um erro será relatado. Do rastreamento ObjectAnimator.start () ao método ValueAnimator.star () final pode ser visto.
Quanto a como criar o Looper atual, não é o conteúdo deste capítulo. Os leitores vão à aula por conta própria. . . . .
Na verdade, o código-fonte foi seguido até o animateValue final (fração flutuante),
Existe um trecho de código chamado: mValues [i] .calculateValue (fração); este código é usado para calcular o valor do atributo de cada quadro
Ok, vamos dar uma olhada em como a animação de propriedade obtém os métodos get () e set (). Isso é o que mais nos preocupa. . . .
O método get () é obtido quando não somos inicializados. Em PropertyValuesHolder
Obtido por reflexão
Então, que tal set () ? Na verdade, ele também está em PropertyValuesHolder . Porque criamos o segundo parâmetro passado por ObjectAnimator,
Ele é armazenado nesta classe, portanto, get e set são obtidos por meio de PropertyValuesHolder .
O rastreamento do código-fonte acima é apenas uma interceptação da parte principal, o leitor encontrará o caminho de rastreamento específico por si mesmo ,,, haha
============ [ Agência intermediária de animação ] ==================
Ok, vamos fazer uma demonstração para verificar o método de animação de propriedade - propriedade
Primeiro criamos uma classe AnimationTemp
class AnimationTemp {
private val TAG = "AnimationTemp"
fun setAbcint(abcInt: Int) {
Log.d(TAG, "调用 setAbcInt() abcInt : $abcInt ")
}
fun getAbcint(): Int {
Log.d(TAG, "调用 getAbcInt() ")
return 100
}
}
Então, de acordo com o princípio da animação de atributos, o objeto de animação pode ser qualquer objeto, bom. Usamos este AnimationTemp como o objeto de animação
/**
* 参数1 以 AnimationTemp 作为 动画对象
* 参数2 改变 abcint 这个属性,
* 对应会在目标对象里找:setAbcint(abcInt: Int) 这个方法,注意,方法名大小写
* 参数3 起始值
* 参数4 结束值
* */
val ofInt = ObjectAnimator.ofInt(AnimationTemp(), "abcint", 0, 10)
ofInt.duration = 100
ofInt.start()
Veja os resultados da corrida:
Ok, é óbvio. . . . . Continue chamando setAbcint (abcInt: Int) para imprimir de 0 a 10
Em resumo, analise o princípio:
1> ObjectAnimator.onInt () cria uma animação com um gradiente Int. O objeto de animação é uma instância de AnimationTemp. A propriedade que precisa ser alterada é chamada: abcint
2> Quando a animação está em andamento, o sistema tenta alterar o abcint do objeto de animação , então o correspondente
O sistema irá procurar por um método chamado: setAbcint (abcInt: Int) no objeto de animação , preste atenção ao tipo de parâmetros de entrada do método e ao caso do nome do método
3> Então, ao longo do ciclo de animação, chame setAbcint (abcInt: Int) para alterar o valor da propriedade da animação de destino
Neste momento, você pode ver que nosso getAbcint () não é chamado, na verdade o código-fonte acima foi analisado, ou seja, ele será chamado quando não tivermos nenhum valor de inicialização.
Ok, vamos mudar o código:
Ao criar uma animação, passe o valor final (se você passar apenas um Int, o sistema padroniza esse valor como o valor final)
/**
* 参数1 以 AnimationTemp 作为 动画对象
* 参数2 改变 abcint 这个属性,
* 对应会在目标对象里找:setAbcint(abcInt: Int) 这个方法,注意,方法名大小写
* 参数3 结束值
* */
val ofInt = ObjectAnimator.ofInt(AnimationTemp(), "abcint", 10)
ofInt.duration = 100
ofInt.start()
Então, AnimationTemp permanece inalterado
class AnimationTemp {
private val TAG = "AnimationTemp"
fun setAbcint(abcInt: Int) {
Log.d(TAG, "调用 setAbcInt() abcInt : $abcInt ")
}
fun getAbcint(): Int {
Log.d(TAG, "调用 getAbcInt() ")
return 100
}
}
Veja os resultados:
Ok, é óbvio. Verifique a análise do código-fonte.
Quando o valor inicial da animação não é inicializado, o método get () da propriedade correspondente será encontrado no destino da animação como o valor inicial.
Aqui, retornamos 100 como o valor inicial e, em seguida, o valor final é 10
Então imprima de 100 a 10
Então, neste ponto, todos sabem basicamente o princípio da animação de atributos. Todos entendem porque o alvo da animação de atributos pode ser qualquer alvo! ! ! !
============ [ Animação de combinação ] ==================
Termine de falar sobre o princípio. Finalmente, vamos falar sobre várias animações juntos. . Animação de combinação. . .
AnimatorSet usa esta classe. Você pode colocar um ou mais ValueAnimator e ObjectAnimator para jogar
Método principal:
play () Tocar um single
playSequentially () reproduz sequencialmente, um após o outro
playTogether () reproduz todas as animações juntas
after (Animator animIn) insere a animação existente na animação de entrada (ex: animIn ), execute
after (long delay) para atrasar a animação existente por milissegundos especificados e execute
antes (Animator animIn) para inserir a animação existente na animação de entrada (Por exemplo: animIn ) A execução anterior de
with (Animator anim) executará a animação existente e a animação passada (ou seja: animIn ) ao mesmo tempo
【Demo1 ---- depois de】
Ok, vamos executar o código:
val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
alphaAnimator.duration = 4000
val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
rotationAnimator.duration = 4000
val animatorSet = AnimatorSet()
animatorSet.play(alphaAnimator).after(rotationAnimator)
animatorSet.start()
O efeito de execução é: rotationAnimator é executado primeiro, alphaAnimator é executado
【Demo2 ---- com】
val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
alphaAnimator.duration = 4000
val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
rotationAnimator.duration = 4000
val animatorSet = AnimatorSet()
animatorSet.play(alphaAnimator).with(rotationAnimator)
animatorSet.start()
O efeito de execução é: rotationAnimator e alphaAnimator estão executando ao mesmo tempo
【Demo3 ---- antes de】
val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
alphaAnimator.duration = 4000
val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
rotationAnimator.duration = 4000
val animatorSet = AnimatorSet()
animatorSet.play(alphaAnimator).before(rotationAnimator)
animatorSet.start()
O efeito de execução é: alphaAnimator é executado primeiro, e rotationAnimator é executado depois
【Demo4 - depois (longo)】
val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
alphaAnimator.duration = 4000
val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
rotationAnimator.duration = 4000
val animatorSet = AnimatorSet()
animatorSet.play(alphaAnimator).before(rotationAnimator).after(3000)
animatorSet.start()
O efeito de execução é: atrasar 3 segundos, alphaAnimator executa primeiro, rotationAnimator executa depois
【Demo5 ---- playTogether】
val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
alphaAnimator.duration = 4000
val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
rotationAnimator.duration = 4000
val animatorSet = AnimatorSet()
animatorSet.playTogether(alphaAnimator, rotationAnimator)
animatorSet.start()
O efeito de execução é: rotationAnimator e alphaAnimator estão executando ao mesmo tempo
【Demo5 ---- playSequentially】
val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
alphaAnimator.duration = 4000
val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
rotationAnimator.duration = 4000
val animatorSet = AnimatorSet()
animatorSet.playSequentially(alphaAnimator, rotationAnimator)
animatorSet.start()
O efeito de execução é: alphaAnimator é executado primeiro, e rotationAnimator é executado depois
==================== [ Monitoramento de animação ] =================
Na verdade é um método
val alphaAnimator = ObjectAnimator.ofFloat(tv_animator_view, "alpha", 0F, 1F, 0F, 1F)
alphaAnimator.duration = 4000
val rotationAnimator = ObjectAnimator.ofFloat(tv_animator_view, "rotation", 0F, -90F, 90F, 0F)
rotationAnimator.duration = 4000
val animatorSet = AnimatorSet()
animatorSet.addListener(object : Animator.AnimatorListener{
override fun onAnimationRepeat(animation: Animator?) {
//重复
}
override fun onAnimationEnd(animation: Animator?) {
//结束
}
override fun onAnimationCancel(animation: Animator?) {
//取消
}
override fun onAnimationStart(animation: Animator?) {
//开始
}
})
animatorSet.playSequentially(alphaAnimator, rotationAnimator)
animatorSet.start()
Na verdade, você não sabe se está confiante, você descobriu que a animação do atributo não encontrou setFillAfter () para definir se a animação é restaurada ao estado inicial.
Bem, podemos monitorar o final da animação e, em seguida, definir o estado de destino quando ela terminar
public class ResetAnimatorListenerAdapter extends AnimatorListenerAdapter {
View animatorView;
public ResetAnimatorListenerAdapter(View animatorView) {
this.animatorView = animatorView;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//重置 animatorView 的状态
animatorView.setTranslationY(0);
animatorView.setAlpha(0.0f);
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
}
}
O acima mencionado que o código Chase define dinamicamente a animação, na verdade, ele também pode ser definido através de xml, sem falar nas especificidades. Como não é recomendado usar xml, ele está relativamente morto e não pode ser alterado.
Finalmente, as ressalvas da animação
1: Impeça OOM e evite usar animação de quadro com mais fotos. . .
2: Para evitar vazamentos de memória, pare a animação quando a visualização sair do invisível
3: O atributo de animação é usado acima do Android3.0, inferior a esta versão, você pode usar a biblioteca compatível
4: Visualizar animação, quando a visualização é movida, a configuração View.GONE às vezes se torna inválida.Neste momento, basta chamar View.clearAnimation () para limpar a animação.
5: Quando a animação está em andamento, você pode ativar a aceleração de hardware para melhorar a fluência.
Não há problema com o código acima, por favor deixe uma mensagem para corrigir quaisquer problemas, obrigado