Ответ 1
TL; DR Рассмотрим следующий код из TableLayout:
public void requestLayout() {
if (mInitialized) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).forceLayout();
}
}
super.requestLayout();
}
Здесь каждый дочерний элемент TableLayout будет помечен как подлежащий измерению в будущем макете через вызов forceLayout()
. Подобная обработка будет выполняться, если requestLayout()
вызывается для каждого дочернего элемента, но requestLayout()
пузырится вверх по иерархии представления, поэтому requestLayout()
дочернего элемента TableLayout вызывает его родительский requestLayout()
. Это создаст бесконечный цикл с TableLayout и его дочерним, вызывающим друг друга. forceLayout()
измеряет без угрозы бесконечной рекурсии.
forceLayout()
не вызывает requestLayout()
для своего родителя, как указано, но очищает кеш просмотра и устанавливает пару флагов.
public void forceLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
}
requestLayout()
очищает кеш и устанавливает эти же флаги как forceLayout()
, но также может вызывать requestLayout()
у родителя.
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
...
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
...
}
requestLayout()
должен пузыриться по всей иерархии.
Итак, что делает forceLayout()
на самом деле? Чтобы исследовать этот вопрос, я взял предоставленное приложение и изменил его для отслеживания вызовов onMeasure()
, onLayout()
и onDraw()
для двух групп представлений (дедушка и родительский элемент) и дочернего представления. Я добавил ребенка-сиблинга к первому, чтобы сравнить то, что происходит с ними двумя. Я также использовал отладчик для отслеживания вызовов measure()
и requestLayout()
. Выход был зафиксирован в logcat, и с logcat была подготовлена электронная таблица, суммирующая операции. (Исходный код и ссылка на документацию в этом ответе каталогизированы на этот проект GitHub.
Тест-приложение вызывает forceLayout()
и requestLayout()
для двух групп просмотра и дочернего представления во всех возможных комбинациях - всего 64. (Многие из этих комбинаций не реалистичны в реальном мире, но включены для полноты.) В таблице ниже приведены ключевые области для обсуждения. Полный лист можно найти в репозитории GitHub.
Раздел A. В этом разделе на трех представлениях вызывается forceLayout()
. Как заметил Сурагч, на самом деле ничего не происходит, кроме onDraw()
, когда forceLayout()
вызывается во всех представлениях. Вот журнал для раздела A:
I/MainActivity: 1 ****************************************** *
I/MainActivity: 2 *******************************************
I/MainActivity: 3 *******************************************
I/MainActivity: 4 *******************************************
I/MainActivity: 5 *******************************************
I/MainActivity: 6 *******************************************
I/MainActivity: 7 *******************************************
I/MyChildView: onDraw называется (1)
"1 ****..." соответствует строке в электронной таблице. Линии типа "I/MyChildView: onDraw called (1)" идентифицируют представление ( "MyChildView" ), метод представления ( "onDraw" ) и "(x)" будет "(1)" для первого детского представления ", (2)" для второго дочернего представления и "(null)" для других не-дочерних представлений.
Это неожиданный результат с именем метода: forceLayout()
.
Раздел B. В строке 8 вызывается requestLayout()
в дочернем представлении, и результаты ожидаются: на всех представлениях принимаются мера, макет и чертеж. Строка 9 добавляет вызов forceLayout()
к ребенку, но результаты те же. Вот журнал для раздела B:
/MainActivity: 8 *******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested = false (1)
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested = false (null)
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested = false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested = false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested = false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure, называемое
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure называется
I/measure: MyChildView (1)
I/MyChildView: onMeasure называется (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout называется
I/ViewGroupParent: onLayout называется
I/MyChildView: onLayout называется (1)
I/MyChildView: onDraw называется (1)
I/MainActivity: 9 *******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested = false (1)
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested = false (null)
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested = false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested = false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested = false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure, называемое
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure называется
I/measure: MyChildView (1)
I/MyChildView: onMeasure называется (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout называется
I/ViewGroupParent: onLayout называется
I/MyChildView: onLayout называется (1)
I/MyChildView: onDraw называется (1)
Раздел C. Здесь интересны вещи. Для строк 10 и 11 вызывается requestLayout()
в дочернем представлении, а forceLayout()
вызывается в дочернем родительском представлении. В результате последующие шаги измерения/макета/ничьи, которые мы видели в разделе B, не происходят. Я считаю, что именно поэтому fluidsonic сказал, что forceLayout()
нарушено. См. fooobar.com/info/52896/....
Фактически, дочерний вид рассматривает вызов requestLayout()
в родительском, но находит, что макет уже запрошен у родителя. (mParent != null && !mParent.isLayoutRequested()
). Ниже приведена ссылка на код для isLayoutRequested()
.
public boolean isLayoutRequested() {
return (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
}
Помните, что forceLayout()
установите флаг PFLAG_FORCE_LAYOUT
. Вот почему цепочка requestLayout()
останавливается у родителя. Это может быть проблемой или просто неправильным использованием forceLayout()
.
Продолжая работу с остальной частью C, мы можем "заставить" больше всего вызвать вызов дочернего элемента onDraw()
.
Вот журнал для раздела C:
I/MainActivity: 10 ****************************************** *
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested = true (1)
I/MainActivity: 11 *******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested = true (1)
I/MainActivity: 12 *******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested = false (1)
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested = true (null)
I/MyChildView: onDraw называется (1)
I/MainActivity: 13 *******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested = false (1)
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested = true (null)
I/MyChildView: onDraw называется (1)
I/MainActivity: 14 *******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested = true (1)
I/MyChildView: onDraw называется (1)
I/MainActivity: 15 *******************************************
I/requestLayout: MyChildView (1)
I/requestLayout: MyChildView isLayoutRequested = true (1)
I/MyChildView: onDraw называется (1)
Раздел D. Этот раздел может содержать секрет forceLayout()
. В строке 16 вызов requestLayout()
для родителя приводит к прохождению меры/макета для родителя и дедушки, но не для ребенка. Если вызов для forceLayout()
сделан на дочернем элементе, тогда ребенок включен. Фактически, вызов выполняется дочернему элементу onMeasure()
, в то время как вызов не выполняется для его родного брата onMeasure()
. Это связано с призывом к forceLayout()
для ребенка. Таким образом, кажется, что здесь forceLayout()
используется, чтобы заставить структуру измерить ребенка, который обычно не измерялся. Замечу, что это происходит только тогда, когда forceLayout()
вызывается в прямом потомке целевого представления requestLayout()
.
Одним из таких примеров такого типа обработки является TableLayout. В переопределении requestLayout()
в TableLayout для каждого дочернего элемента вызывается forceLayout()
. Это позволит избежать вызова requestLayout()
для каждого дочернего элемента и связанных с ним служебных данных, которые повлекут за собой (хотя, вероятно, и небольшие). Это также позволит избежать катастрофической рекурсии, так как дочерний элемент requestLayout()
может вызывать родительский requestLayout()
, который будет вызывать дочерний... вы получаете идею. Вот код из TableLayout:
public void requestLayout() {
if (mInitialized) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).forceLayout();
}
}
super.requestLayout();
}
В ListView.java существует необходимость переопределить дочерний элемент перед повторным использованием. Смотрите здесь здесь. forceLayout()
работает здесь, чтобы ребенок переоценил.
// Since this view was measured directly aginst the parent measure
// spec, we must measure it again before reuse.
child.forceLayout();
Вот журнал для раздела D:
I/MainActivity: 16 ****************************************** *
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested = false (null)
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested = false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested = false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested = false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure, называемое
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure называется
I/measure: MyChildView (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout называется
I/ViewGroupParent: onLayout называется
I/MainActivity: 17 *******************************************
I/requestLayout: ViewGroupParent (null)
I/requestLayout: ViewGroupParent isLayoutRequested = false (null)
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested = false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested = false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested = false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure, называемое
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure называется
I/measure: MyChildView (1)
I/MyChildView: onMeasure называется (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout называется
I/ViewGroupParent: onLayout называется
I/MyChildView: onLayout называется (1)
I/MyChildView: onDraw называется (1)
Раздел E. Этот раздел далее демонстрирует, что в запущенных макетах передачи участвуют только прямые потомки целевого представления вызова requestLayout()
. Строки 34 и 35, по-видимому, указывают на то, что вложенные представления могут цепочки.
Вот журнал для раздела E:
I/MainActivity: 32 ****************************************** *
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested = false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested = false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested = false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure, называемое
I/measure: ViewGroupParent (null)
I/ViewGroupGrandparent: onLayout называется
I/MainActivity: 33 *******************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested = false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested = false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested = false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure, называемое
I/measure: ViewGroupParent (null)
I/ViewGroupGrandparent: onLayout называется
I/MainActivity: 34 *******************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested = false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested = false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested = false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure, называемое
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure называется
I/measure: MyChildView (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout называется
I/ViewGroupParent: onLayout называется
I/MainActivity: 35 *******************************************
I/requestLayout: ViewGroupGrandparent (null)
I/requestLayout: ViewGroupGrandparent isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: ContentFrameLayout (null)
I/requestLayout: ContentFrameLayout isLayoutRequested = false (null)
I/requestLayout: ActionBarOverlayLayout (null)
I/requestLayout: ActionBarOverlayLayout isLayoutRequested = false (null)
I/requestLayout: FrameLayout (null)
I/requestLayout: FrameLayout isLayoutRequested = false (null)
I/requestLayout: LinearLayout (null)
I/requestLayout: LinearLayout isLayoutRequested = false (null)
I/requestLayout: DecorView (null)
I/requestLayout: DecorView isLayoutRequested = false (null)
I/measure: ViewGroupGrandparent (null)
I/ViewGroupGrandparent: onMeasure, называемое
I/measure: ViewGroupParent (null)
I/ViewGroupParent: onMeasure называется
I/measure: MyChildView (1)
I/MyChildView: onMeasure называется (1)
I/measure: MyChildView (2)
I/ViewGroupGrandparent: onLayout называется
I/ViewGroupParent: onLayout называется
I/MyChildView: onLayout называется (1)
I/MyChildView: onDraw называется (1)
Итак, это мой прием для forceLayout()
: используйте его, когда есть дети, которые должны быть пересчитаны, например, в TableLayout, и вы не хотите называть requestLayout()
для каждого ребенка - forceLayout()
более легкий вес и избежит рекурсии. (См. Примечания в Разделе C.) forceLayout()
также может использоваться для принудительного перенаправления конкретных прямых детей, когда это необходимо, когда они обычно не измеряются. forceLayout()
не работает в одиночку и должен быть сопряжен с соответствующими вызовами requestLayout()