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