Работал ли с кастомными view

Ответ

Да, работал. Создавал кастомные View, переопределяя методы onDraw(), onMeasure(), onLayout(). Например, для анимированного прогресс-бара:

class CustomProgressBar @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : View(context, attrs) {

    private var progress = 0f
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        // Рисуем прогресс
        canvas.drawRect(0f, 0f, width * progress, height.toFloat(), paint)
    }

    fun setProgress(progress: Float) {
        this.progress = progress.coerceIn(0f, 1f)
        invalidate()
    }
}

Основные моменты:

  1. Обработка атрибутов из XML через obtainStyledAttributes
  2. Корректная работа с onMeasure для wrap_content
  3. Оптимизация перерисовки через clipRect или save/restore
  4. Поддержка аппаратного ускорения

Ответ 18+ 🔞

А, ну это классика, ебать мои старые костыли! Да, работал, конечно. Это ж как в старые добрые времена — самому всё с нуля городить, когда стандартные виджеты не катят. Создавал кастомные View, переопределяя эти самые священные методы: onDraw(), onMeasure(), onLayout(). Чувствуешь себя богом интерфейса, пока не начнётся эта ёперная борьба с wrap_content.

Вот, смотри, элементарный пример — анимированный прогресс-бар. Проще пареной репы, но тут вся суть:

class CustomProgressBar @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : View(context, attrs) {

    private var progress = 0f
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        // Рисуем прогресс
        canvas.drawRect(0f, 0f, width * progress, height.toFloat(), paint)
    }

    fun setProgress(progress: Float) {
        this.progress = progress.coerceIn(0f, 1f)
        invalidate()
    }
}

С виду — хуй с горы, ан нет. А теперь лови основные грабли, на которых все пиздятся, пока не набьют шишку.

  1. Атрибуты из XML. Это же святое! Без obtainStyledAttributes твой кастомный виджет — просто мартышлюшка, которую нельзя нормально настроить из разметки. Надо вытягивать цвета, толщины, флаги, а то потом будешь руками в коде всё пихать — терпения ноль ебать.

  2. Этот ёбаный onMeasure для wrap_content. Вот тут-то и начинается цирк. Если не обработать — твоя вьюха сожрёт весь экран, будто ей неймётся. Надо правильно посчитать свои хотелки, вызвать setMeasuredDimension(), а иначе будет пиздопроебибна с версткой.

  3. Оптимизация перерисовки. Рисуешь всё подряд в onDraw()? Ну и зря. Если виджет сложный, надо использовать clipRect, чтобы лишние области не трогать, или save()/restore() для трансформаций канваса. Иначе на слабых устройствах будет не анимация, а слайд-шоу из адских мук. Сам от себя охуеешь, глядя на просадки фпс.

  4. Аппаратное ускорение. Его поддержка — это не просто галочка. Иногда некоторые операции рисования (особенно всякие тени или сложные пути) могут вести себя как хитрая жопа — то работают, то нет. Надо знать, что можно, а что нельзя при включённом слое.

Короче, дело это благородное, но чтобы не вышло манда с ушами, надо всё делать с умом. Зато когда работает как часы — чувствуешь себя королём.