先写一下得出的结论:
vertical 布局的LinearLayout,如果其中某个 child的 height + marginTop + marginBottom值为负值,就可能出现LinearLayout最下方多出一块空白区域的问题,应该是原生代码的一个设计缺陷吧。
代码如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final LinearLayout parent = this.findViewById(R.id.parent);
//add 1
RelativeLayout itemLayout = (RelativeLayout) LayoutInflater.from(MainActivity.this).inflate(R.layout.item, parent, false);
itemLayout.setBackgroundColor(0xffff0000);
parent.addView(itemLayout);
//add 2
RelativeLayout itemLayout2 = (RelativeLayout) LayoutInflater.from(MainActivity.this).inflate(R.layout.item2, parent, false);
itemLayout2.setBackgroundColor(0xff00ff00);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) itemLayout2.getLayoutParams();
//top_margin是 -100dp
int topMargin = this.getResources().getDimensionPixelSize(R.dimen.top_margin);
lp.setMargins(0, topMargin, 0, 0);
parent.addView(itemLayout2);
//add 3
RelativeLayout itemLayout3 = (RelativeLayout) LayoutInflater.from(MainActivity.this).inflate(R.layout.item, parent, false);
itemLayout3.setBackgroundColor(0xff0000ff);
parent.addView(itemLayout3);
}
activity_main.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:id="@+id/parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff000000">
</LinearLayout>
</RelativeLayout>
item.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#ffff0000"
tools:context=".MainActivity">
</RelativeLayout>
item2.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="200dp"
android:layout_height="50dp"
android:background="#ffff0000"
tools:context=".MainActivity">
</RelativeLayout>
代码实际上很简单,LinearLayout作为父控件,add了3个子控件itemLayout1、itemLayout2、itemLayout3,但是最终的效果是这样的:
LinearLayout背景是黑色的,想象中LinearLayout底部应该和蓝色方块底边齐平的,实际上看到LinearLayout最下面是有部分空白区域的。
然后看了下LinearLayout的onMearsure方法,在累加计算总高度时是这样算的:
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
getNextLocationOffset的值是0,可以忽略
int getNextLocationOffset(View child) {
return 0;
}
totalLength是累加child高度前的总高度,如果totalLength + childHeight + lp.topMargin +lp.bottomMargin小于totalLength,还是取原totalLength的,也就是说如果childHeight + lp.topMargin +lp.bottomMargin<0, 就会出现总高度计算多出来一块的现象,不知道代码是故意这样写的还是一个Bug.
为了改这个问题我们复写LinearLayout,重新定义一个MyLinearLayout:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measuredHeight = getMeasuredHeight();
int childrenCount = getChildCount();
int reduceTotal = 0;
for (int i=0; i<childrenCount; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int childHeightSpace = lp.topMargin + lp.bottomMargin + child.getMeasuredHeight();
if (childHeightSpace <0) {
reduceTotal += childHeightSpace;
}
}
measuredHeight += reduceTotal;
int heightSizeAndState = resolveSizeAndState(measuredHeight, heightMeasureSpec, 0);
setMeasuredDimension(widthMeasureSpec, heightSizeAndState);
}
然后在activity_main.xml布局中使用MyLinearLayout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.hmct.myapplication.MyLinearLayout
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:id="@+id/parent"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff000000">
</com.example.hmct.myapplication.MyLinearLayout>
</RelativeLayout>
显示效果如下:
能看到LinearLayout底部已经和蓝框底部齐平了,达到了预期的效果。