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

View personnalisé Android pour la fonction d'animation de coche

D'abord, montrez les effets visuels

Animation

Image statique

1. Réflexion

[Android View personnalisé : une petite animation de coche soignée] Dans l'article précédent, nous avons基本上实现了控件的效果,但是...但是...经过三四天后,仔细看回自己写的代码,虽然思路还在,但是部分代码还是不能一下子的看得明白...

Mon dieu, cela doit être refondu immédiatement ! Heureusement, un ami simple ChangQin a imité l'écriture de ce contrôle, après avoir regardé, je pense que je peux aussi le réaliser ainsi.

2. Réflexion

à propos de la pensée de dessin du contrôle des widgets, vous pouvez consulter l'article précédent, ici nous ne l'analysons plus. D'abord, analysons les problèmes persistants des widgets dans l'article précédent, quels endroits doivent être améliorés.

Prenez dessiner la progression du cercle Pour voir cette étape

//compteur
private int ringCounter = 0;
@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 if (!isChecked) {
  ...
  return;
 }
 //dessiner l'arc de progression, chaque dessin est ajouté12unité, c'est-à-dire que l'arc a de nouveau balayé12degré
 //ici12unité est écrite à l'avance, et après, nous pouvons réaliser une configuration personnalisée
 ringCounter += 12;
 si ringCounter >= 360) {
  ringCounter = 360;
 }
 canvas.drawArc(mRectF, 90, ringCounter, false, mPaintRing);
 ...
 //redessiner强制
 postInvalidate();
}

Ici, nous avons défini un compteur ringCounter, lorsque le dessin est effectué, il est basé sur12unité pour l'auto-incrément atteindre360, pour simuler le changement de progression.

Réfléchissez bien

En changeant l'unité d'auto-incrément pour contrôler le changement de la vitesse de l'animation, ce qui est difficile à ajuster à votre satisfaction. À ce moment-là, nous pouvons penser que le contrôle de la vitesse d'exécution de l'animation rapide ou lente est en fait de contrôler le temps. Si vous pouvezen utilisant le temps pour contrôler la vitesse de l'animationça serait beaucoup plus pratique. L'animation est divisée4étapes d'exécution, si chaque étape de l'animation doit être réalisée à l'aide d'un compteur manuel, il faudra définir4variables membres ou plusTrop de variables membres rendront le code encore plus confusSi l'animation doit être ajoutéeInterpolateur, le compteur manuel écrit à la main ne peut tout simplement pas répondre aux analyses mentionnées ci-dessusJe ne peux pas l'accepter

3. Modifiez, modifiez, modifiez

Alors, comment améliorer le problème mentionné précédemment ? La réponse est d'utiliser l'animation des propriétés personnalisée pour le résoudre. Donc, l'objet principal de cet article est d'utiliser l'animation des propriétés pour remplacer le compteur manuel, en veillant à ce que la logique du code soit aussi claire que possible, en particulier dans la méthode onDraw().

L'un des avantages de l'animation des propriétés est qu'elle génère automatiquement une série de valeurs que vous souhaitez, en fonction d'une plage de valeurs donnée, et en combinaison avec un interpolateur, elle peut produire des effets inattendus. Dans le prochain paragraphe, nous allons réorganiser étape par étape les parties de l'animation.

3.1 Dessiner la barre de progression circulaire

Tout d'abord, utiliser ObjectAnimator personnalisé pour simuler la progression

//ringProgress est le nom de l'attribut personnalisé, la gamme de valeurs générées est de 0 - 360, c'est-à-dire l'angle d'un cercle
ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360);
//Définir le temps d'exécution de l'animation, ce qui est un bon substitut pour le contrôle de la vitesse de l'animation par unité auto-incrementée auparavant
mRingAnimator.setDuration(mRingAnimatorDuration);
//Pour le moment, il n'est pas nécessaire d'utiliser un interpolateur
mRingAnimator.setInterpolator(null);

Pour la création d'animations des propriétés personnalisées, il faut configurer les setter et getter correspondants, car pendant l'exécution de l'animation, il trouvera le setter pour modifier la valeur correspondante.

private int getRingProgress() {
 return ringProgress;
}
private void setRingProgress(int ringProgress) {
 //Lors de l'exécution de l'animation, il appelle le setter
 //Ici, nous pouvons enregistrer les valeurs générées par l'animation, les stocker dans des variables et les utiliser dans onDraw
 this.ringProgress = ringProgress;
 //N'oubliez pas de redessiner
 postInvalidate();
}

Dessiner finalement dans onDraw()

//Dessiner l'arc de progression circulaire canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing);

3.2 Dessiner l'animation de contraction vers le centre du cercle

De même, créer une animation des propriétés

//L'attribut personnalisé ici est le rayon de contraction du cercle
ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0);
//Ajouter un interpolateur de décélération
mCircleAnimator.setInterpolator(new DecelerateInterpolator());
mCircleAnimator.setDuration(mCircleAnimatorDuration);

setter/Le getter est similaire et ne sera pas mentionné

Dessiner finalement dans onDraw()

//Dessiner le fond
mPaintCircle.setColor(checkBaseColor);
canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radius : 0, mPaintCircle);
//Une fois que le cercle de progression est dessiné, dessiner un cercle de contraction
if (ringProgress == 360) {
 mPaintCircle.setColor(checkTickColor);
 canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle);
}

3.3 Dessiner l'effet de poulie et d'agrandissement puis de rebond

Ce sont deux effets indépendants, ici ils sont exécutés en même temps, je les ai donc regroupés

Définir d'abord l'animation des propriétés

//transparence progressive de l'animation de coche
ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255);
mAlphaAnimator.setDuration(200);
//La dernière animation d'agrandissement et de rebond, modifie la largeur du pinceau pour réaliser
//Quant à la largeur du pinceau, la gamme des changements est
//Commencer par la largeur d'initialisation, puis multiplier par n la largeur d'initialisation, et finalement revenir à la largeur d'initialisation
ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES);
mScaleAnimator.setInterpolator(null);
mScaleAnimator.setDuration(mScaleAnimatorDuration);
//l'animation de coche et d'agrandissement rebondissent ensemble
AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet();
mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator);

getter/setter

private int getTickAlpha() {
 return 0;
}
private void setTickAlpha(int tickAlpha) {
 //Définir la transparence, on peut ne pas enregistrer la variable
 //Définir directement la valeur de transparence dans le pinceau
 mPaintTick.setAlpha(tickAlpha);
 postInvalidate();
}
private float getRingStrokeWidth() {
 return mPaintRing.getStrokeWidth();
}
private void setRingStrokeWidth(float strokeWidth) {
 //Définir la largeur du pinceau, on peut ne pas enregistrer la variable
 //Définir directement la largeur du pinceau dans le pinceau
 mPaintRing.setStrokeWidth(strokeWidth);
 postInvalidate();
}

Finalement, comme précédemment, dessiner dans onDraw()

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

3.4 Exécuter les animations en ordre

Pour exécuter plusieurs animations, on peut utiliser AnimatorSet, où playTogether() exécute ensemble, et playSequentially() exécute une après l'autre, étape par étape

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

Enfin, exécuter l'animation dans onDraw()

//Ici, un identificateur est défini pour informer le programme que l'animation ne peut être exécutée qu'une seule fois
if (!isAnimationRunning) {
 isAnimationRunning = true;
 //Exécuter l'animation
 mFinalAnimatorSet.start();
}

3.5 Chaque méthode devrait avoir une seule responsabilité

Si je mettais la méthode de définition des animations d'attributs dans onDraw(), je trouve ça un peu désordonné, et en regardant de plus près, ces animations d'attributs ne nécessitent pas de changements dynamiques, pourquoi ne pas les extraire et les initialiser dès le départ ?

donc, nous allons extraire le code des animations d'attributs et les initialiser dans le constructeur

public TickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 ...
 initAnimatorCounter();
}
/**
 * Initialiser quelques compteurs avec ObjectAnimator
 */
private void initAnimatorCounter() {
 //progression circulaire
 ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360);
 ...
 //animation de contraction
 ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0);
 ...
 //transparence progressive de l'animation de coche
 ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255);
 ...
 //La dernière animation d'agrandissement et de rebond, modifie la largeur du pinceau pour réaliser
 ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES);
 ...
 //l'animation de coche et d'agrandissement rebondissent ensemble
 AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet();
 mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator);
 mFinalAnimatorSet = new AnimatorSet();
 mFinalAnimatorSet.playSequentially(mRingAnimator, mCircleAnimator, mAlphaScaleAnimatorSet);
}

Enfin, la méthode onDraw() est responsable uniquement du dessin simple, sans autre considération

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 if (!isChecked) {
  canvas.drawArc(mRectF, 90, 360, false, mPaintRing);
  canvas.drawLines(mPoints, mPaintTick);
  return;
 }
 //dessiner l'arc de cercle de progression
 canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing);
 //dessiner un fond jaune
 mPaintCircle.setColor(checkBaseColor);
 canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radius : 0, mPaintCircle);
 //Dessiner un cercle blanc en rétrécissement
 if (ringProgress == 360) {
  mPaintCircle.setColor(checkTickColor);
  canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle);
 }
 //Dessiner un coche, ainsi qu'une animation d'agrandissement et de réduction
 if (circleRadius == 0) {
  canvas.drawLines(mPoints, mPaintTick);
  canvas.drawArc(mRectF, 0, 360, false, mPaintRing);
 }
 //Animation ObjectAnimator remplace le compteur
 if (!isAnimationRunning) {
  isAnimationRunning = true;
  mFinalAnimatorSet.start();
 }
}

Le résultat final est le même, la logique du code est claire

Donc, je pense que, dans le développement, il est utile de faire un review de son code à temps, que ce soit pour soi-même ou pour la maintenance future.

C'est tout~ Merci de votre lecture, je vais également vous donner l'adresse GitHub du projet.

> Adresse GitHub : TickView, une petite animation de coche soignéehttps://github.com/ChengangFeng/TickView

C'est tout~ Merci de votre lecture, je vais également vous donner l'adresse GitHub du projet.

Vous pourriez aussi aimer