LinearLayout——线性布局(下)

来源:互联网 发布:ubuntu 安装 kde桌面 编辑:程序博客网 时间:2024/05/24 06:12

本文部分内容参考:http://blog.csdn.net/kkkvvv123/article/details/9029381

LinearLayout中的weight属性(在子View下设置)是非常重要的一个属性,他可以做到n等分一行的空间,使得子View可以均匀的分布在布局空间内。也可以按照要求按比例分配空间。这在多分辨率适配中是个很有力的武器。weightSum是配合weight使用的。下面先来分析LinearLayout的源码了解weight实现的原理。

源码片段:

        /*         *delta         额外空间         *widthSize     LinearLayout的宽度         *mTotalLength  所有子View的宽度的和(还没有考虑layout_weight)         *totalWeight: 所有子View的layout_weight的和         *mWeihtSUm:  LinearLayout的android:weightSum属性         */                        //计算额外空间        int delta = widthSize - mTotalLength;        //必须有额外空间存在且有weight设置        if (delta != 0 && totalWeight > 0.0f) {            //如果LinearLayout的android:weightSum设置则weightSum等于设定值            //否则为totalWeight            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;            //将根Gravity有关的偏移初始化            maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;            maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;            maxHeight = -1;            mTotalLength = 0;                        //count为子View的个数            for (int i = 0; i < count; ++i) {                final View child = getVirtualChildAt(i);                                //如果为null或View.GONE则跳过                if (child == null || child.getVisibility() == View.GONE) {                    continue;                }                                final LinearLayout.LayoutParams lp =                        (LinearLayout.LayoutParams) child.getLayoutParams();                //可见weight属性应为float                float childExtra = lp.weight;                if (childExtra > 0) {                    //当前View占额外空间的比例, 注意这个是取整的!                    //delta为负数的话这里的share也为负数                    int share = (int) (childExtra * delta / weightSum);                    //分配完成后,在总量里面减去已分配部分                    weightSum -= childExtra;                    delta -= share;                    final int childHeightMeasureSpec = getChildMeasureSpec(                            heightMeasureSpec,                            mPaddingTop + mPaddingBottom                             + lp.topMargin + lp.bottomMargin,                            lp.height);                    // TODO: Use a field like lp.isMeasured to figure out if this                    // child has been previously measured                                        // MeasureSpec.EXACTLY代表之前已经给定确定的size                    //之后即使在改变size至少也要比当前给定的大                    if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {                        // child was measured once already above ...                         //base new measurement                        // on stored values                        //如果结果为负数则Width等于0                        int childWidth = child.getMeasuredWidth() + share;                        if (childWidth < 0) {                            childWidth = 0;                        }                        //将当前View按新的Width去measure                        child.measure(                            MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY)                            , childHeightMeasureSpec);                    } else {                        // child was skipped in the loop above.                         //Measure for this first time here                        child.measure(MeasureSpec.makeMeasureSpec(                                share > 0 ? share : 0, MeasureSpec.EXACTLY),                                childHeightMeasureSpec);                    }                }

源码中关键部分为

//计算额外空间(当赋给width的值大于LinearLayout的width时可能为负数)        int delta = widthSize - mTotalLength;
 int share = (int) (childExtra * delta / weightSum);                    //分配完成后,在总量里面减去已分配部分                    weightSum -= childExtra;                    delta -= share;

int childWidth = child.getMeasuredWidth() + share;                        if (childWidth < 0) {                            childWidth = 0;                        }

SDK中对LinearLayout的介绍http://developer.android.com/guide/topics/ui/layout/linear.html中有一块对weight平分的实现进行了说明:

Equally weighted children

To create a linear layout in which each child uses the same amount of space on the screen, set the android:layout_height of each view to "0dp" (for a vertical layout) or theandroid:layout_width of each view to "0dp" (for a horizontal layout). Then set theandroid:layout_weight of each view to "1".

通过sdk的说明也可以知道,weight是对剩余空间的分配而不是对LinearLayout空间的分配!千万不要想当然……下面举几个例子,看看weight和weightSum到底怎么使用。

例1:weightSum为默认值,子View的width=match_parent,weight分别为: 1  1  2

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal" >    <Button         android:layout_weight="1"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="Button 1"/>    <Button         android:layout_weight="1"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="Button 2"/>    <Button         android:layout_weight="2"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="Button 3"/>    </LinearLayout>

效果图:


第三个button为什么不见了呢?下面来看一些计算就能明白了。

设:屏幕最大宽度为PM

delta = PM - 3 * PM = -2PM

mWeightSum = 1.0F + 1.0F + 2.0F = 4.0F

第一个按钮:

float childExtra = lp.weight = 1.0F

share = (int) (childExtra* delta / mWeightSum) =(int)( 1.0F * (-2PM) / 4.0F ) = -0.5PM 

childWidth = PM + share = 0.5PM //第一个按钮的宽是屏幕最大宽度的一半

weightSum -= childExtra(weightSum= 3)

delta -= share ( delta = -1.5P)

第二个按钮:

float childExtra = lp.weight = 1.0F

share = (int) (childExtra* delta / mWeightSum) =(int)( 1.0F * (-1.5PM) / 3.0F ) = -0.5PM 

childWidth = PM + share = 0.5PM //第二个按钮的宽也是屏幕最大宽度的一半

weightSum -= childExtra(weightSum= 2)

delta -= share ( delta = -P)

第三个按钮:

float childExtra = lp.weight = 1.0F

share = (int) (childExtra* delta / mWeightSum) =(int)( 2.0F * PM / 2.0F ) = -PM 

childWidth = PM + share = 0 //第三个按钮的宽是0

例2:如果weight分别为: 1  1  3,代码省略直接看效果


上图可以看到Button2右边不见了!仿照例1的计算可以得出Button1的width=3/5PM,Button2的width=3/5PM!他们的和大于PM所以右边会不见啦。

其实可以根据源码里的公式列出一个不等式组然后解出一个关系式:


k是子View的个数,wj是某一个子View的weight,不等式右边是所有weight的和。

当不等式左边等于右边,那么会出现例1的情况

当不等式左边大于右边,那么会出现例2的情况

当不等式左边小右边,那么会保证所有子View在LinearLayout里


接着说weightSum,这个属性是手动设所有定子View的weight和。如果这个weightSum的值小于所有子View的weight的和,那么会出现类似上面说过的2个问题。所以一般weightSum会比所有weight的和大,这样能实现类似缩放的功能,当然只是缩小没有放大!有源码在,分析应该没问题了,具体效果还是大家自己去尝试下吧!