为了理解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上找到:
答案 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的猜测,它只是一个文档错误。