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