在Android上使用RelativeLayout时的双重征税 [英] Double Taxation when using a RelativeLayout on Android
问题描述
为了了解双重征税在Android上,我编写了以下非常简单的代码.有一个带有三个TextView
的RelativeLayout
.
<?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
替换为名为MyLinearLayout
的LinearLayoiut
子级:
<?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
时,为什么在调用MyRelativeLayout
的onMeasure
之前,系统需要在每个孩子上两次调用onMeasure
?在您的示例中,对于MyLinearLayout
,每个孩子都会被测量一次,如您在上面的日志输出中所见.
问题2.
以下是双重征税"部分中的其他内容我不明白:
使用RelativeLayout容器时,您可以使用 将View对象相对于其他View的位置定位 对象,该框架执行以下操作:
执行布局和度量传递,在此期间框架 根据每个子对象的位置,计算每个子对象的位置和大小 孩子的要求.使用此数据,也将对象权重计入 帐户,以找出相关视图的正确位置.
使用此数据,同时考虑对象权重,以找出 相关视图的正确位置.
但是等等...他们不是在谈论android:layout_weight
这是LinearLayout
的功能吗?
上面的代码也可以在GitHub上获得:
的方法 的方法
问题1.
使用
MyRelativeLayout
时,为什么在调用MyRelativeLayout
的onMeasure
之前,系统需要在每个孩子上两次调用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 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
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 callonMeasure
on each child twice beforeonMeasure
ofMyRelativeLayout
is called? WithMyLinearLayout
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 ofLinearLayout
?
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屋!