记LinearLayout原生的一个尺寸计算Bug

先写一下得出的结论:

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底部已经和蓝框底部齐平了,达到了预期的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值