Пользовательские представления
Создавайте пользовательские компоненты интерфейса с нуля. Расширяйте View, реализуйте onMeasure и onDraw с помощью Canvas и Paint, определяйте пользовательские XML-атрибуты и создавайте составные представления.
«Пользовательские представления» — бесплатный урок Android Academy на CoddyKit. Это урок 3 из 5. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 5 уроков всего.
Зачем нужны пользовательские представления
Иногда встроенные представления Android не позволяют в точности получить нужный результат. Пользовательские представления позволяют:
- Рисовать фигуры, графики, индикаторы прогресса или диаграммы, которых нет в SDK
- Создавать фирменные компоненты интерфейса, единообразно используемые во всём приложении
- Объединять несколько представлений в один повторно используемый компонент (составное представление)
- Повышать производительность за счёт устранения вложенных иерархий макетов
Расширение View
Создайте пользовательское представление, расширив View (или подкласс, например ImageView). Переопределите необходимый конструктор:
class CircleProgressView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
var progress: Float = 0f
set(value) {
field = value.coerceIn(0f, 100f)
invalidate() // trigger redraw
}
// onMeasure and onDraw go here
}onMeasure()
onMeasure() сообщает родительскому элементу, какой размер требуется этому представлению. Всегда вызывайте setMeasuredDimension() в конце:
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val desiredSize = 200 // dp → pixels
val px = (desiredSize * resources.displayMetrics.density).toInt()
val width = resolveSize(px, widthMeasureSpec)
val height = resolveSize(px, heightMeasureSpec)
// Square: same width and height
val size = minOf(width, height)
setMeasuredDimension(size, size)
}Paint — инструмент рисования
Объект Paint хранит параметры стиля рисования (цвет, ширину обводки, сглаживание). Создайте его один раз — никогда не создавайте внутри onDraw():
class CircleProgressView ... : View(...) {
// Create Paint in init, not in onDraw!
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.LTGRAY
style = Paint.Style.STROKE
strokeWidth = 20f
strokeCap = Paint.Cap.ROUND
}
private val progressPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.parseColor("#6750A4")
style = Paint.Style.STROKE
strokeWidth = 20f
strokeCap = Paint.Cap.ROUND
}
}onDraw() — рисование представления
onDraw(canvas) вызывается каждый раз, когда представление нужно перерисовать. Используйте Canvas для рисования фигур, текста и растровых изображений:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = (minOf(width, height) / 2f) - 30f
// Draw background circle
canvas.drawCircle(cx, cy, radius, backgroundPaint)
// Draw progress arc
val oval = RectF(cx - radius, cy - radius, cx + radius, cy + radius)
val sweepAngle = 360f * (progress / 100f)
canvas.drawArc(oval, -90f, sweepAngle, false, progressPaint)
}invalidate() и requestLayout()
Два метода для запуска обновления:
invalidate()— перерисовывает представление (вызываетonDraw). Используйте, когда содержимое изменилось, но размер остался прежним.requestLayout()— заново измеряет и перерисовывает представление (вызывает сначалаonMeasure, затемonDraw). Используйте, когда размер представления изменился.
Пользовательские атрибуты (attrs.xml)
Объявите пользовательские XML-атрибуты, чтобы настраивать представление из файлов макетов:
<!-- res/values/attrs.xml -->
<resources>
<declare-styleable name="CircleProgressView">
<attr name="progressColor" format="color" />
<attr name="trackColor" format="color" />
<attr name="strokeWidth" format="dimension" />
<attr name="progress" format="float" />
</declare-styleable>
</resources>Чтение пользовательских атрибутов
Считывайте attrs в конструкторе с помощью obtainStyledAttributes:
init {
context.obtainStyledAttributes(attrs, R.styleable.CircleProgressView).use { ta ->
progressPaint.color = ta.getColor(
R.styleable.CircleProgressView_progressColor,
Color.parseColor("#6750A4")
)
backgroundPaint.color = ta.getColor(
R.styleable.CircleProgressView_trackColor,
Color.LTGRAY
)
val sw = ta.getDimension(R.styleable.CircleProgressView_strokeWidth, 20f)
progressPaint.strokeWidth = sw
backgroundPaint.strokeWidth = sw
progress = ta.getFloat(R.styleable.CircleProgressView_progress, 0f)
}
}Использование пользовательского представления в XML
Используйте пользовательское представление в XML-макете, указав полный путь к классу и пользовательские атрибуты:
<!-- In any layout XML -->
<com.example.myapp.CircleProgressView
android:id="@+id/progressView"
android:layout_width="120dp"
android:layout_height="120dp"
app:progress="65"
app:progressColor="@color/primary"
app:trackColor="@color/surface_variant"
app:strokeWidth="12dp" />
<!-- Control in code: -->
binding.progressView.progress = 75fСоставные представления
Составное представление объединяет существующие представления в один повторно используемый компонент. Расширьте класс макета, например LinearLayout:
class SearchBarView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {
private val binding = ViewSearchBarBinding.inflate(LayoutInflater.from(context), this)
init {
orientation = HORIZONTAL
binding.ivSearch.setOnClickListener {
onSearchClick?.invoke(binding.etSearch.text.toString())
}
}
var onSearchClick: ((String) -> Unit)? = null
fun setHint(hint: String) { binding.etSearch.hint = hint }
}Рисование текста
Рисуйте текст на Canvas с помощью drawText(). Точно выравнивайте его по центру с помощью Paint.getFontMetrics():
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.BLACK
textSize = 48f
textAlign = Paint.Align.CENTER
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
// Vertically center the text:
val fm = textPaint.fontMetrics
val textY = cy - (fm.ascent + fm.descent) / 2f
canvas.drawText("${progress.toInt()}%", cx, textY, textPaint)
}Быстрая проверка
Какой метод следует вызвать, чтобы перерисовать пользовательское представление, если данные изменились, но размер остался прежним?
Повторение: пользовательские представления
Теперь вы умеете рисовать на экране что угодно:
- Расширяйте
Viewс помощью@JvmOverloads constructor onMeasure()— сообщайте требуемый размер с помощьюsetMeasuredDimension()Paint— задавайте стиль; создавайте один раз, а не внутриonDraw()onDraw(canvas)— рисуйте круги, дуги, текст и растровые изображения- Используйте
invalidate()для перерисовки, аrequestLayout()— для повторного измерения - Пользовательские атрибуты через
attrs.xmlдля настройки из XML - Составное представление — расширяйте макет, чтобы объединять существующие представления
Далее: оживите интерфейс с помощью анимаций и переходов.
Часто задаваемые вопросы
Урок «Пользовательские представления» бесплатный?
Да — полный текст урока «Пользовательские представления» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 5 уроков всего.
Чему я научусь в уроке «Пользовательские представления»?
Создавайте пользовательские компоненты интерфейса с нуля. Расширяйте View, реализуйте onMeasure и onDraw с помощью Canvas и Paint, определяйте пользовательские XML-атрибуты и создавайте составные пре… Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Android Academy?
Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 5.
Сколько времени занимает урок «Пользовательские представления»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Android Academy?
Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы Material Design
- Темы и стили
- Пользовательские представления
- Анимации и переходы
- Нижняя навигация и вкладки