Использование forceLayout (), requestLayout () и invalidate ()


185

Я немного запутался о роли forceLayout(), requestLayout()и invalidate()методов Viewкласса.

Когда они будут вызваны?

Ответы:


357

Чтобы лучше понять ответы Франсуа BOURLIEUX и Dalvik, я предлагаю вам взглянуть на эту удивительную диаграмму жизненного цикла представления от Arpit Mathur : введите описание изображения здесь


28
Я часто вижу, что requestLayout вызывается непосредственно после вызова invalidate, я даже вижу, что происходит в исходном коде Android для таких вещей, как TextView, но в соответствии с этой схемой это избыточно, верно? Так есть ли цель для этого?
Tcox

5
Ну, это интересный вопрос, и, честно говоря, я действительно не знаю, почему они вызывают оба метода, например TextView. Я подумал , что , может быть , они хотят , чтобы нарисовать Viewв последний раз , прежде чем они изменить его расположение связанных параметров, но это на самом деле не имеет никакого смысла , если мы думаем о тех вызовах, вызываемых в другом порядке (и они называют invalidate()сразу после requestLayout()в TextViewтакже). Может быть, это заслуживает другого вопроса о StackOverflow :)?
Бартек Липински

14
(1/2) : Я не думаю, что вы понимаете эти два метода ( invalidate()и requestLayout()) правильно. Цель этих методов - рассказать, Viewчто за недействительность (как вы ее назвали) произошла. Это не похоже на Viewрешение, по какому пути следовать после вызова одного из этих методов. Логика выбора View-lifecycle-path - это выбор правильного метода для вызова самого себя. Если есть что-то, связанное с изменением размера - requestLayout()следует вызвать, если есть только визуальное изменение без изменения размера - следует позвонить invalidate().
Бартек Липински

11
(2/2): Если вы Viewкаким-либо образом измените размер своего , например, вы получаете токLayoutParams a Viewи изменяете их, но НЕ вызываете ни один, requestLayoutни setLayoutParams(который вызывает requestLayoutвнутри), то вы можете звонить invalidate()столько, сколько хотите, и Viewне будет проходить через процесс измерения верстки, поэтому не будет изменять его размер. Если вы не скажете, Viewчто его размер изменился (с помощью requestLayoutвызова метода), тогда он Viewбудет считать, что это не так, onMeasureи onLayoutне будет вызываться.
Бартек Липински

2
@tcox больше здесь -> stackoverflow.com/questions/35279374/…
Sotti

125

invalidate()

Вызов invalidate()выполняется, когда вы хотите запланировать перерисовку представления. Это приведет к тому, onDrawчто вызов будет в конце концов (скоро, но не сразу). Пример того, когда пользовательское представление будет вызывать его, - это когда свойство цвета текста или фона изменилось.

Вид будет перерисован, но размер не изменится.

requestLayout()

Если что-то в вашем представлении изменится, что повлияет на размер, вам следует позвонить requestLayout(). Это сработает, onMeasureи onLayoutне только для этого представления, но и до линии родительских представлений.

Вызов requestLayout()будет не гарантировано приведет кonDraw (вопреки тому , что предполагает диаграмма в общепринятом ответа), так что, как правило , в сочетании с invalidate().

invalidate();
requestLayout();

Примером этого может быть изменение свойства текста у пользовательской метки. Ярлык изменит размер, и, следовательно, его необходимо переизмерить и перерисовать.

forceLayout()

Когда есть requestLayout()вызов, который вызывается в родительской группе представлений, нет необходимости пересматривать и ретранслировать его дочерние представления. Однако, если ребенок должен быть включен в переиздание и ретрансляцию, вы можете вызвать forceLayout()ребенка. forceLayout()работает только на ребенка, если это происходит в сочетании сrequestLayout() на его непосредственном родителе. Вызов forceLayout()сам по себе не будет иметь никакого эффекта, так как он не запускает requestLayout()дерево представлений.

Читать этот раздел вопросов и ответов для более подробного описанияforceLayout() .

Дальнейшее обучение


1
но разве не имеет смысла сначала вызывать requestLayout (), а затем инвалидировать ()?
схолт

2
@scholt, насколько я знаю, порядок не имеет значения, поэтому вы можете позвонить requestLayout()раньше, invalidate()если хотите. Никто не делает макет или рисует сразу. Скорее, они устанавливают флаги, которые в конечном итоге приводят к ретрансляции и перерисовке.
Сурагч

27

Здесь вы можете найти ответ: http://developer.android.com/guide/topics/ui/how-android-draws.html

Для меня вызов invalidate()только обновляет представление и вызов, чтобы requestLayout()обновить представление и вычислить размер представления на экране.


3
как насчет forceLayout () тогда?
Сдабет

@fiddler, этот метод просто устанавливает два флага: PFLAG_FORCE_LAYOUT и PFLAG_INVALIDATED
suitianshi

7
@suitianshi Каковы последствия установки флагов тогда?
Сергей

1
@Sergey Следствием этого является то, что этот флаг переопределяет кэш измерений (представления используют кэш для более быстрого измерения в будущем для того же MeasureSpec); см. строку 18783 из View.java здесь: github.com/android/platform_frameworks_base/blob/master/core/…
RhetoricalRuvim

3

вы используете invalidate () для представления, которое вы хотите перерисовать, он вызовет его onDraw (Canvas c) для вызова, а requestLayout () заставит весь рендеринг макета (фаза измерения и фаза позиционирования) выполняться снова. Вы должны использовать его, если вы изменяете размер дочернего представления во время выполнения, но только в особых случаях, например, ограничения из родительского представления (под этим я подразумеваю, что высота или ширина родительского элемента равны WRAP_CONTENT и, таким образом, соответствуют измерению дочерних элементов, прежде чем они смогут снова их обернуть)


3

Этот ответ не является правильным оforceLayout() .

Как вы можете видеть в кодеforceLayout() он просто помечает представление как «нуждается в ретрансляции», но он не планирует и не запускает это ретрансляцию. Ретрансляция не произойдет, пока в какой-то момент в будущем родительский элемент представления не будет выложен по какой-либо другой причине.

Существует также гораздо большая проблема при использовании forceLayout()и requestLayout():

Допустим, вы позвонили forceLayout()на представление. Теперь при вызове requestLayout()потомка этого представления Android будет рекурсивно вызывать requestLayout()предков этого потомка. Проблема в том, что он остановит рекурсию в представлении, на которое вы звонили forceLayout(). Таким образом, requestLayout()вызов никогда не достигнет корня представления и, следовательно, никогда не запланирует передачу макета. Целое поддерево иерархии представления ожидает макета, и вызов requestLayout()любого представления этого поддерева не вызовет макет. Только вызов requestLayout()любого вида за пределами этого поддерева разрушит заклинание.

Я бы подумал, что реализация forceLayout()(и как она влияет, requestLayout()будет нарушена, и вы никогда не должны использовать эту функцию в своем коде.


1
Здравствуй! Как ты пришел с этим заклинанием ? Это где-то задокументировано?
Азизбекян

1
К сожалению, это не задокументировано и, вероятно, даже не предназначено. Я понял это, исследуя код Viewи сам разбираясь с проблемой и отлаживая ее.
Fluidsonic

Тогда мне интересно узнать о цели forceLayout()API: он на самом деле не вызывает передачу макета, скорее он просто изменяет флаг, в котором наблюдаетсяonMeasure() , но onMeasure()не будет вызываться, пока не будет вызван requestLayout()явный View#measure(). Это означает, что это forceLayout()должно быть в паре с requestLayout(). С другой стороны, зачем тогда выполнять, forceLayout()если мне все еще нужно выполнять requestLayout()?
Азизбекян

@azizbekian нет, ты не обязан. requestLayout()делает все, что forceLayout()делает.
Fluidsonic

1
@Suragch пропустил это, спасибо! Очень интересный анализ. Предполагаемое использование forceLayoutимеет смысл. Так что, в конце концов, он очень плохо назван и задокументирован.
Fluidsonic

0

invalidate()---> onDraw()из пользовательского интерфейса

postInvalidate()---> onDraw()из фоновой темы

requestLayout()---> onMeasure()и onLayout()И НЕ ОБЯЗАТЕЛЬНО onDraw()

  • ВАЖНО : вызов этого метода не влияет на дочерний элемент вызываемого класса.

forceLayout()---> onMeasure()и onLayout() ПРОСТО, ЕСЛИ позвонил прямой родитель requestLayout().

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.