Android 百分比布局库(percent-support-lib) 解析与扩展

Ò»¡¢¸ÅÊö

ÖÜÄ©ÓÎÏ·´òµÃ¹ýÃÍ£¬ÓÚÊÇÖÜÌì°¾Ò¹Âë´úÂ룬ÖÜÒ»ÔçÉÏ»ë»ëججµÄ·¢ÏÖandroid-percent-support-lib-sampleÕâ¸öÏîÄ¿£¬GoogleÖÕÓÚ¿ªÊ¼Ö§³Ö°Ù·Ö±ÈµÄ·½Ê½²¼¾ÖÁË£¬Ë²¼äÂö¶¯»ØÀ´£¬°¡ßÖßÖ¡£¶ÔÓÚÕâÖÖÀúÊ·ÐÔµÄʱ¿Ì£¬²»³öƪ²©¿ÍÄÑÒÔ±í´ïÎÒÄÚÐĵļ¤¶¯¡£

»¹¼ÇµÃ²»¾ÃÇ°£¬·¢ÁËƪ²©¿Í£ºAndroid ÆÁÄ»ÊÊÅä·½°¸£¬Õâƪ²©¿ÍÒÔWebÒ³ÃæÉè¼ÆÒý³öÒ»ÖÖÊÊÅä·½°¸£¬×îÖÕµÄÄ¿µÄ¾ÍÊÇ¿ÉÒÔͨ¹ý°Ù·Ö±È¿ØÖƿؼþµÄ´óС¡£µ±È»ÁË£¬´æÔÚһЩÎÊÌ⣬±ÈÈ磺

  • ¶ÔÓÚûÓп¼Âǵ½ÆÁÄ»³ß´ç£¬¿ÉÄÜ»á³öÏÖÒâÍâµÄÇé¿ö£»

  • apkµÄ´óС»áÔö¼Ó£»

µ±È»ÁËandroid-percent-supportÕâ¸ö¿â£¬»ù±¾¿ÉÒÔ½â¾öÉÏÊöÎÊÌ⣬ÊDz»ÊÇÓеãС¼¤¶¯£¬ÉԵȣ¬ÎÒÃÇÏÈÃèÊöÏÂÕâ¸ösupport-lib¡£

Õâ¸ö¿âÌṩÁË£º

  • Á½ÖÖ²¼¾Ö¹©´ó¼ÒʹÓãº
    PercentRelativeLayout¡¢PercentFrameLayout£¬Í¨¹ýÃû×־ͿÉÒÔ¿´³ö£¬ÕâÊǼ̳Ð×ÔFrameLayoutºÍRelativeLayoutÁ½¸öÈÝÆ÷Àࣻ

  • Ö§³ÖµÄÊôÐÔÓУº

layout_widthPercent¡¢layout_heightPercent¡¢ 

layout_marginPercent¡¢layout_marginLeftPercent¡¢ 

layout_marginTopPercent¡¢layout_marginRightPercent¡¢ 

layout_marginBottomPercent¡¢layout_marginStartPercent¡¢layout_marginEndPercent¡£

¿ÉÒÔ¿´µ½Ö§³Ö¿í¸ß£¬ÒÔ¼°margin¡£

Ò²¾ÍÊÇ˵£¬´ó¼ÒÖ»ÒªÔÚ¿ª·¢¹ý³ÌÖÐʹÓÃPercentRelativeLayout¡¢PercentFrameLayoutÌæ»»FrameLayout¡¢RelativeLayout¼´¿É¡£

ÊDz»ÊǺܼòµ¥£¬²»¹ýòËÆûÓÐLinearLayout£¬ÓÐÈË»á˵LinearLayoutÓÐweightÊôÐÔѽ¡£µ«ÊÇ£¬weightÊôÐÔÖ»ÄÜÖ§³ÖÒ»¸ö·½Ïòѽ~~¹þ£¬Ã»Ê£¬¸ÕºÃ¸øÎÒÃÇÒ»¸ö»ú»áÈ¥×Ô¶¨ÒåÒ»¸öPercentLinearLayout¡£

ºÃÁË£¬±¾ÎÄ·ÖΪ3¸ö²¿·Ö£º

  • PercentRelativeLayout¡¢PercentFrameLayoutµÄʹÓÃ

  • ¶ÔÉÏÊö¿Ø¼þÔ´Âë·ÖÎö

  • ×Ô¶¨ÒåPercentLinearLayout

¶þ¡¢Ê¹ÓÃ

¹ØÓÚʹÓã¬Æäʵ¼°Æä¼òµ¥£¬²¢ÇÒgithubÉÏÒ²ÓÐÀý×Ó£¬android-percent-support-lib-sample¡£ÎÒÃǾͼòµ¥¹ýһϣº

Ê×ÏȼǵÃÔÚbuild.gradleÌí¼Ó£º

compile 'com.android.support:percent:22.2.0'

£¨Ò»£©PercentFrameLayout

<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_gravity="left|top"
        android:background="#44ff0000"
        android:text="width:30%,height:20%"
        app:layout_heightPercent="20%"
        android:gravity="center"
        app:layout_widthPercent="30%"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_gravity="right|top"
        android:gravity="center"
        android:background="#4400ff00"
        android:text="width:70%,height:20%"
        app:layout_heightPercent="20%"
        app:layout_widthPercent="70%"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_gravity="bottom"
        android:background="#770000ff"
        android:text="width:100%,height:10%"
        android:gravity="center"
        app:layout_heightPercent="10%"
        app:layout_widthPercent="100%"/>
</android.support.percent.PercentFrameLayout>

3¸öTextView£¬ºÜ¼òµ¥£¬Ö±½Ó¿´Ð§¹ûͼ£º

1435714220966407.png

(¶þ) PercentRelativeLayout

<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true">
    <TextView
        android:id="@+id/row_one_item_one"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_alignParentTop="true"
        android:background="#7700ff00"
        android:text="w:70%,h:20%"
        android:gravity="center"
        app:layout_heightPercent="20%"
        app:layout_widthPercent="70%"/>
    <TextView
        android:id="@+id/row_one_item_two"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_toRightOf="@+id/row_one_item_one"
        android:background="#396190"
        android:text="w:30%,h:20%"
        app:layout_heightPercent="20%"
        android:gravity="center"
        app:layout_widthPercent="30%"/>
    <ImageView
        android:id="@+id/row_two_item_one"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:src="@drawable/tangyan"
        android:scaleType="centerCrop"
        android:layout_below="@+id/row_one_item_one"
        android:background="#d89695"
        app:layout_heightPercent="70%"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_below="@id/row_two_item_one"
        android:background="#770000ff"
        android:gravity="center"
        android:text="width:100%,height:10%"
        app:layout_heightPercent="10%"
        app:layout_widthPercent="100%"/>
</android.support.percent.PercentRelativeLayout>

ok£¬ÒÀÈ»ÊÇÖ±½Ó¿´Ð§¹ûͼ:

1435714331129749.png

ʹÓÃûʲôºÃ˵µÄ£¬¾ÍÊÇÖ±¹ÛµÄ¿´Ò»Ï¡£

Èý¡¢Ô´Âë·ÖÎö

ÆäʵϸÏëһϣ¬GoogleÖ»ÊǶÔÎÒÃÇÔ­±¾ÊìϤµÄRelativeLayoutºÍFrameLayout½øÐеŦÄܵÄÀ©Õ¹£¬Ê¹ÆäÖ§³ÖÁËpercentÏà¹ØµÄÊôÐÔ¡£

ÄÇô£¬ÎÒÃÇ¿¼ÂÇÏ£¬Èç¹ûÊÇÎÒÃÇÌí¼ÓÕâÖÖÀ©Õ¹£¬ÎÒÃÇ»áÔõô×ö£º

  • ͨ¹ýLayoutParams»ñÈ¡childÉèÖõÄpercentÏà¹ØÊôÐÔµÄÖµ

  • onMeasureµÄʱºò£¬½«childµÄwidth,heightµÄÖµ£¬Í¨¹ý»ñÈ¡µÄ×Ô¶¨ÒåÊôÐÔµÄÖµ½øÐмÆË㣨eg:ÈÝÆ÷µÄ¿í * fraction £©£¬¼ÆËãºó´«Èë¸øchild.measure(w,h);

ok£¬ÓÐÁËÉÏÃæµÄ²ÂÏ룬ÎÒÃÇÖ±½Ó¿´PercentFrameLayoutµÄÔ´Âë¡£

public class PercentFrameLayout extends FrameLayout {
    private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
    //Ê¡ÂÔÁË£¬Á½¸ö¹¹Ôì·½·¨
    public PercentFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mHelper.handleMeasuredStateTooSmall()) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mHelper.restoreOriginalParams();
    }
    public static class LayoutParams extends FrameLayout.LayoutParams
            implements PercentLayoutHelper.PercentLayoutParams {
        private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
        }
        //Ê¡ÂÔÁËһЩ´úÂë...
        @Override
        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
            return mPercentLayoutInfo;
        }
        @Override
        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
        }
    }
}

´úÂëÊÇÏ൱µÄ¶Ì£¬¿ÉÒÔ¿´µ½PercentFrameLayoutÀïÃæÊ×ÏÈÖØдÁËgenerateLayoutParams·½·¨£¬µ±È»ÁË£¬ÓÉÓÚÖ§³ÖÁËһЩеÄlayout_ÊôÐÔ£¬ÄÇô¿Ï¶¨ÐèÒª¶¨Òå¶ÔÓ¦µÄLayoutParams¡£

£¨Ò»£©percentÏà¹ØÊôÐԵĻñÈ¡

¿ÉÒÔ¿´µ½PercentFrameLayout.LayoutParamsÔÚÔ­ÓеÄFrameLayout.LayoutParams»ù´¡ÉÏ£¬ÊµÏÖÁËPercentLayoutHelper.PercentLayoutParams½Ó¿Ú¡£

Õâ¸ö½Ó¿ÚºÜ¼òµ¥£¬Ö»ÓÐÒ»¸ö·½·¨£º

public interface PercentLayoutParams {
   PercentLayoutInfo getPercentLayoutInfo();
}

¶ø£¬Õâ¸ö·½·¨µÄʵÏÖÄØ£¬Ò²Ö»ÓÐÒ»ÐУºreturn mPercentLayoutInfo;£¬ÄÇôÕâ¸ömPercentLayoutInfoÔÚÄÄÍê³É¸³ÖµÄØ£¿

¿´PercentFrameLayout.LayoutParamsµÄ¹¹Ôì·½·¨£º

public LayoutParams(Context c, AttributeSet attrs) {
    super(c, attrs);
    mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
}

¿ÉÒÔ¿´µ½£¬½«attrs´«Èë¸øgetPercentLayoutInfo·½·¨£¬ÄÇô²»ÓÃ˵£¬Õâ¸ö·½·¨µÄÄÚ²¿£¬¿Ï¶¨ÊÇ»ñÈ¡×Ô¶¨ÒåÊôÐÔµÄÖµ£¬È»ºó½«Æä·â×°µ½PercentLayoutInfo¶ÔÏóÖУ¬×îºó·µ»Ø¡£

´úÂëÈçÏ£º

public static PercentLayoutInfo getPercentLayoutInfo(Context context, AttributeSet attrs) {
    PercentLayoutInfo info = null;
    TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);
    float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1, -1f);
    if (value != -1f) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "percent width: " + value);
        }
        info = info != null ? info: new PercentLayoutInfo();
        info.widthPercent = value;
    }
    value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);
    if (value != -1f) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "percent height: " + value);
        }
        info = info != null ? info: new PercentLayoutInfo();
        info.heightPercent = value;
    }
    value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);
    if (value != -1f) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "percent margin: " + value);
        }
        info = info != null ? info: new PercentLayoutInfo();
        info.leftMarginPercent = value;
        info.topMarginPercent = value;
        info.rightMarginPercent = value;
        info.bottomMarginPercent = value;
    }
    value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1, -1f);
    if (value != -1f) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "percent left margin: " + value);
        }
        info = info != null ? info: new PercentLayoutInfo();
        info.leftMarginPercent = value;
    }
    value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1, -1f);
    if (value != -1f) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "percent top margin: " + value);
        }
        info = info != null ? info: new PercentLayoutInfo();
        info.topMarginPercent = value;
    }
    value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1, -1f);
    if (value != -1f) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "percent right margin: " + value);
        }
        info = info != null ? info: new PercentLayoutInfo();
        info.rightMarginPercent = value;
    }
    value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1, -1f);
    if (value != -1f) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "percent bottom margin: " + value);
        }
        info = info != null ? info: new PercentLayoutInfo();
        info.bottomMarginPercent = value;
    }
    value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1, -1f);
    if (value != -1f) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "percent start margin: " + value);
        }
        info = info != null ? info: new PercentLayoutInfo();
        info.startMarginPercent = value;
    }
    value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1, -1f);
    if (value != -1f) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "percent end margin: " + value);
        }
        info = info != null ? info: new PercentLayoutInfo();
        info.endMarginPercent = value;
    }
    array.recycle();
    if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "constructed: " + info);
    }
    return info;
}

ÊDz»ÊǺÍÎÒÃÇƽʱµÄÈ¡ÖµºÜÀàËÆ£¬ËùÓеÄÖµ×îÖÕ·â×°µ½PercentLayoutInfo¶ÔÏóÖС£

ok£¬µ½´ËÎÒÃǵÄÊôÐÔ»ñÈ¡¾Í½éÉÜÍê³É£¬ÓÐÁËÕâЩÊôÐÔ£¬ÊDz»ÊÇonMeasureÀïÃæÒª½øÐÐʹÓÃÄØ£¿

(¶þ) onMeasueÖÐÖØмÆËãchildµÄ³ß´ç

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if (mHelper.handleMeasuredStateTooSmall()) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

¿ÉÒÔ¿´µ½onMeasureÖеĴúÂëÒ³ºÜÉÙ£¬¿´À´ºËÐĵĴúÂ붼±»·â×°ÔÚmHelperµÄ·½·¨ÖУ¬ÎÒÃÇÖ±½Ó¿´mHelper.adjustChildren·½·¨¡£

/**
     * Iterates over children and changes their width and height to one calculated from percentage
     * values.
     * @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
     * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
     */
public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
    //...
    int widthHint = View.MeasureSpec.getSize(widthMeasureSpec);
    int heightHint = View.MeasureSpec.getSize(heightMeasureSpec);
    for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
        View view = mHost.getChildAt(i);
        ViewGroup.LayoutParams params = view.getLayoutParams();
        if (params instanceof PercentLayoutParams) {
            PercentLayoutInfo info = ((PercentLayoutParams) params).getPercentLayoutInfo();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "using " + info);
            }
            if (info != null) {
                if (params instanceof ViewGroup.MarginLayoutParams) {
                    info.fillMarginLayoutParams((ViewGroup.MarginLayoutParams) params, widthHint, heightHint);
                } else {
                    info.fillLayoutParams(params, widthHint, heightHint);
                }
            }
        }
    }
}

ͨ¹ý×¢ÊÍÒ²ÄÜ¿´³ö£¬´Ë·½·¨ÖбéÀúËùÓеĺ¢×Ó£¬Í¨¹ý°Ù·Ö±ÈµÄÊôÐÔÖØÐÂÉèÖÃÆä¿í¶ÈºÍ¸ß¶È¡£

Ê×ÏÈÔÚwidthHint¡¢heightHint±£´æÈÝÆ÷µÄ¿í¡¢¸ß£¬È»ºó±éÀúËùÓеĺ¢×Ó£¬ÅжÏÆäLayoutParamsÊÇ·ñÊÇPercentLayoutParamsÀàÐÍ£¬Èç¹ûÊÇ£¬Í¨¹ýparams.getPercentLayoutInfoÄóöinfo¶ÔÏó¡£

ÊÇ·ñ»¹¼ÇµÃ£¬ÉÏÃæµÄ·ÖÎöÖУ¬PercentLayoutInfo±£´æÁËpercentÏà¹ØÊôÐÔµÄÖµ¡£

Èç¹ûinfo²»Îªnull£¬ÔòÅжÏÊÇ·ñÐèÒª´¦Àímargin£»ÎÒÃÇÖ±½Ó¿´fillLayoutParams·½·¨£¨´¦ÀímarginÒ²ÊÇÀàËƵģ©¡£

/**
         * Fills {@code ViewGroup.LayoutParams} dimensions based on percentage values.
         */
public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint, int heightHint) {
    // Preserve the original layout params, so we can restore them after the measure step.
    mPreservedParams.width = params.width;
    mPreservedParams.height = params.height;
    if (widthPercent >= 0) {
        params.width = (int)(widthHint * widthPercent);
    }
    if (heightPercent >= 0) {
        params.height = (int)(heightHint * heightPercent);
    }
    if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
    }
}

Ê×Ïȱ£´æÔ­±¾µÄwidthºÍheight£¬È»ºóÖØÖÃparamsµÄwidthºÍheightΪ(int) (widthHint * widthPercent)ºÍ(int) (heightHint * heightPercent);¡£

µ½´Ë£¬ÆäʵÎÒÃǵİٷֱÈת»»¾Í½áÊøÁË£¬ÀíÂÛÉϾÍÒѾ­ÊµÏÖÁ˶ÔÓڰٷֱȵÄÖ§³Ö£¬²»¹ýGoogle»¹¿¼ÂÇÁËһЩϸ½Ú¡£

ÎÒÃǻص½onMeasure·½·¨£º

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if (mHelper.handleMeasuredStateTooSmall()) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

ÏÂÃ滹ÓиömHelper.handleMeasuredStateTooSmallµÄÅжϣ¬Ò²¾ÍÊÇ˵£¬Èç¹ûÄãÉèÖõİٷֱȣ¬×îÖÕ¼ÆËã³öÀ´µÄMeasuredSize¹ýСµÄ»°£¬»á½øÐÐһЩ²Ù×÷¡£ 
´úÂëÈçÏ£º

public boolean handleMeasuredStateTooSmall() {
    boolean needsSecondMeasure = false;
    for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
        View view = mHost.getChildAt(i);
        ViewGroup.LayoutParams params = view.getLayoutParams();
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "should handle measured state too small " + view + " " + params);
        }
        if (params instanceof PercentLayoutParams) {
            PercentLayoutInfo info = ((PercentLayoutParams) params).getPercentLayoutInfo();
            if (info != null) {
                if (shouldHandleMeasuredWidthTooSmall(view, info)) {
                    needsSecondMeasure = true;
                    params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
                }
                if (shouldHandleMeasuredHeightTooSmall(view, info)) {
                    needsSecondMeasure = true;
                    params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
                }
            }
        }
    }
    if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
    }
    return needsSecondMeasure;
}

Ê×ÏȱéÀúËùÓеĺ¢×Ó£¬Äóöº¢×ÓµÄlayoutparams£¬Èç¹ûÊÇPercentLayoutParamsʵÀý£¬ÔòÈ¡³öinfo¡£Èç¹ûinfo²»Îªnull£¬µ÷ÓÃshouldHandleMeasuredWidthTooSmallÅжϣº

private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {
    int state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK;
    return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 && info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
}

ÕâÀï¾ÍÊÇÅжϣ¬Èç¹ûÄãÉèÖõÄmeasuredWidth»òÕßmeasureHeight¹ýСµÄ»°£¬²¢ÇÒÄãÔÚ²¼¾ÖÎļþÖÐlayout_w/h ÉèÖõÄÊÇWRAP_CONTENTµÄ»°£¬½«params.width / height= ViewGroup.LayoutParams.WRAP_CONTENT£¬È»ºóÖØвâÁ¿¡£

¹þ£¬onMeasureÖÕÓÚ½áÊøÁË~~~ÏÖÔÚÎÒ¾õµÃÓ¦¸Ã´úÂë½áÊøÁË°É£¬³ß´ç¶¼ÉèÖúÃÁË£¬»¹ÐèÒª¸ÉÂïô£¬but£¬Äã»á·¢ÏÖonLayoutÒ²ÖØдÁË£¬ÎÒÃÇÓÖ²»¸Ä±älayout¹æÔò£¬ÔÚonLayoutÀïÃæ¸ÉʲôëÏߣº

@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    mHelper.restoreOriginalParams();
}

¼ÌÐø¿´mHelper.restoreOriginalParams

/**
     * Iterates over children and restores their original dimensions that were changed for
     * percentage values. Calling this method only makes sense if you previously called
     * {@link PercentLayoutHelper#adjustChildren(int, int)}.
     */
public void restoreOriginalParams() {
    for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
        View view = mHost.getChildAt(i);
        ViewGroup.LayoutParams params = view.getLayoutParams();
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "should restore " + view + " " + params);
        }
        if (params instanceof PercentLayoutParams) {
            PercentLayoutInfo info = ((PercentLayoutParams) params).getPercentLayoutInfo();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "using " + info);
            }
            if (info != null) {
                if (params instanceof ViewGroup.MarginLayoutParams) {
                    info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);
                } else {
                    info.restoreLayoutParams(params);
                }
            }
        }
    }
}

àÛ£¬Ô­À´ÊÇÖØлָ´Ô­±¾µÄ³ß´çÖµ£¬Ò²¾ÍÊÇ˵onMeasureÀïÃæµÄ¶ÔÖµ½øÐÐÁ˸ı䣬²âÁ¿Íê³Éºó¡£ÔÚÕâ¸öµØ·½£¬½«ÖµÓÖ»Ö¸´³ÉÈç¹û²¼¾ÖÎļþÖеÄÖµ£¬ÉÏÃæдµÄ¶¼ÊÇ0¡£»Ö¸´ºÜ¼òµ¥£º

public void restoreLayoutParams(ViewGroup.LayoutParams params) {
    params.width = mPreservedParams.width;
    params.height = mPreservedParams.height;
}

ÄãÓ¦¸ÃûÓÐÍüÔÚÄÄ´æµÄ°Ñ~ÍüÁ˵Ļ°£¬Âé·³Ctrl+F ¡®mPreservedParams.width¡¯ ¡£

Ò²¾ÍÊÇ˵£¬ÄãÈ¥´òÓ¡ÉÏÃæд·¨£¬²¼¾ÖÎļþÖÐviewµÄv.getLayoutParams().width£¬Õâ¸öÖµÓ¦¸ÃÊÇ0¡£

ÕâÀï¸Ð¾õÂÔ΢²»Ë¬~Õâ¸ö0ûÈöÓô¦Ñ½£¬»¹²»Èç²»ÖØÖÃ~~

ºÃÁË£¬µ½´Ë¾Í·ÖÎöÍêÁË£¬ÆäʵÖ÷Òª¾Í¼¸¸ö²½Ö裺

  • LayoutParamsÖÐÊôÐԵĻñÈ¡

  • onMeasureÖУ¬¸Ä±äparams.widthΪ°Ù·Ö±È¼ÆËã½á¹û£¬²âÁ¿

  • Èç¹û²âÁ¿Öµ¹ýСÇÒÉèÖõÄw/hÊÇwrap_content£¬ÖØвâÁ¿

  • onLayoutÖУ¬ÖØÖÃparams.w/hΪ²¼¾ÖÎļþÖбàдµÄÖµ

¿ÉÒÔ¿´µ½£¬ÓÐÁËRelativeLayout¡¢FrameLayoutµÄÀ©Õ¹£¬¾¹È»Ã»ÓÐLinearLayout¼¸¸öÒâ˼¡£ºÃÔÚ£¬ÎÒÃǵĺËÐÄ´úÂ붼ÓÉPercentLayoutHelper·â×°ÁË£¬×Ô¼ºÀ©Õ¹ÏÂLinearLayoutÒ²²»¸´ÔÓ¡£

Èý¡¢ÊµÏÖPercentLinearlayout

¿ÉÄÜÓÐÈË»á˵£¬ÓÐÁËweightѽ£¬µ«ÊÇweightÄÜ×öµ½¿í¡¢¸ßͬʱ°Ù·Ö±È¸³ÖµÂ

ºÃÁË£¬´úÂëºÜ¼òµ¥£¬ÈçÏ£º

£¨Ò»£©PercentLinearLayout

package com.juliengenoud.percentsamples;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.percent.PercentLayoutHelper;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.LinearLayout;
/**
 * Created by zhy on 15/6/30.
 */
public class PercentLinearLayout extends LinearLayout
{
    private PercentLayoutHelper mPercentLayoutHelper;
    public PercentLinearLayout(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        mPercentLayoutHelper = new PercentLayoutHelper(this);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        mPercentLayoutHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mPercentLayoutHelper.handleMeasuredStateTooSmall())
        {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        super.onLayout(changed, l, t, r, b);
        mPercentLayoutHelper.restoreOriginalParams();
    }
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs)
    {
        return new LayoutParams(getContext(), attrs);
    }
    public static class LayoutParams extends LinearLayout.LayoutParams
            implements PercentLayoutHelper.PercentLayoutParams
    {
        private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
        public LayoutParams(Context c, AttributeSet attrs)
        {
            super(c, attrs);
            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
        }
        @Override
        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo()
        {
            return mPercentLayoutInfo;
        }
        @Override
        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)
        {
            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
        }
        public LayoutParams(int width, int height) {
            super(width, height);
        }
        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }
        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }
    }
}

Èç¹ûÄãÏêϸ¿´ÁËÉÏÃæµÄÔ´Âë·ÖÎö£¬Õâ¸ö´úÂëÊDz»ÊÇûÈö½âÊ͵ÄÁË~

£¨¶þ£©²âÊÔ²¼¾Ö

<?xml version="1.0" encoding="utf-8"?>
<com.juliengenoud.percentsamples.PercentLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff44aacc"
        android:text="width:60%,height:5%"
        android:textColor="#ffffff"
        app:layout_heightPercent="5%"
        app:layout_marginBottomPercent="5%"
        app:layout_widthPercent="60%"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff4400cc"
        android:gravity="center"
        android:textColor="#ffffff"
        android:text="width:70%,height:10%"
        app:layout_heightPercent="10%"
        app:layout_marginBottomPercent="5%"
        app:layout_widthPercent="70%"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff44aacc"
        android:gravity="center"
        android:text="width:80%,height:15%"
        android:textColor="#ffffff"
        app:layout_heightPercent="15%"
        app:layout_marginBottomPercent="5%"
        app:layout_widthPercent="80%"/>
    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#ff4400cc"
        android:gravity="center"
        android:text="width:90%,height:5%"
        android:textColor="#ffffff"
        app:layout_heightPercent="20%"
        app:layout_marginBottomPercent="10%"
        app:layout_widthPercent="90%"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="#ff44aacc"
        android:gravity="center"
        android:text="width:100%,height:25%"
        android:textColor="#ffffff"
        app:layout_heightPercent="25%"
        app:layout_marginBottomPercent="5%"
        />
</com.juliengenoud.percentsamples.PercentLinearLayout>

ÎÒÃÇ×ÝÏòÅÅÁеļ¸¸öTextView£¬·Ö±ðÉèÖÿí/¸ß¶¼Îª°Ù·Ö±È£¬ÇÒÖ®¼äµÄ¼ä¸ôΪ5%p¡£

(Èý)Ч¹ûͼ

1435714998245711.png

ok£¬µ½´Ë£¬ÎÒÃÇʹÓá¢Ô´Âë·ÖÎö¡¢À©Õ¹PercentLinearLayout¾Í½áÊøÁË¡£

Ìí¼ÓPercentLinearLayoutºóµÄµØÖ·£ºµã»÷²é¿´

~~have a nice day ~~