9 Stimmen

Wie man in Android auf einer Leinwand mit wechselnder Farbe einen Farbverlauf malt?

Ich muss den Hintergrund meines Canvas mit einer Farbe und Schattierung/Gradienten bemalen, aber bei jedem onDraw-Aufruf möchte ich möglicherweise die Farbe ändern.

Ich habe Schwierigkeiten, dies zu tun, ohne bei jedem onDraw-Aufruf ein neues Objekt zu erstellen. Hat jemand Ideen? Wenn ich drawPaint() verwende und meinem Farbobjekt einen neuen Shader() setze, habe ich ein neues Shader-Objekt erstellt, und wenn ich ein neues GradientDrawable() erstelle, auch. Ich möchte GC vermeiden.

I dachte, ich könnte ein GradientDrawable() Objekt wiederverwenden und die .setColor() Methode aufrufen, aber das setzt lediglich alle damit verbundenen Gradientendaten zurück und malt das Drawable als diese feste Farbe.

Jemand?

6voto

Romain Guy Punkte 96489

Leider müssen Sie jedes Mal einen neuen LinearGradient erstellen. Beachten Sie, dass Sie keinen GradientDrawable verwenden müssen, Sie können die Form selbst mit einem Paint zeichnen, auf dem Sie den Shader einstellen.

0 Stimmen

Vielen Dank, Romain, könntest du bitte deine Expertise in meinem Kommentar zu campnic teilen? Ich frage mich, welche Methode .draw...() am effizientesten wäre, um entweder die beiden benötigten Ebenen für diesen Effekt zu zeichnen oder ob LayerDrawable genau das ist, was ich brauche.

0 Stimmen

@Romain Guy Ich habe versucht, SweepGradient zu verwenden, um den konischen Farbverlaufseffekt zu erzielen. Aber anscheinend funktioniert es nur mit zwei Farben. Wenn ich ein vierfarbiges Array verwende, scheint SweepGradient nicht wie erwartet zu funktionieren. Ich habe verschiedene Kombinationen von positions-Werten ausprobiert und auch versucht, die Positionen als NULL festzulegen, aber die meiste Zeit erhalte ich nur zwei Farben oder einen Farbverlauf, der nicht konisch ist. Jede Hilfe wäre sehr willkommen.

0voto

Nick Campion Punkte 10449

Ich denke, was du tun möchtest, ist das hier. Erstelle ein LayerDrawable und füge dann zwei Drawables darin ein. Das erste Drawable, das du einfügst, sollte seine Farbe auf die Farbe setzen, die du als Hintergrund haben möchtest, und das zweite sollte ein Gradient sein, der auf Schwarz eingestellt ist und von Alpha 0 auf Alpha 255 übergeht.

Weitere Informationen finden Sie unter: http://developer.android.com/guide/topics/resources/drawable-resource.html#LayerList

0 Stimmen

Campnic - danke, das ist ungefähr das, was ich im Sinn hatte, als ich daran dachte, canvas.drawARGB() mit dem Alpha-Wert xFF aufzurufen und dann einen Malpinsel mit Shader oder Verlauf mit geringerem Alpha darüber zu legen. Ich mache mir nur Sorgen um die Effizienz. Ich wollte das gesamte Canvas vermeiden, um es mit zwei Ebenen zu zeichnen, und wollte wissen, ob es eine integrierte Konstruktion gibt, um dies auf einmal zu tun. Erreicht dies der LayerDrawable oder erfolgt das tatsächlich schichtweise durch sequentielles Zeichnen?

0 Stimmen

Ich habe ein wenig recherchiert, und anscheinend würde es darauf hinauslaufen, dass du zwei vollständige Ebenen über den gesamten Bildschirm zeichnest. Der Code für LayerDrawable.draw durchläuft einfach die untergeordneten Drawables in absteigender Indexreihenfolge. Ich denke, die Leistungsfähigkeit des LayerDrawable ist vielleicht nicht das, was du verwenden möchtest. Romain hat viel mehr Erfahrung mit diesem Thema, also kann er dir wahrscheinlich eine bessere Vorstellung geben :)

0 Stimmen

Ich schätze es wirklich, dass du dir das anschaust - das ist auch das, was ich gefunden habe und vermute, als ich die Dokumentation durchgesehen habe. Ich muss mir etwas Kreatives für einen schönen dynamischen Hintergrund überlegen, wenn es keine eingebaute Lösung gibt.

0voto

lewylewus Punkte 1

Diese Frage ist fast in der Mitte des Jahres 2017 immer noch gültig. Ich wollte einen reibungslosen Übergang für Farbänderungen in meiner benutzerdefinierten Ansicht erstellen. Ich habe Shader mit LinearGradients verwendet.

Meine Absicht war es, einen reibungslosen Übergang zwischen dem aktivierten und deaktivierten Zustand meiner Ansicht zu schaffen.

Mein Fall ist aufgrund des Aufbaus und des Zwecks der Ansicht noch komplizierter. Es ist ein Schieberegler. Meine Ansicht besteht daher aus fast zwei identischen Ebenen, die mit Canvas übereinander gezeichnet sind. Das dritte Element ist ein Zeiger.

Beide Ebenen verwenden LinearGradients, der Zeiger verwendet eine einzige Farbe.

Zusammenfassend muss ich also gleichzeitig einen Übergang zwischen 5 Farben durchführen:

  • Start- und Endfarbe für untere Ebene - ein LinearGradient,
  • Start- und Endfarbe für obere Ebene - zweiter LinearGradient,
  • Zeigerfarbe - normale Farbe;

Die Lösung dafür besteht darin, 5 separate ValueAnimators zu haben. Das Wichtigste ist, ValueAnimator.ofArgb() zu verwenden:

private ValueAnimator frontStartColorAnimator;
private ValueAnimator frontEndColorAnimator;
private ValueAnimator bottomStartColorAnimator;
private ValueAnimator bottomEndColorAnimator;
private ValueAnimator pointerColorAnimator;
private AnimatorSet colorsAnimatorSet;

...

frontStartColorAnimator = ValueAnimator.ofArgb(frontLayerStartColor, frontLayerDisabledStartColor);
frontStartColorAnimator.setDuration(animationDuration);
frontStartColorAnimator.setInterpolator(new LinearInterpolator());
frontStartColorAnimator.addUpdateListener(animation
            -> shaderFrontLayerStartColor = (int) animation.getAnimatedValue());

frontEndColorAnimator = ValueAnimator.ofArgb(frontLayerEndColor, frontLayerDisabledEndColor);
frontEndColorAnimator.setDuration(animationDuration);
frontEndColorAnimator.setInterpolator(new LinearInterpolator());
frontEndColorAnimator.addUpdateListener(animation -> {
        shaderFrontLayerEndColor = (int) animation.getAnimatedValue();
        frontLayerPaint.setShader(new LinearGradient(0, 0, getWidth(), 0, shaderFrontLayerStartColor,
                shaderFrontLayerEndColor, Shader.TileMode.CLAMP));
    });

bottomStartColorAnimator = ValueAnimator.ofArgb(bottomLayerStartColor, bottomLayerDisabledStartColor);
bottomStartColorAnimator.setDuration(animationDuration);
bottomStartColorAnimator.setInterpolator(new LinearInterpolator());
bottomStartColorAnimator.addUpdateListener(animation ->
            shaderBottomLayerStartColor = (int) animation.getAnimatedValue());

bottomEndColorAnimator = ValueAnimator.ofArgb(bottomLayerEndColor, bottomLayerDisabledEndColor);
bottomEndColorAnimator.setDuration(animationDuration);
bottomEndColorAnimator.setInterpolator(new LinearInterpolator());
bottomEndColorAnimator.addUpdateListener(animation -> {
        shaderBottomLayerEndColor = (int) animation.getAnimatedValue();
        backLayerPaint.setShader(new LinearGradient(0, 0, 0, getHeight(), shaderBottomLayerStartColor,
                shaderBottomLayerEndColor, Shader.TileMode.CLAMP));
    });

pointerColorAnimator = ValueAnimator.ofArgb(pointerEnabledColor, pointerDisabledColor);
pointerColorAnimator.setDuration(animationDuration);
pointerColorAnimator.setInterpolator(new LinearInterpolator());
pointerColorAnimator.addUpdateListener(animation -> {
        pointerColor = (int) animation.getAnimatedValue();
        pointerPaint.setColor(pointerColor);
    });

colorsAnimatorSet = new AnimatorSet();
colorsAnimatorSet.playTogether(frontStartColorAnimator, frontEndColorAnimator,
            bottomStartColorAnimator, bottomEndColorAnimator, pointerColorAnimator);

...

colorsAnimatorSet.start();

Ich war zu Beginn auch besorgt darüber, bei jedem Animationsupdate einen neuen LinearGradient zu erstellen, aber dann habe ich das GPU-Profilieren auf dem Gerät mit "Auf dem Bildschirm als Balken" gestartet. Alles läuft gut und innerhalb eines sicheren Bereichs.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X