onMeasure и onLayout дочерних представлений не вызываются после requestLayout для их родителей

Я пытаюсь понять, как Android измеряет и выкладывает взгляды. Я создал образец приложения с пользовательскими представлениями и группами просмотра.

CustomViewGroup

public class CustomViewGroup extends ViewGroup {

    private static final String LOG_TAG = "CustomViewGroup";

    public CustomViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(LOG_TAG, String.format(Locale.ENGLISH,
                getTag() + " " +
                        "onMeasure(widthMeasureSpec=%d, heightMeasureSpec%d)",
                widthMeasureSpec, heightMeasureSpec
        ));

        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != View.GONE) {
                // Make or work out measurements for children here (MeasureSpec.make...)
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        Log.d(LOG_TAG, String.format(Locale.ENGLISH,
                getTag() + " " +
                        "onLayout(changed=%b, left=%d, top=%d, right=%d, bottom=%d)",
                changed, left, top, top, right, bottom
        ));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.d(LOG_TAG, getTag() + " " + "onDraw");
    }
}

CustomView

public class CustomView extends View {

    private static final String LOG_TAG = "CustomView";

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.d(LOG_TAG, String.format(Locale.ENGLISH,
                getTag() + " " +
                "onLayout(changed=%b, left=%d, top=%d, right=%d, bottom=%d)",
                changed, left, top, top, right, bottom
        ));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.d(LOG_TAG, getTag() + " " +"onDraw");
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(LOG_TAG, String.format(Locale.ENGLISH,
                getTag() + " " +
                "onMeasure(widthMeasureSpec=%d, heightMeasureSpec=%d)",
                widthMeasureSpec, heightMeasureSpec
        ));
    }
}

Основная деятельность

public class MainActivity extends AppCompatActivity {

    private View mCustomGroup1;
    private View mCustomGroup2;
    private View mContainer;

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

        Log.d("MainAct", "onCreate");
        mCustomGroup1 = findViewById(R.id.requestLayout);
        mCustomGroup2 = findViewById(R.id.requestLayout2);
        mContainer = findViewById(R.id.activity_main);

        findViewById(R.id.requestLayoutBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCustomGroup1.requestLayout();
            }
        });

        findViewById(R.id.requestLayoutBtn2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCustomGroup2.requestLayout();
            }
        });

        findViewById(R.id.requestLayoutContBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mContainer.requestLayout();
            }
        });

    }
}

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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="ru.dmitriev.squareorderedlayout.MainActivity">

    <Button
        android:id="@+id/requestLayoutBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="requestLayout" />

    <Button
        android:id="@+id/requestLayoutBtn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="requestLayout2" />

    <Button
        android:id="@+id/requestLayoutContBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="requestLayoutCont" />

    <ru.dmitriev.squareorderedlayout.CustomViewGroup
        android:id="@+id/requestLayout"
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#7f7"
        android:tag="CustomVieGroup1">

        <ru.dmitriev.squareorderedlayout.CustomView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:tag="CustomView11" />

        <ru.dmitriev.squareorderedlayout.CustomView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:tag="CustomView12" />
    </ru.dmitriev.squareorderedlayout.CustomViewGroup>

    <ru.dmitriev.squareorderedlayout.CustomViewGroup
        android:id="@+id/requestLayout2"
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#f77"
        android:tag="CustomVieGroup2">

        <ru.dmitriev.squareorderedlayout.CustomView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:tag="CustomView21" />

        <ru.dmitriev.squareorderedlayout.CustomView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:tag="CustomView22" />

    </ru.dmitriev.squareorderedlayout.CustomViewGroup>
</LinearLayout>

Когда начинается деятельность, onMeasure, onLayout а также onDraw вызываются в тегах групп просмотра CustomVieGroup1 а также CustomVieGroup2, Каждая группа представления вызывает эти методы для своих детей. Все нормально. Я понимаю, это.

Когда я звоню requestLayout в группе представлений с тегом CustomVieGroup1, onMeasure, onLayout а также onDraw Вызываются на CustomVieGroup1. Группа вызывает эти методы для своих детей. Я понимаю, это.

Когда я звоню requestLayout в группе представлений с тегом CustomVieGroup2, onMeasure, onLayout а также onDraw Вызываются на CustomVieGroup2. Группа вызывает эти методы для своих детей. Я тоже это понимаю.

Но когда я звоню requestLayout на вид с идентификатором activity_main, onMeasure а также onLayout не призваны CustomVieGroup1 или же CustomVieGroup2, Зачем? Я ожидал что onMeasure, onLayout а также onDraw будет вызван на обоих CustomVieGroup1 а также CustomVieGroup2 (т.е. cgildren представления с идентификатором activity_main)

Согласно документации requestLayout:

Это запланирует проход макета дерева представления.

1 ответ

Решение

requestLayout() вызовет onLayout() метод LinearLayout (@id/activity_main) для выполнения. Тем не менее, нет никаких гарантий, что LinearLayout затем переделаем своих детей; это действительно зависит от его реализации. Например, ваш LinearLayout ширина / высота установлена match_parent, Когда LinearLayout во второй раз, его ширина и высота не изменятся, потому что его размер основан на родительском размере, а не на его дочерних элементах. Поскольку его расположение не основано на его дочерних элементах, нет необходимости LinearLayout изменить макет своих детей. Вот почему аннулирование макета почти любого ViewGroup размер которого совпадает с его родителем, не приведет к тому, что дочерние элементы этого макета будут размечены.

Вы должны быть в состоянии подтвердить, что это меняет ваш LinearLayout (@id/activity_main) в 'wrap_content'. Таким образом, размер макета становится зависимым от его дочерних элементов, поэтому ему необходимо изменить макет дочерних элементов. Это приведет к вызову методов пользовательского макета представления. Надеюсь это поможет,

Другие вопросы по тегам