» » Практический пример создания собственного View-компонента

 

Практический пример создания собственного View-компонента

Автор: admin от 27-12-2018, 17:45, посмотрело: 37

Практический пример создания собственного View-компонента



Мне нравится Dribbble. Там есть много крутых и вдохновляющих дизайн-проектов. Но если вы разработчик, то часто чувство прекрасного быстро сменяется на отчаяние, когда вы начинаете думать о том, как реализовать этот крутой дизайн.



В этой статье я покажу вам пример такого дизайна и его реализацию, но перед этим давайте поговорим о решении проблемы в целом.

Glide/Picasso и Retrofit очень здорово помогут мне.



Но когда вам нужно реализовать какой-то необычный дизайн, это не всегда лучший выбор. Вам нужно будет потратить время на поиск хорошей, поддерживаемой библиотеки, которая будет делать что-то подобное. Затем вам нужно заглянуть в код, чтобы убедиться, что там написано что-то адекватное. Вам нужно будет уделить больше времени пониманию настроек и конфигураций, которыми вы сможете управлять для использования библиотеки в ваших задачах. И давайте будем честными, скорее всего, библиотека не покроет ваших нужд на 100%, и вам нужно будет пойти на некоторые компромиссы с дизайнерами.



Поэтому я говорю о том, что зачастую проще и лучше создать свой собственный View-компонент. Когда я говорю «собственный View-компонент», я имею в виду расширение класса View, переопределение метода onDraw() и использование Paint и Canvas для рисования View-компонента. Это может показаться страшным, если вы не делали этого раньше, потому что у этих классов есть много методов и свойств, но вы можете сосредоточиться на основных:




  • canvas.drawRect() — укажите координаты углов и нарисуете прямоугольник;




  • canvas.drawRoundRect() — дополнительно укажите радиус, и углы прямоугольника будут закруглены;




  • canvas.drawPath() — это более сложный, но и более мощный способ создания собственной фигуры с помощью линий и кривых;




  • canvas.drawText() — для рисования текста на канвасе (с помощью Paint вы сможете контролировать размер, цвет и другие свойства);




  • canvas.drawCircle() — укажите центральную точку и радиус и получится круг;




  • canvas.drawArc() — укажите ограничивающий прямоугольник, а также начальный и поворотный углы для рисования дуги;




  • paint.style — указывает, будет ли нарисованная фигура заполнена, обведена или и то, и другое;




  • paint.color — указывает цвет (включая прозрачность);




  • paint.strokeWidth — управляет шириной для обводки фигур;




  • paint.pathEffect — позволяет влиять на геометрию рисуемой фигуры;




  • paint.shader — позволяет рисовать градиенты.






Помните, иногда вам может понадобиться использовать другие API, но даже овладев этими методами, вы сможете рисовать очень сложные фигуры.



Практический пример



Вот такой дизайн предлагает нам Pepper:



Практический пример создания собственного View-компонента



Здесь много чего интересного, но давайте разберём всё на мелкие кусочки.



Шаг 1. Рассчитать позиции маркеров



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


Мы находим минимальное и максимальное значения, вычисляем соотношение пикселей на единицу, размер шага по горизонтали между маркерами и позиции X и Y.



Шаг 2. Нарисовать градиент



Практический пример создания собственного View-компонента



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


Мы создаем фигуру, начиная с левого края, проводя линию между каждым маркером и завершая фигуру в начальной точке. Затем рисуем эту фигуру, используя краску с градиентным шейдером.



Шаг 3. Нарисовать сетку



Практический пример создания собственного View-компонента



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


Мы настраиваем краску, чтобы она рисовала пунктиром. Затем мы используем специальный цикл языка Kotlin, который позволяет нам перебирать маркеры с шагом 7 (количество дней в неделе). Для каждого маркера мы берём координату X и рисуем вертикальную пунктирную линию от вершины графика до zeroY.



Шаг 4. Нарисовать график и маркеры



Практический пример создания собственного View-компонента



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


Мы перебираем маркеры, рисуем для каждого из них закрашенный круг и простую линию от предыдущего маркера до текущего.



Шаг 5. Нарисовать кнопки недель



Практический пример создания собственного View-компонента



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


Мы перебираем метки недель, находим координату X середины недели и начинаем рисовать кнопку по слоям: сначала рисуем фон с закругленными углами, затем границу и, наконец, текст. Мы настраиваем краску перед рисованием каждого слоя.



Шаг 6. Нарисовать числовые маркеры справа



Практический пример создания собственного View-компонента



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


Координата X — это позиция последнего маркера плюс некоторый отступ. Координата Y рассчитывается с использованием соотношения пикселей на единицу. Мы форматируем число в строку (при необходимости добавляем разделитель тысяч) и рисуем текст.



Вот и всё, теперь наш onDraw() будет выглядеть так:



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


И объединение слоёв даст нам желаемый результат:



Практический пример создания собственного View-компонента



Итог




  • Не бойтесь создавать собственные View-компоненты (при необходимости).

  • Изучите основные API Canvas и Paint.

  • Разбивайте ваш дизайн на маленькие слои и рисуйте каждый независимо.



  • Что касается последнего пункта, для меня это один из лучших уроков программирования в целом: когда сталкиваетесь с большой и сложной задачей, разбейте её на более мелкие, более простые задачи.



    Источник: Хабр / Интересные публикации

    Категория: Программирование, Веб-разработка, Game Development, Android

    Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
    Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

    Добавление комментария

    Имя:*
    E-Mail:
    Комментарий:
    Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
    Введите два слова, показанных на изображении: *