Цикл жизни View
onAttachedToWindow(): Этот метод вызывается, когда View прикрепляется к окну. Это происходит после того, как View и все ее дочерние элементы были измерены и размещены на экране. В этот момент можно выполнять инициализацию, которая требует доступа к размерам и положению View.
onDetachedFromWindow(): Вызывается, когда View открепляется от окна. Это может произойти, когда View удаляется из иерархии представлений или когда ее активность становится невидимой. Здесь вы можете выполнить очистку ресурсов или отмену анимаций, связанных с этим View.
onMeasure(): Этот метод вызывается, когда система определяет размеры View. В этом методе View должна указать желаемые размеры с помощью метода setMeasuredDimension()
. Обычно это переопределяется, чтобы правильно измерить размеры View в соответствии с ее содержимым и параметрами разметки.
onLayout(): Вызывается, когда View должна определить свое положение и размер на экране. В этом методе вы должны установить положение и размеры своих дочерних элементов с помощью методов layout()
или layoutParams
.
onDraw(): Этот метод вызывается при отрисовке View на экране. В этом методе вы можете рисовать содержимое View с помощью Canvas и Paint.
Метод вызывается когда View приаттачена к Window. В этой фазе вьюшка знает что она может быть активна, следовательно может начаться выделение ресурсов и настройка Listener'ов.
onMeasure
позволяет установить размеры виджета в соответствии с его содержимым и параметрами макета. Это включает вычисление ширины и высоты, которые должны занимать виджет на экране.ViewGroup
), onMeasure
используется для измерения размеров этих дочерних элементов и их расположения в пределах родительского виджета.onMeasure
учитывает ограничения, наложенные на виджет через его параметры макета (например, wrap_content
, match_parent
или конкретные размеры). Он должен обеспечить, чтобы размеры виджета соответствовали этим ограничениям.onMeasure
также учитывает текущий размер экрана и другие факторы, которые могут влиять на размеры и позиционирование виджета, чтобы обеспечить корректное отображение на различных устройствах и в различных условиях.onMeasure
, система Android использует полученные измерения для определения точного размера и расположения виджета перед его фактической отрисовкой (в методе onLayout
и последующей onDraw
).onMeasure()
не возвращает никаких значение, необходимо вызвать метод setMeasuredDimension()
для установки размеров.
Вызывается после измерения Views для расположения их на экране
Размеры и расположения вычислены на на предыдущих шагах, так что View может быть отрисована на их основе. В методе onDraw(canvas: Canvas)
созданный объект Canvas отправляет команды OpenGL-ES в графический процессор.
Важно: нельзя создавать какие-либо объекты в этом методе, потому что метод onDraw
может быть вызван несколько раз.
invalidate
Метод который настаивает на принудительной перерисовке View, изменения в котором мы хотим показать.
Когда вызывается метод invalidate()
, Android Framework помечает область, занимаемую этим представлением, как недействительную (требующую перерисовки). Это приводит к вызову метода onDraw()
этого представления при следующей перерисовке экрана. Таким образом, представление будет перерисовано, отображая любые внесенные изменения в его внешний вид.
Например, если вы изменяете цвет, текст или размер представления, вызовите invalidate()
, чтобы обновить его отображение с учетом внесенных изменений.
requestLayout
Нужно вызвать когда требует повторное вычисление фаз измерения и компоновки View (measure → layout → draw). Простыми словами, requestLayout()
следует вызывать, когда происходит изменение границ представления.
Когда вызывается метод requestLayout()
, система представлений помечает представление как недействительное в отношении его размеров и макета. Это приводит к повторному запуску цикла измерения и компоновки представлений для этого представления и его родителей.
Например, если размеры представления изменяются (например, ширина или высота), вызов requestLayout()
необходим, чтобы уведомить систему представлений о необходимости пересчета макета и размеров представления.
Инкапсулирует требования макета, передаваемые от родительского к дочернему представлению. Каждая MeasureSpec представляет требование для ширины или высоты и состоит из размера и режима.
Существует три возможных режима:
MeasureSpec.EXACTLY
(Точно): Родительское представление устанавливает точный размер для дочернего. Дочернему представлению будут предоставлены эти границы независимо от его собственных размеров. Экземпляры, указывающие фиксированную ширину для представления, весы в LinearLayout или атрибут match_parent и т. д., используют этот режим.
MeasureSpec.AT_MOST
(Не более): Дочернее представление может быть таким большим, как ему нужно, но не больше указанного размера.
MeasureSpec.UNSPECIFIED
(Неопределенный): Родитель не накладывает никаких ограничений на размер дочернего представления. Оно может быть любого размера, какое захочет.
Эти режимы помогают определить, каким образом дочернее представление будет измерено и насколько свободно оно будет в изменении размеров в соответствии с макетом, заданным его родителем.
import android.view.View
import android.view.View.MeasureSpec
fun main() {
val parentWidth = 200 // Ширина родительского представления
val parentHeight = 300 // Высота родительского представления
val childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.EXACTLY)
val childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.AT_MOST)
// Создаем дочернее представление
val childView = View(null)
// Измеряем дочернее представление с использованием MeasureSpec
childView.measure(childWidthMeasureSpec, childHeightMeasureSpec)
// Получаем измеренные размеры дочернего представления
val measuredWidth = childView.measuredWidth
val measuredHeight = childView.measuredHeight
println("Measured Width: $measuredWidth")
println("Measured Height: $measuredHeight")
}
В этом примере создается дочернее представление, которое измеряется с помощью MeasureSpec
. Ширина родительского представления установлена как точное значение (EXACTLY), а высота - как не более указанного размера (AT_MOST). Результаты измерений сохраняются в переменных measuredWidth
и measuredHeight
.
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
class CustomView : View {
// Переменные для хранения значений пользовательских атрибутов
private var customColor: Int = Color.BLACK
private var customText: String = "Hello, World!"
// Переменные для рисования текста
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
constructor(context: Context) : super(context) {
init(null)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) {
init(attrs)
}
// Метод для инициализации атрибутов
private fun init(attrs: AttributeSet?) {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView)
// Получаем значения пользовательских атрибутов
customColor = typedArray.getColor(R.styleable.CustomView_customColor, Color.BLACK)
customText = typedArray.getString(R.styleable.CustomView_customText) ?: "Hello, World!"
// Освобождаем ресурсы
typedArray.recycle()
// Устанавливаем параметры рисования текста
paint.color = customColor
paint.textSize = 50f
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Рисуем текст в центре представления
val x = width / 2f - paint.measureText(customText) / 2f
val y = height / 2f + paint.textSize / 2f
canvas.drawText(customText, x, y, paint)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
// Рассчитываем размеры представления
val desiredWidth = suggestedMinimumWidth + paddingLeft + paddingRight
val desiredHeight = suggestedMinimumHeight + paddingTop + paddingBottom
val width = resolveSize(desiredWidth, widthMeasureSpec)
val height = resolveSize(desiredHeight, heightMeasureSpec)
setMeasuredDimension(width, height)
}
}