Как работает forceLayout() в Android

В этот ответ я написал

forceLayout()

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

В принципе, вызов requestLayout() приводит к вызову parent.requestLayout(), но вызов forceLayout() не выполняется.

Насколько я помню, я написал свой ответ, прочитав документацию и source код. Однако я не использовал forceLayout. Пользователь прокомментировал, что он не работал, как я описал.

Тестирование forceLayout

Я, наконец, обдумываю причину этого. Я создал простой проект с grandparent ViewGroup, родительский ViewGroup и дочерний View. Я использовал пользовательские представления для каждого из них, чтобы я мог наблюдать за операторами журнала в onMeasure, onLayout и onDraw.

Когда макет сначала создается из xml, я получаю следующий журнал:

ViewGroupGrandparent onMeasure called
ViewGroupParent onMeasure called
MyChildView onMeasure called
ViewGroupGrandparent onMeasure called
ViewGroupParent onMeasure called
MyChildView onMeasure called
ViewGroupGrandparent onLayout called
ViewGroupParent onLayout called
MyChildView onLayout called
MyChildView onDraw called

forceLayout

Это выглядит как разумный вывод. Однако, когда я впоследствии называю forceLayout индивидуально по любому из представлений, я ничего не получаю. Если я их сразу вызову, тогда вызывается дочерний вид onDraw.

ребенок

childView.forceLayout();
// (no log output)

родителя

viewGroupParent.forceLayout();
// (no log output)

прародитель

viewGroupGrandparent.forceLayout();
// (no log output)

все вместе

childView.forceLayout();
viewGroupParent.forceLayout();
viewGroupGrandparent.forceLayout();

// MyChildView onDraw called

requestLayout

С другой стороны, вызов requestLayout имеет гораздо больший эффект.

ребенок

childView.requestLayout();

// ViewGroupGrandparent onMeasure called
// ViewGroupParent onMeasure called
// MyChildView onMeasure called
// ViewGroupGrandparent onLayout called
// ViewGroupParent onLayout called
// MyChildView onLayout called
// MyChildView onDraw called

родителя

viewGroupParent.requestLayout();

// ViewGroupGrandparent onMeasure called
// ViewGroupParent onMeasure called
// ViewGroupGrandparent onLayout called
// ViewGroupParent onLayout called

прародитель

viewGroupGrandparent.requestLayout();

// ViewGroupGrandparent onMeasure called
// ViewGroupGrandparent onLayout called

Вопрос

Когда forceLayout имеет какой-либо эффект? Почему это не работает, как это предполагается в моих примерах выше?

Дополнительный код

Вот код, который я использовал для выполнения тестов выше.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.forcelayout.MainActivity">

    <com.example.forcelayout.ViewGroupGrandparent
        android:id="@+id/view_group_grandparent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <com.example.forcelayout.ViewGroupParent
            android:id="@+id/view_group_parent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <com.example.forcelayout.MyChildView
                android:id="@+id/child_view"
                android:layout_width="100dp"
                android:layout_height="100dp"/>
        </com.example.forcelayout.ViewGroupParent>
    </com.example.forcelayout.ViewGroupGrandparent>

    <Button
        android:text="Click me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="buttonClick"/>

</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void buttonClick(View view) {
        Log.i("TAG", "buttonClick: ");

        ViewGroupGrandparent viewGroupGrandparent = (ViewGroupGrandparent) findViewById(R.id.view_group_grandparent);
        ViewGroupParent viewGroupParent = (ViewGroupParent) findViewById(R.id.view_group_parent);
        MyChildView childView = (MyChildView) findViewById(R.id.child_view);


        childView.forceLayout();
        //viewGroupParent.forceLayout();
        //viewGroupGrandparent.forceLayout();

        //childView.requestLayout();
        //viewGroupParent.requestLayout();
        //viewGroupGrandparent.requestLayout();
    }
}

ViewGroupGrandparent.java

public class ViewGroupGrandparent extends LinearLayout {

    public ViewGroupGrandparent(Context context) {
        super(context);
    }

    public ViewGroupGrandparent(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ViewGroupGrandparent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i("TAG", "ViewGroupGrandparent onMeasure called");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.i("TAG", "ViewGroupGrandparent onLayout called");
        super.onLayout(changed, l, t, r, b);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.i("TAG", "ViewGroupGrandparent onDraw called");
        super.onDraw(canvas);
    }
}

ViewGroupParent.java

public class ViewGroupParent extends LinearLayout {

    public ViewGroupParent(Context context) {
        super(context);
    }

    public ViewGroupParent(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ViewGroupParent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i("TAG", "ViewGroupParent onMeasure called");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.i("TAG", "ViewGroupParent onLayout called");
        super.onLayout(changed, l, t, r, b);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.i("TAG", "ViewGroupParent onDraw called");
        super.onDraw(canvas);
    }
}

MyChildView.java

public class MyChildView extends View {

    public MyChildView(Context context) {
        super(context);
    }

    public MyChildView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyChildView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i("TAG", "MyChildView onMeasure called");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        Log.i("TAG", "MyChildView onLayout called");
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.i("TAG", "MyChildView onDraw called");
        super.onDraw(canvas);
    }
}

Ответ 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()