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

Implémentation de l'effet de barre de progression à effet de bascule personnalisée pour Android

J'ai vu un composant IOS PendulumView sur Internet, qui réalise l'effet d'animation de l'horloge. Comme les barres de progression natives ne sont pas très jolies, je veux pouvoir créer une View personnalisée pour réaliser un tel effet, qui peut également être utilisé pour les barres de progression de chargement des pages à l'avenir. 

Pas de bavardage, montrons d'abord l'effet visuel

 

La bordure noire en bas est accidentellement enregistrée pendant l'enregistrement et peut être ignorée. 

Puisque c'est une View personnalisée, nous suivons le processus standard, premier étape, créer des propriétés personnalisées 

Propriétés personnalisées 

Créer un fichier de propriétés 

Dans le projet Android, dans le répertoire res->values" sous-dossier, créez un fichier attrs.xml avec le contenu suivant :

 <?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="PendulumView">
  <attr name="globeNum" format="integer"/>
  <attr name="globeColor" format="color"/>
  <attr name="globeRadius" format="dimension"/>
  <attr name="swingRadius" format="dimension"/>
 </declare-styleable>
</resources>

Parmi eux, declare-L'attribut name de styleable est utilisé pour faire référence à ce fichier de propriétés dans le code. L'attribut name, généralement, est le nom de la classe de notre View personnalisée, ce qui est assez intuitif.

L'utilisation de styleale permet au système de nous accomplir beaucoup d'écritures constantes (tableau int[], constante d'index) et autres, simplifiant notre travail de développement, par exemple, le R.styleable.PendulumView_golbeNum utilisé dans le code suivant est généré automatiquement par le système pour nous. 

La propriété globeNum représente le nombre de boules, globeColor représente la couleur des boules, globeRadius représente le rayon des boules, swingRadius représente le rayon de balancement 

Lire les valeurs des attributs 

Lire les valeurs des attributs dans le constructeur personnalisé de la vue 

Il est également possible d'obtenir les valeurs des attributs via AttributeSet, mais si les valeurs des attributs sont de type référence, seule l'ID est obtenu, il est nécessaire de continuer à analyser l'ID pour obtenir la véritable valeur de l'attribut, et TypedArray nous aide directement à accomplir cette tâche. 

public PendulumView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //Utiliser TypedArray pour lire les valeurs des attributs personnalisés
    TypedArray ta = context.getResources().obtainAttributes(attrs, R.styleable.PendulumView);
    int count = ta.getIndexCount();
    for (int i = 0; i < count; i++) {
      int attr = ta.getIndex(i);
      switch (attr) {
        case R.styleable.PendulumView_globeNum:
          mGlobeNum = ta.getInt(attr, 5);
          break;
        case R.styleable.PendulumView_globeRadius:
          mGlobeRadius = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 16, getResources().getDisplayMetrics()));
          break;
        case R.styleable.PendulumView_globeColor:
          mGlobeColor = ta.getColor(attr, Color.BLUE);
          break;
        case R.styleable.PendulumView_swingRadius:
          mSwingRadius = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 16, getResources().getDisplayMetrics()));
          break;
      }
    }
    ta.recycle(); //Pour éviter les problèmes lors de la prochaine lecture
    mPaint = new Paint();
    mPaint.setColor(mGlobeColor);
  }

Redéfinir la méthode OnMeasure() 

@Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    //la hauteur est le rayon des billes+rayon de swing
    int height = mGlobeRadius + mSwingRadius;
    //la largeur est2*rayon de swing+(nombre de billes-1)*diamètre des billes
    int width = mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1) + mSwingRadius;
    //Si le mode de mesure est EXACTLY, utilisez directement la valeur recommandée, sinon (généralement, traiter le cas wrap_content), utilisez la largeur et la hauteur calculées par vous-même
    setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize : width, (heightMode == MeasureSpec.EXACTLY) ? heightSize : height);
  }


 int height = mGlobeRadius + mSwingRadius;
<pre name="code" class="java">int width = mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1) + mSwingRadius;
Utilisé pour traiter le mode de mesure AT_MOST, généralement, la largeur et la hauteur personnalisées de View sont définies sur wrap_content, dans ce cas, la largeur et la hauteur de View sont calculées à partir du nombre de billes, du rayon et du rayon de swing, comme illustré dans la figure suivante : 

par le nombre de billes5Par exemple, la taille de View est la zone rouge rectangulaire de la figure suivante 

Redéfinir la méthode onDraw() 

@Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //Dessiner les autres billes sauf les deux billes de chaque côté
    for (int i = 0; i < mGlobeNum - 2; i++) {
      canvas.drawCircle(mSwingRadius + (i + 1) * 2 * mGlobeRadius, mSwingRadius, mGlobeRadius, mPaint);
    }
    if (mLeftPoint == null || mRightPoint == null) {
      //Initialiser les coordonnées des deux billes les plus à gauche et à droite
      mLeftPoint = new Point(mSwingRadius, mSwingRadius);
      mRightPoint = new Point(mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1), mSwingRadius);
      //Démarrer l'animation de balancement
      startPendulumAnimation();
    }
    //Dessiner les deux billes de chaque côté
    canvas.drawCircle(mLeftPoint.x, mLeftPoint.y, mGlobeRadius, mPaint);
    canvas.drawCircle(mRightPoint.x, mRightPoint.y, mGlobeRadius, mPaint);
  }

La méthode onDraw() est la clé de la vue personnalisée, où l'effet d'affichage de la vue est dessiné. Le code dessine d'abord les autres billes sauf les deux billes les plus à gauche et à droite, puis juge les valeurs des coordonnées des deux billes à gauche et à droite. Si c'est la première fois que le dessin est effectué, les valeurs des coordonnées sont vides, alors initialisez les coordonnées des deux billes et démarrez l'animation. Enfin, dessinez les deux billes à gauche et à droite à l'aide des valeurs x, y de mLeftPoint et mRightPoint. 

où mLeftPoint et mRightPoint sont des objets android.graphics.Point, utilisés uniquement pour stocker les coordonnées x, y des deux petites billes de chaque côté. 

Utilisation de l'animation d'attribut 

public void startPendulumAnimation() {
    //Utilisation de l'animation d'attribut
    final ValueAnimator anim = ValueAnimator.ofObject(new TypeEvaluator() {
      @Override
      public Object evaluate(float fraction, Object startValue, Object endValue) {
        //Le paramètre fraction est utilisé pour représenter l'avancement de l'animation, nous le calculons en fonction de lui pour obtenir la valeur actuelle de l'animation
        double angle = Math.toRadians(90 * fraction);
        int x = (int) ((mSwingRadius - mGlobeRadius) * Math.sin(angle));
        int y = (int) ((mSwingRadius - mGlobeRadius) * Math.cos(angle));
        Point point = new Point(x, y);
        return point;
      }
    }, new Point(), new Point());
    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        Point point = (Point) animation.getAnimatedValue();
        //Obtenir la valeur fraction actuelle
        float fraction = anim.getAnimatedFraction();
        //Juger si fraction diminue d'abord puis augmente, c'est-à-dire si elle est dans l'état de se balancer vers le haut
        //Changer la balle chaque fois qu'elle va se balancer vers le haut
        if (lastSlope && fraction > mLastFraction) {
          isNext = !isNext;
        }
        //Obtenir l'effet d'animation en modifiant constamment les coordonnées x, y des balles gauche et droite
        //Utiliser isNext pour déterminer si la balle gauche doit bouger ou la balle droite
        if (isNext) {
          //Lorsque la balle gauche oscille, la balle droite est placée à l'emplacement initial
          mRightPoint.x = mSwingRadius + mGlobeRadius * 2 * (mGlobeNum - 1);
          mRightPoint.y = mSwingRadius;
          mLeftPoint.x = mSwingRadius - point.x;
          mLeftPoint.y = mGlobeRadius + point.y;
        } else {
          //Lorsque la balle droite oscille, la balle gauche est placée à l'emplacement initial
          mLeftPoint.x = mSwingRadius;
          mRightPoint.y = mSwingRadius;
          mRightPoint.x = mSwingRadius + (mGlobeNum - 1) * mGlobeRadius * 2 + point.x;
          mRightPoint.y = mGlobeRadius + point.y;
        }
        invalidate();
        lastSlope = fraction < mLastFraction;
        mLastFraction = fraction;
      }
    });
    //Définir la lecture en boucle illimitée
    anim.setRepeatCount(ValueAnimator.INFINITE);
    //Définir le mode de répétition en lecture inverse
    anim.setRepeatMode(ValueAnimator.REVERSE);
    anim.setDuration(200);
    //Définir l'interpolateur, contrôler la vitesse de variation de l'animation
    anim.setInterpolator(new DecelerateInterpolator());
    anim.start();
  }

 où l'utilisation de ValueAnimator.ofObject permet de manipuler l'objet Point, de manière plus concrète et illustrative. De plus, l'utilisation de ofObject avec un objet TypeEvaluator personnalisé permet d'obtenir la valeur fraction, qui est un nombre décimal allant de 0-1Le nombre décimal de variation. Par conséquent, les deux derniers paramètres startValue (new Point()) et endValue (new Point()) n'ont pas de sens réel, ils peuvent également être omis directement. L'écriture ici est principalement pour faciliter la compréhension. De même, ValueAnimator.ofFloat(0f, 1f) pour obtenir un nombre décimal de variation allant de 0-1Le nombre décimal de variation.

     final ValueAnimator anim = ValueAnimator.ofObject(new TypeEvaluator() {
      @Override
      public Object evaluate(float fraction, Object startValue, Object endValue) {
        //Le paramètre fraction est utilisé pour représenter l'avancement de l'animation, nous le calculons en fonction de lui pour obtenir la valeur actuelle de l'animation
        double angle = Math.toRadians(90 * fraction);
        int x = (int) ((mSwingRadius - mGlobeRadius) * Math.sin(angle));
        int y = (int) ((mSwingRadius - mGlobeRadius) * Math.cos(angle));
        Point point = new Point(x, y);
        return point;
      }
    }, new Point(), new Point());

Par fraction, nous calculons la valeur de variation de l'angle de balancement de la balle, 0-90 degrés

 

mSwingRadius-mGlobeRadius représente la longueur de la ligne verte dans l'image, la trajectoire de balancement, la trajectoire de la balle centrale est une ligne tracée par un cercle de rayon(mSwingRadius-mGlobeRadius)est une courbe circulaire de rayon variable,la valeur de X changeante est(mSwingRadius-mGlobeRadius)*sin(angle),la valeur de y changeante est(mSwingRadius-mGlobeRadius)*cos(angle) 

La coordonnée centrale réelle de la balle correspondante est (mSwingRadius-x,mGlobeRadius+y) 

La trajectoire de la balle de droite est similaire à celle de la balle de gauche, mais la direction est différente. La coordonnée centrale réelle de la balle de droite (mSwingRadius + (mGlobeNum - 1) * mGlobeRadius * 2 + x,mGlobeRadius+y) 

Il est visible que les coordonnées verticales des billes de chaque côté sont les mêmes, mais les coordonnées horizontales sont différentes. 

        float fraction = anim.getAnimatedFraction();
        //Juger si fraction diminue d'abord puis augmente, c'est-à-dire si elle est dans l'état de se balancer vers le haut
        //Changer la balle chaque fois qu'elle va se balancer vers le haut
        if (lastSlope && fraction > mLastFraction) {
          isNext = !isNext;
        }
        //Enregistrer si la fraction précédente diminue sans cesse
        lastSlope = fraction < mLastFraction;
        //Enregistrer la fraction précédente
        mLastFraction = fraction;

 Ces deux lignes de code sont utilisées pour calculer quand il faut changer la balle en mouvement, cette animation est configurée pour une lecture en boucle, et le mode de lecture en boucle est en ordre inverse, donc un cycle de l'animation est le processus de lancer la balle et de la faire tomber. Pendant ce processus, la valeur de fraction change de 0 à1et ensuite par1devient 0. Alors, quand est-ce que le début d'un nouveau cycle d'animation ? C'est lorsque le ballon est sur le point d'être lancé. À ce moment-là, basculez le ballon en mouvement pour réaliser l'effet d'animation où le ballon de gauche tombe et le ballon de droite est lancé, et vice versa. 

Alors, comment attraper ce point de temps ? 

Lorsque le ballon est lancé, la valeur de fraction augmente constamment, et lorsque le ballon tombe, la valeur de fraction diminue constamment. Le moment où le ballon est sur le point d'être lancé est le moment où fraction passe de la diminution constante à l'augmentation constante. Le code enregistre si fraction était en diminution la dernière fois, puis compare si fraction est en augmentation cette fois, si les deux conditions sont remplies, alors basculez le ballon en mouvement. 

    anim.setDuration(200);
    //Définir l'interpolateur, contrôler la vitesse de variation de l'animation
    anim.setInterpolator(new DecelerateInterpolator());
    anim.start();

Définir la durée de l'animation de200 millisecondes, le lecteur peut modifier cette valeur pour modifier la vitesse de balancement du ballon.

Définir l'interpolateur d'animation, car le lancer du ballon est un processus de décélération progressive, et la chute est un processus d'accélération progressive, donc utiliser DecelerateInterpolator pour réaliser l'effet de décélération, et l'effet d'accélération lors de la lecture à l'envers. 

L'animation de démarrage, l'effet de bascule personnalisé de la vue de progression est réalisé ! Courez-y, voyez l'effet !

C'est tout pour cet article, j'espère que cela aidera à votre apprentissage, et j'espère que vous soutiendrez également le tutoriel de cri.

Déclaration : Le contenu de cet article est issu du réseau, propriété de l'auteur original, partagé par les utilisateurs d'Internet et téléversé spontanément. Ce site n'est pas propriétaire des droits d'auteur, n'a pas été édité par l'homme, et n'assume aucune responsabilité juridique connexe. Si vous trouvez du contenu suspect de violation de droits d'auteur, veuillez envoyer un e-mail à : notice#oldtoolbag.com (veuillez remplacer # par @ lors de l'envoi d'un e-mail pour signaler une violation, et fournir des preuves pertinentes. Une fois vérifié, ce site supprimera immédiatement le contenu suspect de violation de droits d'auteur.)

Vous pourriez aussi aimer