Un ejemplo práctico de crear su propio componente View

Un ejemplo práctico de crear su propio componente View


Me gusta Dribbble Hay muchos proyectos de diseño interesantes e inspiradores. Pero si usted es un desarrollador, a menudo la sensación de belleza rápidamente da paso a la desesperación cuando comienza a pensar en cómo implementar este diseño genial.


En este artículo, le mostraré un ejemplo de dicho diseño y su implementación, pero antes de eso, hablemos sobre la solución del problema en su conjunto.


La forma más fácil es usar algún tipo de biblioteca que cubra nuestras necesidades. Ahora no me malinterpreten, soy un gran defensor del enfoque de "no reinventar la rueda". Hay excelentes bibliotecas de código abierto, y cuando necesito cargar imágenes o implementar la API REST, Glide / Picasso y Retrofit me ayudarán mucho.


Pero cuando necesita implementar un diseño inusual, esta no siempre es la mejor opción. Deberá pasar tiempo buscando una buena biblioteca compatible que haga algo similar. Luego, debe examinar el código para asegurarse de que se escriba algo adecuado allí. Deberá dedicar más tiempo a comprender la configuración y las configuraciones que puede administrar para usar la biblioteca en sus tareas. Y seamos honestos, lo más probable es que la biblioteca no cubra sus necesidades al 100%, y tendrá que hacer algunos compromisos con los diseñadores.


Por lo tanto, digo que a menudo es más fácil y mejor crear su propio componente View . Cuando digo "componente de View nativo", me refiero a la extensión de la clase de View , anulando el método onDraw() y usando Paint y Canvas para dibujar el componente de View . Esto puede parecer aterrador si no lo ha hecho antes, porque estas clases tienen muchos métodos y propiedades, pero puede centrarse en los principales:


  • canvas.drawRect() : especifica las coordenadas de las esquinas y dibuja un rectángulo;


  • canvas.drawRoundRect() : opcionalmente, especifique el radio y las esquinas del rectángulo se redondearán;


  • canvas.drawPath() es una forma más compleja, pero también más poderosa de crear su propia forma usando líneas y curvas;


  • canvas.drawText() : para dibujar texto en el lienzo (con Paint puede controlar el tamaño, el color y otras propiedades);


  • canvas.drawCircle() : especifique el punto central y el radio y obtenga un círculo;


  • canvas.drawArc() : especifica el rectángulo delimitador, así como los ángulos de inicio y giro para dibujar el arco;


  • paint.style : indica si la forma dibujada se rellenará, circulará o ambas;


  • paint.color : indica el color (incluida la transparencia);


  • paint.strokeWidth : controla el ancho de las formas de trazo;


  • paint.pathEffect : le permite influir en la geometría de la figura dibujada;


  • paint.shader : le permite dibujar degradados.



Recuerde, a veces puede que necesite usar otras API, pero incluso si domina estos métodos, puede dibujar formas muy complejas.


Ejemplo práctico


Aquí hay un diseño que Pepper nos ofrece:


Diseño


Aquí hay muchas cosas interesantes, pero tomemos todo en pedazos pequeños.


Paso 1. Calcular las posiciones de los marcadores


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

Encontramos los valores mínimos y máximos, calculamos la relación de píxeles por unidad, el tamaño del paso horizontal entre los marcadores y las posiciones X e Y.


Paso 2. Dibuja un gradiente


Gradiente


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

Creamos una forma comenzando desde el borde izquierdo, dibujando una línea entre cada marcador y terminando la forma en el punto inicial. Luego dibuja esta forma usando pintura con un sombreador degradado.


Paso 3. Dibuja una cuadrícula


Malla


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

Configuramos la pintura para que dibuje con una línea punteada. Luego usamos el ciclo de idioma especial de Kotlin, que nos permite iterar sobre los marcadores en pasos de 7 (la cantidad de días en una semana). Para cada marcador, tomamos la coordenada X y dibujamos una línea de puntos vertical desde la parte superior del gráfico hasta zeroY .


Paso 4. Dibuja un gráfico y marcadores


Gráfico y marcadores


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

Pasamos por los marcadores, dibujamos un círculo lleno para cada uno de ellos y una línea simple desde el marcador anterior al actual.


Paso 5. Dibuja los botones de las semanas


Botones de semanas


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

Repasamos las marcas de la semana, encontramos la coordenada X de la mitad de la semana y comenzamos a dibujar el botón en capas: primero dibujamos un fondo con esquinas redondeadas, luego un borde y, finalmente, texto. Ajustamos la pintura antes de dibujar cada capa.


Paso 6. Dibuja los marcadores numéricos a la derecha


Marcadores numéricos


 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 coordenada X es la posición del último marcador más alguna sangría. La coordenada Y se calcula utilizando la relación de píxeles por unidad. Formateamos el número en una cadena (si es necesario, agregamos un separador de miles) y dibujamos el texto.


Eso es todo, ahora nuestro onDraw() se verá así:


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

Y combinar las capas nos dará el resultado deseado:


Resultado


Resumen


  1. No tenga miedo de crear sus propios componentes de View (si es necesario).
  2. Aprenda las API básicas de Canvas y Paint .
  3. Divide tu diseño en pequeñas capas y dibuja cada una independientemente.

En cuanto al último punto, para mí esta es una de las mejores lecciones de programación en general: cuando se enfrente a una tarea grande y compleja, divídala en tareas más pequeñas y simples.

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


All Articles