Un exemple pratique de création de votre propre composant View

Un exemple pratique de création de votre propre composant View


J'aime Dribbble . Il existe de nombreux projets de design sympas et inspirants. Mais si vous êtes développeur, le sentiment de beauté est souvent remplacé par le désespoir lorsque vous commencez à réfléchir à la façon de mettre en œuvre cette conception cool.


Dans cet article, je vais vous montrer un exemple d'une telle conception et de sa mise en œuvre, mais avant cela, parlons de la résolution du problème dans son ensemble.


La façon la plus simple est d'utiliser une sorte de bibliothèque qui couvre nos besoins. Maintenant, ne vous méprenez pas, je suis un grand partisan de l'approche «ne pas réinventer la roue». Il existe d'excellentes bibliothèques open source, et lorsque j'ai besoin de télécharger des images ou de mettre en œuvre l'API REST, Glide / Picasso et Retrofit m'aideront beaucoup.


Mais lorsque vous devez mettre en œuvre une conception inhabituelle, ce n'est pas toujours le meilleur choix. Vous devrez passer du temps à chercher une bonne bibliothèque prise en charge qui fera quelque chose de similaire. Ensuite, vous devez examiner le code pour vous assurer que quelque chose d'adéquat y est écrit. Vous devrez consacrer plus de temps à la compréhension des paramètres et des configurations que vous pouvez gérer pour utiliser la bibliothèque dans vos tâches. Et soyons honnêtes, très probablement, la bibliothèque ne couvrira pas vos besoins à 100%, et vous devrez faire des compromis avec les concepteurs.


Par conséquent, je dis qu'il est souvent plus facile et mieux de créer votre propre composant View . Quand je dis «composant View natif», je veux dire l'extension de la classe View , onDraw() méthode onDraw() et utilisant Paint et Canvas pour dessiner le composant View . Cela peut sembler effrayant si vous ne l'avez pas encore fait, car ces classes ont de nombreuses méthodes et propriétés, mais vous pouvez vous concentrer sur les principales:


  • canvas.drawRect() - spécifiez les coordonnées des coins et dessinez un rectangle;


  • canvas.drawRoundRect() - spécifiez éventuellement le rayon et les coins du rectangle seront arrondis;


  • canvas.drawPath() est un moyen plus complexe mais aussi plus puissant de créer votre propre forme en utilisant des lignes et des courbes;


  • canvas.drawText() - pour dessiner du texte sur le canevas (en utilisant Paint vous pouvez contrôler la taille, la couleur et d'autres propriétés);


  • canvas.drawCircle() - spécifiez le point central et le rayon et obtenez un cercle;


  • canvas.drawArc() - spécifiez le rectangle englobant, ainsi que les angles de départ et de rotation pour dessiner l'arc;


  • paint.style - indique si la forme dessinée sera remplie, encerclée ou les deux;


  • paint.color - indique la couleur (y compris la transparence);


  • paint.strokeWidth - contrôle la largeur des formes de trait;


  • paint.pathEffect - vous permet d'influencer la géométrie de la figure dessinée;


  • paint.shader - vous permet de dessiner des dégradés.



N'oubliez pas que parfois vous devrez peut-être utiliser d'autres API, mais même après avoir maîtrisé ces méthodes, vous pouvez dessiner des formes très complexes.


Exemple pratique


Voici un design que Pepper nous propose:


La conception


Il y a beaucoup de choses intéressantes ici, mais prenons tout cela en petits morceaux.


Étape 1. Calculer les positions des marqueurs


 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 } } 

Nous trouvons les valeurs minimales et maximales, calculons le rapport de pixels par unité, la taille de pas horizontale entre les marqueurs et les positions X et Y.


Étape 2. Dessinez un dégradé


Dégradé


 // prepare the gradient paint val colors = intArrayOf(colorStart, colorEnd)) val gradient = LinearGradient( 0f, paddingTop, 0f, zeroY, colors, null, CLAMP ) gradientPaint.style = FILL gradientPaint.shader = gradient private fun drawGradient(canvas: Canvas) { path.reset() path.moveTo(paddingLeft, zeroY) for (marker in markers) { path.lineTo(marker.targetPos.x, entry.targetPos.y) } // close the path path.lineTo(markers.last().targetPos.x, zeroY) path.lineTo(paddingLeft, zeroY) canvas.drawPath(path, gradientPaint) } 

Nous créons une forme à partir du bord gauche, en traçant une ligne entre chaque marqueur et en terminant la forme au point de départ. Dessinez ensuite cette forme en utilisant de la peinture avec un dégradé de dégradé.


Étape 3. Tracez une grille


Mesh


 // prepare the guideline paint dottedPaint.style = STROKE dottedPaint.strokeWidth = DOTTED_STROKE_WIDTH_DP dottedPaint.pathEffect = DashPathEffect(floatArrayOf(INTERVAL, INTERVAL), 0f) private fun drawGuidelines(canvas: Canvas) { val first = findFirstDayOfWeekInMonth(markers) for (i in first..markers.lastIndex step 7) { val marker = markers[i] guidelinePath.reset() guidelinePath.moveTo(entry.currentPos.x, paddingTop) guidelinePath.lineTo(entry.currentPos.x, zeroY) canvas.drawPath(guidelinePath, dottedPaint) } } 

Nous définissons la peinture de sorte qu'elle dessine avec une ligne pointillée. Ensuite, nous utilisons le cycle de langue spécial de Kotlin, qui nous permet de parcourir les marqueurs par étapes de 7 (le nombre de jours dans une semaine). Pour chaque marqueur, nous prenons la coordonnée X et dessinons une ligne pointillée verticale du haut du graphique à zeroY .


Étape 4. Dessinez un graphique et des marqueurs


Graphique et marqueurs


 private fun drawLineAndMarkers(canvas: Canvas) { var previousMarker: Marker? = null for (marker in markers) { if (previousMarker != null) { // draw the line val p1 = previousMarker.currentPos val p2 = marker.currentPos canvas.drawLine(p1.x, p1.y, p2.x, p2.y, strokePaint) } previousMarker = marker // draw the marker canvas.drawCircle( marker.currentPos.x, marker.currentPos.y, pointRadius, pointPaint ) } } 

Nous parcourons les marqueurs, dessinons un cercle rempli pour chacun d'eux et une ligne simple du marqueur précédent au marqueur actuel.


Étape 5. Dessinez les boutons des semaines


Boutons Semaines


 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) } } 

Nous passons les marques de la semaine, trouvons la coordonnée X du milieu de la semaine et commençons à dessiner le bouton en couches: nous dessinons d'abord un arrière-plan avec des coins arrondis, puis une bordure et, enfin, du texte. Nous ajustons la peinture avant de dessiner chaque couche.


Étape 6. Dessinez les marqueurs numériques sur la droite


Marqueurs numériques


 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) } } 

La coordonnée X est la position du dernier marqueur plus une indentation. La coordonnée Y est calculée en utilisant le rapport de pixels par unité. Nous mettons en forme le nombre dans une chaîne (si nécessaire, ajoutez un séparateur de milliers) et dessinons le texte.


C'est tout, maintenant notre onDraw() ressemblera à ceci:


 override fun onDraw(canvas: Canvas) { drawGradient(canvas) drawGuidelines(canvas) drawLineAndMarkers(canvas) drawWeeks(canvas) drawGraduations(canvas) } 

Et la combinaison des couches nous donnera le résultat souhaité:


Résultat


Résumé


  1. N'ayez pas peur de créer vos propres composants View (si nécessaire).
  2. Apprenez les API de base Canvas et Paint .
  3. Divisez votre conception en petites couches et dessinez chacune indépendamment.

Quant au dernier point, pour moi, c'est l'une des meilleures leçons de programmation en général: face à une tâche grande et complexe, divisez-la en tâches plus petites et plus simples.

Source: https://habr.com/ru/post/fr433782/


All Articles