在Android上使用RelativeLayout时的双重征税 [英] Double Taxation when using a RelativeLayout on Android

查看:82
本文介绍了在Android上使用RelativeLayout时的双重征税的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了了解双重征税在Android上,我编写了以下非常简单的代码.有一个带有三个TextViewRelativeLayout.

<?xml version="1.0" encoding="utf-8"?>
<ru.maksim.sample_app.MyRelativeLayout 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="ru.maksim.sample_app.MainActivity">

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fca"
        android:tag="text1"
        android:text="Text 1" />

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/text1"
        android:background="#acf"
        android:tag="text2"
        android:text="Text 2" />

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/text1"
        android:layout_toRightOf="@id/text2"
        android:background="#fac"
        android:tag="text3"
        android:text="text 3" />

</ru.maksim.sample_app.MyRelativeLayout>

MyTextView

public class MyTextView extends android.support.v7.widget.AppCompatTextView {

    private static final String TAG = "MyTextView";

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

    public MyTextView(Context context,
                      @Nullable AttributeSet attrs
    ) {
        super(context, attrs);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
        Log.d(TAG,
              "onMeasure, "
                      + getTag()
                      + " widthMeasureSpec=" + MeasureSpecMap.getName(widthMode)
                      + " heightMeasureSpec=" + MeasureSpecMap.getName(heightMode)
        );
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.d(TAG, getTag() + " onLayout");
    }
}

MyRelativeLayout

public class MyRelativeLayout extends RelativeLayout {

    public static final String TAG = "MyRelativeLayout";

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
        Log.d(TAG,
              "onMeasure, "
                      + getTag()
                      + " widthMeasureSpec=" + MeasureSpecMap.getName(widthMode)
                      + " heightMeasureSpec=" + MeasureSpecMap.getName(heightMode)
        );
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.d(TAG, getTag() + " onLayout");
    }
}

Logcat:

09-11 19:25:40.077 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text3 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text2 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyRelativeLayout: onMeasure, null widthMeasureSpec=EXACTLY heightMeasureSpec=EXACTLY

                                                                      [ 09-11 19:25:40.098  7732: 7748 D/         ]
                                                                      HostConnection::get() New Host Connection established 0xa0a8fbc0, tid 7748
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text3 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text2 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyRelativeLayout: onMeasure, null widthMeasureSpec=EXACTLY heightMeasureSpec=EXACTLY
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: text1 onLayout
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: text2 onLayout
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: text3 onLayout
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyRelativeLayout: null onLayout

现在,我们将MyRelativeLayout替换为名为MyLinearLayoutLinearLayoiut子级:

<?xml version="1.0" encoding="utf-8"?>
<ru.maksim.sample_app.MyLinearLayout 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"
    android:orientation="vertical"
    tools:context="ru.maksim.sample_app.MainActivity">

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fca"
        android:tag="text1"
        android:text="Text 1" />

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#acf"
        android:tag="text2"
        android:text="Text 2" />

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fac"
        android:tag="text3"
        android:text="text 3" />

</ru.maksim.sample_app.MyLinearLayout>

MyLinearLayout

public class MyLinearLayout extends LinearLayout {

    public static final String TAG = "MyLinearLayout";

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
        Log.d(TAG,
              "onMeasure, "
                      + getTag()
                      + " widthMeasureSpec=" + MeasureSpecMap.getName(widthMode)
                      + " heightMeasureSpec=" + MeasureSpecMap.getName(heightMode)
        );
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.d(TAG, getTag() + " onLayout");
    }
}

这是我现在在logcat中看到的内容:

09-11 19:50:57.974 2781-2781/? D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:50:57.974 2781-2781/? D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:50:57.974 2781-2781/? D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:50:57.974 2781-2781/? D/MyLinearLayout: onMeasure, null widthMeasureSpec=EXACTLY heightMeasureSpec=EXACTLY

                                                 [ 09-11 19:50:58.004  2781: 2817 D/         ]
                                                 HostConnection::get() New Host Connection established 0xa5ec1940, tid 2817
09-11 19:50:58.017 2781-2781/? D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:50:58.017 2781-2781/? D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:50:58.017 2781-2781/? D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:50:58.017 2781-2781/? D/MyLinearLayout: onMeasure, null widthMeasureSpec=EXACTLY heightMeasureSpec=EXACTLY
09-11 19:50:58.017 2781-2781/? D/MyTextView: text1 onLayout
09-11 19:50:58.017 2781-2781/? D/MyTextView: text2 onLayout
09-11 19:50:58.017 2781-2781/? D/MyTextView: text3 onLayout
09-11 19:50:58.017 2781-2781/? D/MyLinearLayout: null onLayout

上面两个示例中使用的

MeasureSpecMap仅包含以下Map:

public class MeasureSpecMap {

    private static final Map<Integer, String> MAP = new HashMap<>();

    static {
        MAP.put(View.MeasureSpec.AT_MOST, "AT_MOST");
        MAP.put(View.MeasureSpec.EXACTLY, "EXACTLY");
        MAP.put(View.MeasureSpec.UNSPECIFIED, "UNSPECIFIED");
    }

    private MeasureSpecMap() {

    }

    public static String getName(int mode) {
        return MAP.get(mode);
    }

}

问题1.

使用MyRelativeLayout时,为什么在调用MyRelativeLayoutonMeasure之前,系统需要在每个孩子上两次调用onMeasure?在您的示例中,对于MyLinearLayout,每个孩子都会被测量一次,如您在上面的日志输出中所见.

问题2.

以下是双重征税"部分中的其他内容我不明白:

使用RelativeLayout容器时,您可以使用 将View对象相对于其他View的位置定位 对象,该框架执行以下操作:

执行布局和度量传递,在此期间框架 根据每个子对象的位置,计算每个子对象的位置和大小 孩子的要求.使用此数据,也将对象权重计入 帐户,以找出相关视图的正确位置.

使用此数据,同时考虑对象权重,以找出 相关视图的正确位置.

但是等等...他们不是在谈论android:layout_weight这是LinearLayout的功能吗?


上面的代码也可以在GitHub上获得:

使用MyLinearLayout

的方法

使用MyRelativeLayout

的方法

解决方案

问题1.

使用MyRelativeLayout时,为什么在调用MyRelativeLayoutonMeasure之前,系统需要在每个孩子上两次调用onMeasure?在您的示例中,对于MyLinearLayout,每个孩子都会被测量一次,如您在上面的日志输出中所见.

简单地说,一个视图的大小和位置会影响另一个视图的大小和位置.

考虑text3:其宽度不仅取决于所保存文本的长度,还取决于text2的宽度.如果text2消耗了80%的屏幕,那么text3将最多只能获得20%的屏幕.

因此,系统首先进行测量以找出约束".该视图将彼此放置,然后进行第二次测量以找出要使用的最终值.

看看您的日志输出(为简便起见,省略了一些文本):

D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
D/MyTextView: onMeasure, text3 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST

D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
D/MyTextView: onMeasure, text2 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST

看看如何第一次测量text2,它的widthMeasureSpec是模式AT_MOST,而第二次它是模式EXACTLY?第一遍使text2有机会消耗多达100%的屏幕宽度,但第二遍将其限制为实际所需的尺寸.

对于垂直LinearLayout,系统可以通过一次测量获得所需的所有信息.允许text1达到整个窗口高度,允许text2达到整个窗口高度减去text1的高度,依此类推.

问题2.

...

但是等等...他们不是在谈论android:layout_weight这是LinearLayout的功能吗?

我也不理解这部分.我倾向于相信CommonsWare的猜测,它只是一个文档错误.

In order to understand Double Taxation on Android, I wrote the following code which is pretty simple. There is a RelativeLayout with three TextViews.

<?xml version="1.0" encoding="utf-8"?>
<ru.maksim.sample_app.MyRelativeLayout 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="ru.maksim.sample_app.MainActivity">

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fca"
        android:tag="text1"
        android:text="Text 1" />

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/text1"
        android:background="#acf"
        android:tag="text2"
        android:text="Text 2" />

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/text1"
        android:layout_toRightOf="@id/text2"
        android:background="#fac"
        android:tag="text3"
        android:text="text 3" />

</ru.maksim.sample_app.MyRelativeLayout>

MyTextView

public class MyTextView extends android.support.v7.widget.AppCompatTextView {

    private static final String TAG = "MyTextView";

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

    public MyTextView(Context context,
                      @Nullable AttributeSet attrs
    ) {
        super(context, attrs);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
        Log.d(TAG,
              "onMeasure, "
                      + getTag()
                      + " widthMeasureSpec=" + MeasureSpecMap.getName(widthMode)
                      + " heightMeasureSpec=" + MeasureSpecMap.getName(heightMode)
        );
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.d(TAG, getTag() + " onLayout");
    }
}

MyRelativeLayout

public class MyRelativeLayout extends RelativeLayout {

    public static final String TAG = "MyRelativeLayout";

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
        Log.d(TAG,
              "onMeasure, "
                      + getTag()
                      + " widthMeasureSpec=" + MeasureSpecMap.getName(widthMode)
                      + " heightMeasureSpec=" + MeasureSpecMap.getName(heightMode)
        );
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.d(TAG, getTag() + " onLayout");
    }
}

Logcat:

09-11 19:25:40.077 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text3 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text2 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.078 7732-7732/ru.maksim.sample_app D/MyRelativeLayout: onMeasure, null widthMeasureSpec=EXACTLY heightMeasureSpec=EXACTLY

                                                                      [ 09-11 19:25:40.098  7732: 7748 D/         ]
                                                                      HostConnection::get() New Host Connection established 0xa0a8fbc0, tid 7748
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text3 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: onMeasure, text2 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyRelativeLayout: onMeasure, null widthMeasureSpec=EXACTLY heightMeasureSpec=EXACTLY
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: text1 onLayout
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: text2 onLayout
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyTextView: text3 onLayout
09-11 19:25:40.132 7732-7732/ru.maksim.sample_app D/MyRelativeLayout: null onLayout

Now let's replace MyRelativeLayout with a child of LinearLayoiut called MyLinearLayout:

<?xml version="1.0" encoding="utf-8"?>
<ru.maksim.sample_app.MyLinearLayout 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"
    android:orientation="vertical"
    tools:context="ru.maksim.sample_app.MainActivity">

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fca"
        android:tag="text1"
        android:text="Text 1" />

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#acf"
        android:tag="text2"
        android:text="Text 2" />

    <ru.maksim.sample_app.MyTextView
        android:id="@+id/text3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fac"
        android:tag="text3"
        android:text="text 3" />

</ru.maksim.sample_app.MyLinearLayout>

MyLinearLayout

public class MyLinearLayout extends LinearLayout {

    public static final String TAG = "MyLinearLayout";

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
        Log.d(TAG,
              "onMeasure, "
                      + getTag()
                      + " widthMeasureSpec=" + MeasureSpecMap.getName(widthMode)
                      + " heightMeasureSpec=" + MeasureSpecMap.getName(heightMode)
        );
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.d(TAG, getTag() + " onLayout");
    }
}

Here is what I see in logcat now:

09-11 19:50:57.974 2781-2781/? D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:50:57.974 2781-2781/? D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:50:57.974 2781-2781/? D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:50:57.974 2781-2781/? D/MyLinearLayout: onMeasure, null widthMeasureSpec=EXACTLY heightMeasureSpec=EXACTLY

                                                 [ 09-11 19:50:58.004  2781: 2817 D/         ]
                                                 HostConnection::get() New Host Connection established 0xa5ec1940, tid 2817
09-11 19:50:58.017 2781-2781/? D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:50:58.017 2781-2781/? D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
09-11 19:50:58.017 2781-2781/? D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
09-11 19:50:58.017 2781-2781/? D/MyLinearLayout: onMeasure, null widthMeasureSpec=EXACTLY heightMeasureSpec=EXACTLY
09-11 19:50:58.017 2781-2781/? D/MyTextView: text1 onLayout
09-11 19:50:58.017 2781-2781/? D/MyTextView: text2 onLayout
09-11 19:50:58.017 2781-2781/? D/MyTextView: text3 onLayout
09-11 19:50:58.017 2781-2781/? D/MyLinearLayout: null onLayout

MeasureSpecMap used in both examples above just contains the following Map:

public class MeasureSpecMap {

    private static final Map<Integer, String> MAP = new HashMap<>();

    static {
        MAP.put(View.MeasureSpec.AT_MOST, "AT_MOST");
        MAP.put(View.MeasureSpec.EXACTLY, "EXACTLY");
        MAP.put(View.MeasureSpec.UNSPECIFIED, "UNSPECIFIED");
    }

    private MeasureSpecMap() {

    }

    public static String getName(int mode) {
        return MAP.get(mode);
    }

}

Question 1.

When using MyRelativeLayout, why does the system need to call onMeasure on each child twice before onMeasure of MyRelativeLayout is called? With MyLinearLayout each child in my example is measured once as you can see in the log output above.

Question 2.

Here is something else from the Double taxation section that I don't understand:

when you use the RelativeLayout container, which allows you to position View objects with respect to the positions of other View objects, the framework performs the following actions:

Executes a layout-and-measure pass, during which the framework calculates each child object’s position and size, based on each child’s request. Uses this data, also taking object weights into account, to figure out the proper position of correlated views.

Uses this data, also taking object weights into account, to figure out the proper position of correlated views.

But wait... Aren't they talking about android:layout_weight which is a feature of LinearLayout?


The code from above is also available on GitHub:

The approach with MyLinearLayout

The approach with MyRelativeLayout

解决方案

Question 1.

When using MyRelativeLayout, why does the system need to call onMeasure on each child twice before onMeasure of MyRelativeLayout is called? With MyLinearLayout each child in my example is measured once as you can see in the log output above.

In (overly) simple terms, it comes down to the fact that the size and position of one view can affect the size and position of another view.

Consider text3: its width depends not only on the length of the text it is holding, but also on the width of text2; if text2 consumes 80% of the screen, then text3 will only get (at most) 20% of the screen.

So the system does a first measure pass to figure out the "constraints" that views are going to place on each other, and then does a second measure pass to figure out the final values to use.

Take a look at your log output (some text omitted for brevity):

D/MyTextView: onMeasure, text2 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
D/MyTextView: onMeasure, text3 widthMeasureSpec=AT_MOST heightMeasureSpec=AT_MOST
D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST

D/MyTextView: onMeasure, text1 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
D/MyTextView: onMeasure, text3 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST
D/MyTextView: onMeasure, text2 widthMeasureSpec=EXACTLY heightMeasureSpec=AT_MOST

See how the first time text2 is measured, its widthMeasureSpec is of mode AT_MOST while the second time it is of mode EXACTLY? The first pass is giving text2 the opportunity to consume up to 100% of the screen width, but the second pass is limiting it to its actual necessary size.

Whereas for a vertical LinearLayout, the system can get everything it needs to know in a single measurement pass. text1 is allowed up to the full window height, text2 is allowed up to the full window height minus text1's height, and so on.

Question 2.

...

But wait... Aren't they talking about android:layout_weight which is a feature of LinearLayout?

I don't understand this part either. I'm inclined to believe CommonsWare's speculation that it's just a documentation bug.

这篇关于在Android上使用RelativeLayout时的双重征税的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆