
Ich mag Dribbble . Es gibt viele coole und inspirierende Designprojekte. Aber wenn Sie ein Entwickler sind, wird das Gefühl der Schönheit oft schnell durch Verzweiflung ersetzt, wenn Sie darüber nachdenken, wie Sie dieses coole Design implementieren können.
In diesem Artikel werde ich Ihnen ein Beispiel für ein solches Design und seine Implementierung zeigen, aber lassen Sie uns vorher über die Lösung des Problems als Ganzes sprechen.
Am einfachsten ist es, eine Bibliothek zu verwenden, die unsere Anforderungen erfüllt. Versteh mich jetzt nicht falsch, ich bin ein großer Befürworter des Ansatzes "Das Rad nicht neu erfinden". Es gibt großartige Open-Source-Bibliotheken, und wenn ich Bilder hochladen oder die REST-API implementieren muss, helfen mir Glide / Picasso und Retrofit sehr.
Wenn Sie jedoch ein ungewöhnliches Design implementieren müssen, ist dies nicht immer die beste Wahl. Sie müssen Zeit damit verbringen, nach einer guten, unterstützten Bibliothek zu suchen, die etwas Ähnliches tut. Dann müssen Sie in den Code schauen, um sicherzustellen, dass dort etwas Angemessenes geschrieben ist. Sie müssen mehr Zeit darauf verwenden, die Einstellungen und Konfigurationen zu verstehen, mit denen Sie die Bibliothek für Ihre Aufgaben verwenden können. Und seien wir ehrlich, höchstwahrscheinlich wird die Bibliothek Ihre Anforderungen nicht zu 100% decken, und Sie müssen einige Kompromisse mit Designern eingehen.
Daher sage ich, dass es oft einfacher und besser ist, eine eigene View
Komponente zu erstellen. Wenn ich "native onDraw()
" sage, meine ich die Erweiterung der View
Klasse, die die onDraw()
-Methode überschreibt und Paint
und Canvas
zum Zeichnen der View
Komponente verwendet. Dies mag beängstigend erscheinen, wenn Sie es noch nicht getan haben, da diese Klassen viele Methoden und Eigenschaften haben, aber Sie können sich auf die wichtigsten konzentrieren:
canvas.drawRect()
- canvas.drawRect()
die Koordinaten der Ecken an und zeichnen Sie ein Rechteck.
canvas.drawRoundRect()
- canvas.drawRoundRect()
optional den Radius an, und die Ecken des Rechtecks werden gerundet.
canvas.drawPath()
ist eine komplexere, aber auch leistungsstärkere Methode, um mithilfe von Linien und Kurven eine eigene Form zu erstellen.
canvas.drawText()
- zum Zeichnen von Text auf Leinwand (mit Paint
Sie die Größe, Farbe und andere Eigenschaften steuern);
canvas.drawCircle()
- canvas.drawCircle()
den Mittelpunkt und den Radius an und erhalten Sie einen Kreis.
canvas.drawArc()
- canvas.drawArc()
das Begrenzungsrechteck sowie die Start- und canvas.drawArc()
zum Zeichnen des Bogens an.
paint.style
- paint.style
an, ob die gezeichnete Form gefüllt, eingekreist oder beides sein wird.
paint.color
- gibt die Farbe an (einschließlich Transparenz);
paint.strokeWidth
- steuert die Breite für Strichformen;
paint.pathEffect
- Mit dieser paint.pathEffect
können Sie die Geometrie der gezeichneten Figur beeinflussen.
paint.shader
- Ermöglicht das Zeichnen von Verläufen.
Denken Sie daran, dass Sie manchmal andere APIs verwenden müssen, aber selbst wenn Sie diese Methoden beherrschen, können Sie sehr komplexe Formen zeichnen.
Praktisches Beispiel
Hier ist ein Design, das Pepper uns anbietet:

Hier gibt es viele interessante Dinge, aber lassen Sie uns alles in kleine Stücke zerlegen.
Schritt 1. Berechnen Sie die Markerpositionen
private fun calcPositions(markers: List<Marker>) { val max = markers.maxBy { it.value } val min = markers.minBy { it.value } pxPerUnit = chartHeight / (max - min) zeroY = max * pxPerUnit + paddingTop val step = (width - 2 * padding - scalesWidth) / (markers.size - 1) for ((i, marker) in markers.withIndex()) { val x = step * i + paddingLeft val y = zeroY - entry.value * pxPerUnit marker.currentPos.x = x marker.currentPos.y = y } }
Wir finden die Minimal- und Maximalwerte, berechnen das Verhältnis der Pixel pro Einheit, die horizontale Schrittgröße zwischen den Markierungen und den X- und Y-Positionen.
Schritt 2. Zeichnen Sie einen Farbverlauf

Wir erstellen eine Form beginnend am linken Rand, zeichnen eine Linie zwischen den einzelnen Markierungen und beenden die Form am Startpunkt. Zeichnen Sie diese Form dann mit Farbe mit einem Farbverlaufs-Shader.
Schritt 3. Zeichnen Sie ein Raster

Wir setzen die Farbe so, dass sie mit einer gepunkteten Linie gezeichnet wird. Dann verwenden wir den speziellen Kotlin-Sprachzyklus, mit dem wir die Markierungen in Schritten von 7 (der Anzahl der Tage in einer Woche) durchlaufen können. Für jeden Marker nehmen wir die X-Koordinate und zeichnen eine vertikale gepunktete Linie vom oberen zeroY
des Diagramms bis zeroY
.
Schritt 4. Zeichnen Sie ein Diagramm und Markierungen

private fun drawLineAndMarkers(canvas: Canvas) { var previousMarker: Marker? = null for (marker in markers) { if (previousMarker != null) {
Wir gehen durch die Markierungen, zeichnen für jede einen gefüllten Kreis und eine einfache Linie von der vorherigen Markierung zur aktuellen.
Schritt 5. Zeichnen Sie die Wochenschaltflächen

private fun drawWeeks(canvas: Canvas) { for ((i, week) in weeks.withIndex()) { textPaint.getTextBounds(week, 0, week.length, rect) val x = middle(i) val y = zeroY + rect.height() val halfWidth = rect.width() / 2f val halfHeight = rect.height() / 2f val left = x - halfWidth - padding val top = y - halfHeight - padding val right = x + halfWidth + padding val bottom = y + halfHeight + padding rect.set(left, top, right, bottom) paint.color = bgColor paint.style = FILL canvas.drawRoundRect(rect, radius, radius, paint) paint.color = strokeColor paint.style = STROKE canvas.drawRoundRect(rect, radius, radius, paint) canvas.drawText(week, x, keyY, textPaint) } }
Wir gehen die Wochenmarkierungen durch, finden die X-Koordinate der Wochenmitte und zeichnen die Schaltfläche in Ebenen: Zuerst zeichnen wir einen Hintergrund mit abgerundeten Ecken, dann einen Rand und schließlich Text. Wir passen die Farbe an, bevor wir jede Ebene zeichnen.
Schritt 6. Zeichnen Sie die numerischen Markierungen rechts

private fun drawGraduations(canvas: Canvas) { val x = markers.last().currentPos.x + padding for (value in graduations) { val y = zeroY - scale * pxPerUnit val formatted = NumberFormat.getIntegerInstance().format(value) canvas.drawText(formatted, x, y, textPaint) } }
Die X-Koordinate ist die Position des letzten Markers plus einer Einrückung. Die Y-Koordinate wird anhand des Pixelverhältnisses pro Einheit berechnet. Wir formatieren die Zahl in eine Zeichenfolge (fügen Sie bei Bedarf ein Tausendertrennzeichen hinzu) und zeichnen den Text.
Das ist alles, jetzt onDraw()
unser onDraw()
so aus:
override fun onDraw(canvas: Canvas) { drawGradient(canvas) drawGuidelines(canvas) drawLineAndMarkers(canvas) drawWeeks(canvas) drawGraduations(canvas) }
Durch das Kombinieren der Ebenen erhalten wir das gewünschte Ergebnis:

Zusammenfassung
- Haben Sie keine Angst, eigene
View
Komponenten zu erstellen (falls erforderlich). - Lernen Sie die grundlegenden
Canvas
und Paint
APIs kennen. - Teilen Sie Ihr Design in kleine Ebenen auf und zeichnen Sie jede einzeln.
Was den letzten Punkt betrifft, so ist dies für mich eine der besten Programmierlektionen im Allgemeinen: Wenn Sie mit einer großen und komplexen Aufgabe konfrontiert werden, teilen Sie sie in kleinere, einfachere Aufgaben auf.