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

Android: Eigenständige View zur Realisierung der Funktion der Markierung

Zuerst die Effektbilder

Bewegtbilder

Statische Bilder

1. Rückblick

[Android Custom View: Eine exquisite Animation zum Abhaken] Im vorherigen Artikel haben wir den Effekt des Steuerelements in der Regel implementiert, aber... aber... nach drei oder vier Tagen habe ich das selbst geschriebene Code noch einmal genau betrachtet und obwohl die Gedanken noch vorhanden sind, aber einige Teile des Codes sind nicht sofort verständlich...

Oh mein Gott, das muss sofort refaktorisiert werden! Zum Glück hat ein Freund ChangQin dies nachgemacht und ich fand, dass ich es auch so umsetzen könnte.

2. Vertiefen

Über die Gedankenweise der Kontrolle der Zeichnung, kann man das vorherige Artikel lesen, hier wird nicht analysiert. Zuerst analysieren wir einmal die Schwierigkeiten im vorherigen Artikel, wo man Verbesserungen vornehmen muss.

Nehmen wir Zeichnen des Ringfortschritts Diese Schritte zu betrachten

//Zähler
private int ringCounter = 0;
@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 if (!isChecked) {
  ...
  return;
 }
 //Den Fortschritt des Kreises zeichnen, jedes Zeichnen erhöht sich selbst12Einheiten, das bedeutet, dass der Bogen wieder umgeschlagen hat12Grad
 //Hier12Einheiten fest vorgeschrieben, und wir können später eine Konfiguration zur Selbstdefinition hinzufügen
 ringCounter += 12;
 if (ringCounter >= 360) {
  ringCounter = 360;
 }
 canvas.drawArc(mRectF, 90, ringCounter, false, mPaintRing);
 ...
 //Zwanghaftes Neuzustand
 postInvalidate();
}

Hier haben wir einen Zähler ringCounter definiert. Wenn gezeichnet wird, wird basierend auf12Einheiten erhöhen, bis360, um die Änderung des Fortschritts zu simulieren.

Nachdenken

Durch Ändern des Einheitlichen Zählers kann die Geschwindigkeit der Animation angepasst werden, was schwer zu einer zufriedenstellenden Einstellung führt. In diesem Fall können wir daran denken, dass die Geschwindigkeit der Animation durch die Kontrolle der Zeit bestimmt wird. Wenn manmit der Zeit die Geschwindigkeit der Animation steuernDas wäre viel bequemer. Animationen können in4Schritte auszuführen, wenn man jeden Animationsschritt mit einem manuell geschriebenen Zähler umsetzen möchte, dann müsste man definieren4Member-Variablen oder mehrZu viele Member-Variablen machen den Code nur noch unübersichtlicherWenn man die Animationen hinzufügen möchteInterpolator, wie man aus der obigen Analyse sehen kann, ist der manuell geschriebene Zähler nicht ausreichendIch kann das nicht akzeptieren

3. Ändern, ändern, ändern

Wie kann man das erwähnte Problem verbessern? Die Antwort ist, indem man benutzerdefinierte Attributanimationen verwendet, daher geht dieser Artikel hauptsächlich darum, benutzerdefinierte Attributanimationen zu verwenden, um die manuell geschriebenen Zähler zu ersetzen und die Klarheit der Code-Logik尽可能保证, insbesondere im onDraw()-Methoden-Code.

Ein Vorteil des Verwenden von Attributanimationen ist, dass sie Ihnen eine Vielzahl der gewünschten Werte auf der Grundlage eines Wertebereichs generiert, und in Kombination mit Interpolatoren erzielt man oft unerwartete Effekte. Auf der nächsten Seite werde ich Schritt für Schritt die Teile der Animation refaktorieren.

3.1 Zeichnen Sie den Fortschritt des Ringbalkens

Zunächst wird ein benutzerdefinierter ObjectAnimator verwendet, um den Fortschritt zu simulieren

//ringProgress ist der Name des benutzerdefinierten Attributes, der Bereich der generierten Werte ist 0 - 360, ist der Winkel eines Kreises
ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360);
//Die Dauer der Animation wird definiert, was eine gute Alternative zu der Verwendung von Inkrementen ist, um die Geschwindigkeit der Animation zu steuern
mRingAnimator.setDuration(mRingAnimatorDuration);
//Der Interpolationsfilter ist vorläufig nicht erforderlich
mRingAnimator.setInterpolator(null);

Für den benutzerdefinierten Eigenschafts-Animator müssen auch die entsprechenden Setter und Getter konfiguriert werden, da der Setter während der Ausführung der Animation nach dem entsprechenden Setter sucht, um den entsprechenden Wert zu ändern.

private int getRingProgress() {
 return ringProgress;
}
private void setRingProgress(int ringProgress) {
 //Wenn die Animation ausgeführt wird, wird der Setter aufgerufen
 //Hier können wir die von der Animation generierten Werte aufzeichnen und sie in einer Variablen speichern, um sie in ondraw zu verwenden
 this.ringProgress = ringProgress;
 //Denken Sie daran, neu zu zeichnen
 postInvalidate();
}

Schließlich wird in onDraw() gezeichnet

//Zeichnen Sie den Kurvenfortschritt canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing);

3.2 Zeichnen Sie die Animation des Zusammenziehens zum Zentrum des Kreises

Gleichzeitig wird auch ein Eigenschafts-Animator erstellt

//Hier wird die Radiusverringerung des Kreises als benutzerdefiniertes Attribut angegeben
ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0);
//Fügen Sie einen Brems-Interpolationsfilter hinzu
mCircleAnimator.setInterpolator(new DecelerateInterpolator());
mCircleAnimator.setDuration(mCircleAnimatorDuration);

setter/Getter ist ähnlich und wird nicht erwähnt

Schließlich wird in onDraw() gezeichnet

//Zeichnen Sie den Hintergrund
mPaintCircle.setColor(checkBaseColor);
canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radius : 0, mPaintCircle);
//Wenn der Fortschrittsring gezeichnet ist, zeichnen Sie dann den sich zusammenziehenden Kreis
if (ringProgress == 360) {
 mPaintCircle.setColor(checkTickColor);
 canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle);
}

3.3 Zeichnen Sie den Effekt von Zeichnen, Vergrößern und Wiederaufrichten

Dies sind zwei unabhängige Effekte, die hier gleichzeitig ausgeführt werden, daher werde ich sie zusammen erwähnen

Zunächst wird auch ein Eigenschafts-Animator definiert

//Transparentes Verlaufen der durchgestrichenen Alpha-Werte
ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255);
mAlphaAnimator.setDuration(200);
//Die letztendliche Vergrößerung und das Wiederaufladen der Animation ändern die Breite des Stifts
//Die Breite des Stifts variiert im Bereich
//Zunächst beginnt man mit der Initialbreite, geht zur n-fachen Initialbreite und kehrt schließlich zur ursprünglichen Breite zurück
ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES);
mScaleAnimator.setInterpolator(null);
mScaleAnimator.setDuration(mScaleAnimatorDuration);
//Die Animationen zum Ankreuzen und Vergrößern werden gleichzeitig ausgeführt
AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet();
mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator);

getter/setter

private int getTickAlpha() {
 return 0;
}
private void setTickAlpha(int tickAlpha) {
 //Die Transparenz kann ohne Variable gespeichert werden
 //Setze den Wert der Transparenz direkt im Stift
 mPaintTick.setAlpha(tickAlpha);
 postInvalidate();
}
private float getRingStrokeWidth() {
 return mPaintRing.getStrokeWidth();
}
private void setRingStrokeWidth(float strokeWidth) {
 //Die Breite des Stifts kann ohne Variable gespeichert werden
 //Setze die Breite des Stifts direkt im Stift
 mPaintRing.setStrokeWidth(strokeWidth);
 postInvalidate();
}

Am Ende wird ebenfalls in onDraw() gezeichnet

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

3.4 Führe die Animationen in der Reihenfolge aus

Für mehrere Animationen kann AnimatorSet verwendet werden, wobei playTogether() gleichzeitig und playSequentially() schritt für Schritt ausgeführt wird

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

Führe die Animation schließlich in onDraw() aus

//Hier wird ein Bezeichner definiert, der dem Programm mitteilt, dass die Animation每次 nur einmal ausgeführt werden kann
if (!isAnimationRunning) {
 isAnimationRunning = true;
 //Führe die Animation aus
 mFinalAnimatorSet.start();
}

3.5 Jeder Methode sollte eine einzige Aufgabe zugeordnet werden

Wenn ich den Code für die Eigenschaftsanimation in onDraw() platzieren würde, würde mir das chaotisch vorkommen, und wenn ich genauer hinsehe, sind diese Eigenschaftsanimationen nicht dynamisch erforderlich. Warum nicht extrahieren und sofort initialisieren?

Somit werden wir den Code für die Eigenschaftsanimation extrahieren und in den Konstruktor einfügen

public TickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 ...
 initAnimatorCounter();
}
/**
 * Verwende ObjectAnimator, um einige Zähler zu initialisieren
 */
private void initAnimatorCounter() {
 //Kreisfortschritt
 ObjectAnimator mRingAnimator = ObjectAnimator.ofInt(this, "ringProgress", 0, 360);
 ...
 //Schrumpfungsanimation
 ObjectAnimator mCircleAnimator = ObjectAnimator.ofInt(this, "circleRadius", radius - 5, 0);
 ...
 //Transparentes Verlaufen der durchgestrichenen Alpha-Werte
 ObjectAnimator mAlphaAnimator = ObjectAnimator.ofInt(this, "tickAlpha", 0, 255);
 ...
 //Die letztendliche Vergrößerung und das Wiederaufladen der Animation ändern die Breite des Stifts
 ObjectAnimator mScaleAnimator = ObjectAnimator.ofFloat(this, "ringStrokeWidth", mPaintRing.getStrokeWidth(), mPaintRing.getStrokeWidth() * SCALE_TIMES, mPaintRing.getStrokeWidth() / SCALE_TIMES);
 ...
 //Die Animationen zum Ankreuzen und Vergrößern werden gleichzeitig ausgeführt
 AnimatorSet mAlphaScaleAnimatorSet = new AnimatorSet();
 mAlphaScaleAnimatorSet.playTogether(mAlphaAnimator, mScaleAnimator);
 mFinalAnimatorSet = new AnimatorSet();
 mFinalAnimatorSet.playSequentially(mRingAnimator, mCircleAnimator, mAlphaScaleAnimatorSet);
}

Schließlich ist die onDraw()-Methode verantwortlich für einfache Zeichnungen und kümmert sich um nichts anderes

@Override
protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 if (!isChecked) {
  canvas.drawArc(mRectF, 90, 360, false, mPaintRing);
  canvas.drawLines(mPoints, mPaintTick);
  return;
 }
 //Zeichne den Fortschrittsbogen
 canvas.drawArc(mRectF, 90, ringProgress, false, mPaintRing);
 //Zeichne einen gelben Hintergrund
 mPaintCircle.setColor(checkBaseColor);
 canvas.drawCircle(centerX, centerY, ringProgress == 360 ? radius : 0, mPaintCircle);
 //Zeichnen Sie den schrumpfenden weißen Kreis
 if (ringProgress == 360) {
  mPaintCircle.setColor(checkTickColor);
  canvas.drawCircle(centerX, centerY, circleRadius, mPaintCircle);
 }
 //Zeichnen Sie ein Häkchen und eine Animation zur Vergrößerung und Verkleinerung
 if (circleRadius == 0) {
  canvas.drawLines(mPoints, mPaintTick);
  canvas.drawArc(mRectF, 0, 360, false, mPaintRing);
 }
 //ObjectAnimator-Animation ersetzt Zähler
 if (!isAnimationRunning) {
  isAnimationRunning = true;
  mFinalAnimatorSet.start();
 }
}

Das Endresultat ist gleich, die Code-Logik ist klar erkennbar

Also, ich denke, es ist sehr hilfreich, während der Entwicklung gelegentlich seinen eigenen Code zu überprüfen, egal ob es sich um sich selbst oder um die zukünftige Wartung handelt.

Das ist alles~ Vielen Dank, dass Sie gelesen haben. Hier noch einmal die GitHub-Adresse des Projekts:

> GitHub-Adresse: TickView, eine feine kleine Animation zum Markierenhttps://github.com/ChengangFeng/TickView

Das ist alles~ Vielen Dank, dass Sie gelesen haben. Hier noch einmal die GitHub-Adresse des Projekts:

Möchten Sie auch mögen