English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Implementazione personalizzata di View Android per animazione di spunta

Per prima cosa, mostriamo l'effetto visivo

Immagine animata

Immagine statica

1. revisione

[Android Custom View: una piccola animazione di spunta ben fatta] Nell'articolo precedente, abbiamo realizzato基本上实现了控件的效果了,ma... ma... dopo tre o quattro giorni, guardando indietro al codice che avevo scritto, anche se la logica c'era ancora, parte del codice non era chiaro immediatamente...

Mio Dio, devo immediatamente ricostruire! Fortunatamente, un amico chiamato ChangQin ha imitato la scrittura di questo controllo, e ho pensato che potrei implementarlo in questo modo.

2. riflessione

Riguardo all'idea di disegno dei controlli, puoi guardare l'articolo precedente, non analizzerò qui. Prima di tutto, analizziamo i problemi irrequieti dei controlli nell'articolo precedente, quali parti devono essere migliorate.

Prendiamo Disegnare la barra di progresso a cerchio Passo dopo passo, vediamo

//Contatore
private int ringCounter = 0;
@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 if (!isChecked) {
  ...
  return;
 }
 //Disegnare l'arco di progresso, aumentare automaticamente di 12 unità ogni volta che si disegna, ossia l'arco ha percorso altri 12 gradi
 //Questa unità di 12 è scrittasi a priori, in futuro possiamo farne una configurazione per la personalizzazione
 ringCounter += 12;
 if (ringCounter >= 360) {
  ringCounter = 360;
 }
 canvas.drawArc(mRectF, 90, ringCounter, false, mPaintRing);
 ...
 //Forzare il ridisegno
 postInvalidate();
}

Ecco, abbiamo definito un contatore ringCounter, quando disegniamo, aumentiamo di 12 unità, raggiungendo 360, per simulare il cambiamento del progresso.

Pensare bene

Cambiare l'unità di auto-incremento per controllare la variazione della velocità dell'animazione, è difficile regolarla a proprio piacimento, in questo caso possiamo pensare che il controllo della velocità di esecuzione dell'animazione sia controllare il tempo, se possiamoUsare il tempo per controllare la velocità dell'animazioneSarebbe molto più comodo. Se l'animazione viene eseguita in 4 passaggi, se ogni passaggio dell'animazione viene implementato con un contatore manuale, dovrebbero essere definite 4 variabili di membro o più,Troppe variabili di membro renderebbero il codice più confusoSe l'animazione deve essere aggiuntaInterpolatore, quindi il contatore manuale non può soddisfare l'analisi sopra menzionata,Non posso accettarlo

3. Cambiare, cambiare, cambiare

Allora come migliorare il problema menzionato in precedenza? La risposta è utilizzare animazioni delle proprietà personalizzate per risolvere il problema, quindi l'argomento principale di questo articolo è utilizzare animazioni delle proprietà per sostituire il contatore manuale, cercando di mantenere la logica del codice il più chiaro possibile, specialmente il codice nel metodo onDraw().

Un vantaggio dell'animazione delle proprietà è che, data la gamma di valori, ti aiuta a generare una serie di numeri desiderati, e combinato con l'interpolatore, può produrre effetti inaspettati. Nel prossimo capitolo, vedremo passo dopo passo come ricostruire la parte di esecuzione dell'animazione

3.1 Disegnare la barra di progresso a cerchio

Prima di tutto, utilizzare ObjectAnimator personalizzato per simulare il progresso

//ringProgress è un nome di proprietà personalizzato, il range di generazione dei numeri è 0 - 360, che rappresenta un angolo di cerchio
ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360);
//definire il tempo di esecuzione dell'animazione, un ottimo sostituto per l'uso di unità auto-incrementabili precedenti per controllare la velocità di esecuzione dell'animazione
mRingAnimator.setDuration(mRingAnimatorDuration);
//non necessita di interpolatore
mRingAnimator.setInterpolator(null);

Per creare animazioni delle proprietà personalizzate, è necessario configurare i relativi setter e getter, perché durante l'esecuzione dell'animazione, verranno cercati i setter per modificare i relativi valori.

private int getRingProgress() {
 return ringProgress;
}
private void setRingProgress(int ringProgress) {
 //L'animazione esegue quando viene chiamato setter
 //Qui possiamo registrare i valori generati dall'animazione, salvarli in una variabile e usarli durante ondraw
 this.ringProgress = ringProgress;
 //Ricorda di ridisegnare
 postInvalidate();
}

Infine, disegna il grafico in onDraw()

//Disegna l'arco di progresso canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing);

3.2 Eseguire l'animazione di contrazione verso il centro del cerchio

Analogamente, crea anche un'animazione delle proprietà

//L'attributo personalizzato qui è il raggio di contrazione del cerchio
ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0);
//Aggiungi un interpolatore di decelerazione
mCircleAnimator.setInterpolator(new DecelerateInterpolator());
mCircleAnimator.setDuration(mCircleAnimatorDuration);

setter/getter è simile e non lo menzionerò

Infine, disegna in onDraw()

//Disegna lo sfondo
mPaintCircle.setColor(checkBaseColor);
canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radius : 0, mPaintCircle);
//Quando il cerchio di progresso è stato disegnato, disegna il cerchio ristretto
if (ringProgress == 360) {
 mPaintCircle.setColor(checkTickColor);
 canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle);
}

3.3 Eseguire l'effetto di disegno della spina, ingrandimento e rimbalzo

Questi sono due effetti indipendenti, qui eseguiti contemporaneamente, quindi li ho messi insieme

Prima di tutto, definisci l'animazione delle proprietà

//animazione di transizione di trasparenza del tick
ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255);
mAlphaAnimator.setDuration(200);
//l'animazione finale di ingrandimento e rimbalzo, cambia la larghezza del pennello per ottenere
//La larghezza del pennello, il range di variazione è
//Prima dall'iniziale, poi alla n volte l'iniziale, infine di nuovo all'iniziale
ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES);
mScaleAnimator.setInterpolator(null);
mScaleAnimator.setDuration(mScaleAnimatorDuration);
//esegue insieme l'animazione di spunta e l'animazione di ingrandimento di rimbalzo
AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet();
mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator);

getter/setter

private int getTickAlpha() {
 return 0;
}
private void setTickAlpha(int tickAlpha) {
 //Imposta la trasparenza, non è necessario salvare la variabile
 //Imposta direttamente il valore di trasparenza nel pennello
 mPaintTick.setAlpha(tickAlpha);
 postInvalidate();
}
private float getRingStrokeWidth() {
 return mPaintRing.getStrokeWidth();
}
private void setRingStrokeWidth(float strokeWidth) {
 //设置画笔宽度,可以不用变量来保存了
 //直接将画笔宽度设置到画笔里面即可
 mPaintRing.setStrokeWidth(strokeWidth);
 postInvalidate();
}

最后,同样在onDraw()中绘制即可

if (circleRadius == 0) {
 canvas.drawLines(mPoints, mPaintTick);
 canvas.drawArc(mRectF, 0, 360, false, mPaintRing);
}

3.4 按顺序执行动画

执行多个动画,可以使用AnimatorSet,其中playTogether()是同时执行,playSequentially()是一个接一个,一步一步执行。

mFinalAnimatorSet = new AnimatorSet();
mFinalAnimatorSet.playSequentially(mRingAnimator, mCircleAnimator, mAlphaScaleAnimatorSet);

最后在onDraw()中执行动画

//这里定义了一个标识符,用于告诉程序,动画每次只能执行一次
if (!isAnimationRunning) {
 isAnimationRunning = true;
 //执行动画
 mFinalAnimatorSet.start();
}

3.5 每个方法最好只有一个职责

如果将定义属性动画的方法放在onDraw()中,我个人感觉会很乱,再仔细看看,这几个属性动画是不需要动态变化的,为什么不提取出来在一开始的时候就初始化呢?

因此,我们将定义属性动画的代码提取出来,并且放到构造函数中初始化

public TickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 ...
 initAnimatorCounter();
}
/**
 * 使用ObjectAnimator初始化一些计数器
 */
private void initAnimatorCounter() {
 //圆环进度
 ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360);
 ...
 //动画收缩
 ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0);
 ...
 //animazione di transizione di trasparenza del tick
 ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255);
 ...
 //l'animazione finale di ingrandimento e rimbalzo, cambia la larghezza del pennello per ottenere
 ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES);
 ...
 //esegue insieme l'animazione di spunta e l'animazione di ingrandimento di rimbalzo
 AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet();
 mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator);
 mFinalAnimatorSet = new AnimatorSet();
 mFinalAnimatorSet.playSequentially(mRingAnimator, mCircleAnimator, mAlphaScaleAnimatorSet);
}

Infine, il metodo onDraw() si occupa solo di disegni semplici, non di altro

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 if (!isChecked) {
  canvas.drawArc(mRectF, 90, 360, false, mPaintRing);
  canvas.drawLines(mPoints, mPaintTick);
  return;
 }
 //disegna l'arco di progresso
 canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing);
 //disegna lo sfondo giallo
 mPaintCircle.setColor(checkBaseColor);
 canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radius : 0, mPaintCircle);
 //Disegno del cerchio bianco ristretto
 if (ringProgress == 360) {
  mPaintCircle.setColor(checkTickColor);
  canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle);
 }
 //Disegno del cerchio e dell'animazione di ingrandimento e restringimento
 if (circleRadius == 0) {
  canvas.drawLines(mPoints, mPaintTick);
  canvas.drawArc(mRectF, 0, 360, false, mPaintRing);
 }
 //Sostituzione dell'animazione ObjectAnimator per il contatore
 if (!isAnimationRunning) {
  isAnimationRunning = true;
  mFinalAnimatorSet.start();
 }
}

L'effetto finale è lo stesso, la logica del codice è chiara

Penso che, durante lo sviluppo, sia utile fare una revisione periodica del proprio codice, sia per sé stesso che per la manutenzione futura.

That's all~ Grazie per aver letto, ecco di nuovo l'indirizzo del progetto su Github

> Indirizzo Github: TickView, una piccola animazione di spunta ben fattahttps://github.com/ChengangFeng/TickView

Questo è tutto ciò che ho raccolto per voi sull'implementazione di animazioni di spunta personalizzate per View, potete testare e discutere eventuali problemi nella sezione dei commenti sottostante, grazie per il supporto a Tutorial Yell.

Ti potrebbe interessare