在Android

时间:2017-09-11 20:53:28

标签: android android-layout layout android-linearlayout android-relativelayout

为了理解Android上的Double Taxation,我编写了以下代码,非常简单。有一个RelativeLayout有三个TextView s。

<?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替换为LinearLayoiut名为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");
    }
}

以下是我现在在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时,为什么系统需要在调用onMeasure onMeasure之前两次为每个孩子调用MyRelativeLayout?使用MyLinearLayout我的示例中的每个子项都会被测量一次,如上面的日志输出中所示。

问题2。

以下是我不明白的the Double taxation section中的其他内容:

  

当您使用RelativeLayout容器时,允许您使用   position相对于其他View的位置查看对象   对象,框架执行以下操作:

     

执行布局和度量过程,在此过程中使用框架   根据每个子对象计算每个子对象的位置和大小   孩子的要求。使用此数据,也将对象权重纳入其中   帐户,以确定相关视图的正确位置。

     

使用此数据,同时考虑对象权重,以找出答案   相关观点的正确位置。

但是等等......他们不是在谈论android:layout_weight这是LinearLayout的一个特征吗?

上面的代码也可以在GitHub上找到:

The approach with MyLinearLayout

The approach with MyRelativeLayout

1 个答案:

答案 0 :(得分:1)

  

问题1。

     

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

在(过于)简单的术语中,它归结为一个视图的大小和位置可以影响另一个视图的大小和位置。

考虑text3:它的宽度不仅取决于它所持有的文本的长度,还取决于text2的宽度;如果text2消耗了80%的屏幕,那么text3只能获得(最多)20%的屏幕。

因此,系统会进行第一次测量,以确定&#34;约束&#34;视图将相互放置,然后通过第二个度量来确定要使用的最终值。

查看您的日志输出(为简洁起见省略了一些文字):

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的猜测,它只是一个文档错误。