Пользовательские представления являются важной частью разработки Android, поскольку они позволяют разработчикам создавать уникальные и персонализированные элементы пользовательского интерфейса, которые можно повторно использовать в своих приложениях. В этом ответе мы рассмотрим, как рисовать пользовательские представления в Android с помощью Kotlin, а также приведем несколько примеров и пояснений.

  1. Создание пользовательского класса представления Чтобы создать пользовательское представление в Android, нам сначала нужно создать новый класс, расширяющий класс View. Этот новый класс будет содержать весь пользовательский код рисования, который мы хотим реализовать. Вот пример:
class MyCustomView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        // Drawing code goes here
    }
}

2. Реализация пользовательского рисования Когда у нас есть собственный класс представления, мы можем реализовать код пользовательского рисования в методе onDraw(). Этот метод вызывается всякий раз, когда необходимо перерисовать представление, например, при его первом создании, изменении его размера или при его аннулировании с помощью вызова invalidate().

Вот пример того, как нарисовать простой прямоугольник в пользовательском представлении:

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    canvas?.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
}

В этом примере мы используем объект canvas для рисования прямоугольника, покрывающего весь вид. Объект paint определяет цвет и стиль прямоугольника.

3. Настройка с помощью атрибутов Пользовательские представления также можно настроить с помощью атрибутов, которые позволяют разработчикам задавать такие свойства, как цвет, размер и стиль в файлах макета XML. Чтобы включить эту функцию, нам нужно объявить наши настраиваемые атрибуты в файле attrs.xml.

Вот пример того, как объявить пользовательский атрибут для пользовательского представления:

<declare-styleable name="MyCustomView">
    <attr name="customColor" format="color" />
</declare-styleable>

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

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

class MyCustomView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
    private val paint = Paint()

    init {
        context?.theme?.obtainStyledAttributes(attrs, R.styleable.MyCustomView, 0, 0)?.apply {
            try {
                paint.color = getColor(R.styleable.MyCustomView_customColor, Color.BLACK)
            } finally {
                recycle()
            }
        }
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
    }
}

В этом примере мы используем метод getStyledAttributes() для извлечения атрибута customColor из XML-файла макета. Затем мы используем этот цвет, чтобы установить цвет краски для нашего пользовательского представления.

4. Использование настраиваемых представлений в макетах

После того, как мы создали наш пользовательский класс представления и реализовали наш пользовательский код рисования, мы можем использовать наше пользовательское представление в файлах макета XML, как и любое другое представление.

Вот пример того, как использовать настраиваемое представление в файле макета XML:

<com.example.MyCustomView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:customColor="#FF0000" />

Несколько примеров создания пользовательских представлений в Kotlin:

  1. Круглая полоса прогресса:
class CircularProgressView(context: Context, attrs: AttributeSet) : View(context, attrs) {

    private var progress = 0f
    private var progressColor = Color.BLUE
    private var bgColor = Color.GRAY
    private var strokeWidth = 10f

    private var rectF = RectF()

    init {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircularProgressView)
        progressColor = typedArray.getColor(R.styleable.CircularProgressView_progressColor, Color.BLUE)
        bgColor = typedArray.getColor(R.styleable.CircularProgressView_backgroundColor, Color.GRAY)
        strokeWidth = typedArray.getDimension(R.styleable.CircularProgressView_strokeWidth, 10f)
        typedArray.recycle()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        val centerX = width / 2f
        val centerY = height / 2f
        val radius = (width - strokeWidth) / 2f

        // draw background circle
        val bgPaint = Paint()
        bgPaint.color = bgColor
        bgPaint.isAntiAlias = true
        bgPaint.style = Paint.Style.STROKE
        bgPaint.strokeWidth = strokeWidth
        canvas.drawCircle(centerX, centerY, radius, bgPaint)

        // draw progress arc
        val progressPaint = Paint()
        progressPaint.color = progressColor
        progressPaint.isAntiAlias = true
        progressPaint.style = Paint.Style.STROKE
        progressPaint.strokeWidth = strokeWidth
        rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius)
        canvas.drawArc(rectF, -90f, progress, false, progressPaint)
    }

    fun setProgress(progress: Float) {
        this.progress = progress
        invalidate()
    }

    fun setProgressColor(color: Int) {
        this.progressColor = color
        invalidate()
    }

    fun setBackgroundColor(color: Int) {
        this.bgColor = color
        invalidate()
    }

    fun setStrokeWidth(width: Float) {
        this.strokeWidth = width
        invalidate()
    }
}

В этом примере класс CircularProgressView расширяет класс View и реализует пользовательское представление, которое рисует круговой индикатор выполнения. Представление имеет четыре настраиваемых атрибута progressColor, backgroundColor, strokeWidth и progress, которые можно задать в файле макета XML.

Метод onDraw вызывается всякий раз, когда необходимо перерисовать представление. Он использует два объекта Paint для рисования фонового круга и дуги прогресса на холсте.

Методы setProgress, setProgressColor, setBackgroundColor и setStrokeWidth используются для обновления свойств представления и аннулирования представления для запуска перерисовки.

Чтобы использовать это пользовательское представление в XML-файле макета, вы можете добавить следующий код:

<com.example.myapplication.CircularProgressView
    android:id="@+id/progressView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:progressColor="@color/progress_color"
    app:backgroundColor="@color/bg_color"
    app:strokeWidth="10dp" />

Затем в коде Kotlin вы можете получить доступ к индикатору выполнения и изменить его, используя метод findViewById и вызывая методы пользовательского представления:

val progressView = findViewById<CircularProgressView>(R.id.progressView)
progressView.setProgress(50f)
progressView.setProgressColor(Color.RED)

2. Анимированный фон:

class AnimatedBackgroundView(context: Context, attrs: AttributeSet) : View(context, attrs) {

    private var radius: Float = 0f
    private var colors: IntArray = intArrayOf()
    private var handler = Handler(Looper.getMainLooper())

    init {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.AnimatedBackgroundView)
        radius = typedArray.getFloat(R.styleable.AnimatedBackgroundView_radius, 50f)
        colors = typedArray.getIntArray(R.styleable.AnimatedBackgroundView_colors)
        typedArray.recycle()
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        if (colors.isNotEmpty()) {
            val paint = Paint()
            paint.isAntiAlias = true
            for (i in colors.indices) {
                paint.color = colors[i]
                val centerX = (i * radius * 2) + radius
                val centerY = height.toFloat() / 2
                canvas?.drawCircle(centerX, centerY, radius, paint)
            }
        }
        invalidate()
    }

    fun startAnimating() {
        handler.postDelayed(object : Runnable {
            override fun run() {
                for (i in colors.indices) {
                    colors[i] = getNextColor(colors[i])
                }
                invalidate()
                handler.postDelayed(this, 1000)
            }
        }, 1000)
    }

    private fun getNextColor(color: Int): Int {
        val hsv = FloatArray(3)
        Color.colorToHSV(color, hsv)
        hsv[0] = (hsv[0] + 5) % 360
        return Color.HSVToColor(hsv)
    }
}

В этом примере класс AnimatedBackgroundView расширяет класс View и реализует пользовательское представление, которое рисует несколько кругов с изменяющимся цветом фона. Представление имеет два настраиваемых атрибута radius и colors, которые можно установить в файле макета XML.

Метод onDraw вызывается всякий раз, когда необходимо перерисовать представление. Он использует объект Paint для рисования на холсте кругов заданного радиуса и цвета.

Метод startAnimating использует объект Handler для публикации Runnable, который обновляет цвета кругов каждую секунду и делает представление недействительным для запуска перерисовки.

Наконец, метод getNextColor принимает входной цвет и возвращает следующий цвет в цветовом спектре, сдвигая значение оттенка на 5 градусов.

Каждое из этих пользовательских представлений можно реализовать в Kotlin с помощью классов Android Canvas и Paint, а также различных методов компоновки и обработки событий. Создавая настраиваемые представления, разработчики Android могут добавлять в свои приложения уникальные и привлекательные визуальные компоненты, улучшая взаимодействие с пользователем и функциональность.