ÎÄÕ³ö´¦£ºhttp://www.jianshu.com/p/05bc040841cd
com.android.support:percent:22.2.0 :google ³öÁ˸ö°üÀ´Ö§³Ö°´¸¸¿Ø¼þ°Ù·Ö±ÈµÄÏÔʾ.
ÎÒÃÇGradle µ¼Èë
compile 'com.android.support:percent:22.2.0'
¿´Ò»ÏÂ,ÆäʵÕâ¸ö¿âÌØ±ð¼òµ¥Ö»ÓÐ3¸ö class Àà
declare-styleable ÓÐ:
ÓÐ PrecentFrameLayout ºÍ PrecentRelativeLayout
´ÓÕâ¸öÃû³ÆÉÏÃæÎÒÃÇ¿ÉÒÔºÜÇå³þµÄÖªµÀPrecentFrameLayout ¼Ì³Ð FrameLayout PrecentRelativeLayout ¼Ì³Ð RelativeLayout µÄ.
ÆäʵÄã¿ÉÒÔ¿´³öPrecentFrameLayout ºÍPrecentRelativeLayout ÖÐʵÏÖЧ¹ûÀàËÆ,ÓÉ´Ë¿ÉÒÔ¿´³öÁ½¸öÀàÓÐ×ÅÌ«¶àÏàËÆµÄ´úÂë,ËùÒÔ½«²Ù×÷·ÅÔÚHelperÖÐ,²»½öʹÂß¼¸üÇåÎú,ͬʱ¿ÉÒÔ½øÐÐͳһµÄά»¤,Ò»´ÎÐÞ¸Ä.Á½±ßÉúЧ.
ÎÒÃDz鿴PercentRelativeLayout Õâ¸öÀà.ÎÒÃÇÖ±½ÓʹÓà as µÄ ctrl + Êó±ê×ó¼ü,Ö±½Ó½øÈëÔ´ÂëÈ¥²é¿´ (as Õæ°ô)
public class PercentRelativeLayout extends RelativeLayout { private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this); public PercentRelativeLayout(Context context) { super(context); } public PercentRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public PercentRelativeLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { return new PercentRelativeLayout.LayoutParams(this.getContext(), attrs); } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //ÐÞ¸ÄchildView µÄ LayoutParams this.mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); if(this.mHelper.handleMeasuredStateTooSmall()) { //that indicates the measured size is smaller that the space the view would like to have. super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //»¹ÔÉÏÒ»¸ö ×ÓÀàµÄµÄ LayoutParams? Õâ±ßÎÒÓиöÎÊÌâ?»áÔÚÏÂÃæÌá³ö. this.mHelper.restoreOriginalParams(); } public static class LayoutParams extends android.widget.RelativeLayout.LayoutParams implements PercentLayoutParams { private PercentLayoutInfo mPercentLayoutInfo; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); //Ö÷ÒªÊÇ»ñÈ¡ÎÒÃÇ֮ǰÔÚ xml ÉèÖõıÈÀý this.mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs); } public LayoutParams(int width, int height) { super(width, height); } public LayoutParams(android.view.ViewGroup.LayoutParams source) { super(source); } public LayoutParams(MarginLayoutParams source) { super(source); } public PercentLayoutInfo getPercentLayoutInfo() { //ʵÏÖ½Ó¿Ú·µ»ØmPercentLayoutInfo return this.mPercentLayoutInfo; } protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr); } } }
¸´Ð´ÁËgenerateLayoutParams ·½·¨,Õâ¸ö·½·¨ÏµÍ³×Ô¶¯µ÷ÓÃ.ÔÚÌí¼Ó childView µÄµ÷ÓÃ.ͬʱ
Õâ¸ö·½·¨ÊÇ¿ÉÒÔ»ñÈ¡µ½×ÓÀàµÄAttributeSet, ¶øÎÒÃÇÉèÖõıÈÀý¾ÍÔÚAttributeSetÖÐ.
ÎÒÃÇ¿ÉÒÔ¿´µ½generateLayoutParams ·½·¨·µ»ØÁËÒ»¸öеÄLayoutParams ,Õâ¸öLayoutParams ¼Ì³ÐÁËandroid.widget.RelativeLayout.LayoutParams ʵÏÖÁËPercentLayoutParamsÕâ¸ö½Ó¿Ú.
ΪÁ˶à̬,Ëü½«ÎÒÃÇÉèÖõÄÌõ¼þ·ÅÔÚmPercentLayoutInfoÕâ¸öÀàÖÐ,ͨ¹ýÒ»¸ö½Ó¿Ú·µ»Ø¸øÎÒÃÇ.ÒòΪÎÒÃÇÓжà¸ö²»Í¬ÀàÐ͵ĵÄLayoutParams,ͨ¹ýÕâ¸ö½Ó¿Ú¿ÉÒÔʹÓöà̬,ͬʱÓÐÀûÓÚÎÒÃÇ»ùÓÚ Help ½øÐÐÀ©Õ¹.
Õâ¸ö·½·¨Óе㳤.²»Ì«¿ÉÄܰÑËùÓеĴúÂëÌù³öÀ´,ÎÒÌùÖØÒªµÄ.
public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) { if(Log.isLoggable("PercentLayout", 3)) { Log.d("PercentLayout", "adjustChildren: " + this.mHost + " widthMeasureSpec: " + MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: " + MeasureSpec.toString(heightMeasureSpec)); } //»ñÈ¡×ÔÉíµÄ¿í¶ÈµÄ´óС int widthHint = MeasureSpec.getSize(widthMeasureSpec); //»ñÈ¡×ÔÉí¸ß¶ÈµÄ´óС int heightHint = MeasureSpec.getSize(heightMeasureSpec); int i = 0; for(int N = this.mHost.getChildCount(); i < N; ++i) { View view = this.mHost.getChildAt(i); LayoutParams params = view.getLayoutParams(); if(Log.isLoggable("PercentLayout", 3)) { Log.d("PercentLayout", "should adjust " + view + " " + params); } if(params instanceof PercentLayoutHelper.PercentLayoutParams) { //»ñÈ¡µ½PercentLayoutInfo ÎÒÃÇÉèÖõıÈÀý¾ÍÔÚÀïÃæ. PercentLayoutHelper.PercentLayoutInfo info = ((PercentLayoutHelper.PercentLayoutParams)params).getPercentLayoutInfo(); if(Log.isLoggable("PercentLayout", 3)) { Log.d("PercentLayout", "using " + info); } if(info != null) { //Õâ±äΪʲôҪÅжÏLayoutParam µÄÀàÐÍ? //·½·¨ÄÜ×ßµ½ÕâÀïµÄʱºòÒѾ˵Ã÷ÁËinfo != null ,¾ÍÊÇ˵ params Õâ¸öʵÏÖÁËPercentLayoutParamsÕâ¸ö½Ó¿Ú,¶øÎÒÃÇµÄ PercentLayoutParams µÄʵÏÖÀà¶¼ÊÇ MarginLayoutParams µÄ×ÓÀà.ÄÇÎÒÃÇΪʲô»¹Òª×öÕâ¸öÅжÏÄØ?ΪÁËÀ©Õ¹.Èç¹ûÄãµÄҪʵÏֵIJ»ÊÇ»ù±¾²¼¾ÖÀàÐÍ,¶øÊÇ ViewGrop,ËüµÄ layoutParam ÊÇViewGroup.LayoutParams,¶øÄãҪʵÏÖÕâ¸öÕâ¸öЧ¹û.ÄÇôÄãÈç¹ûÖ±½ÓǿתMarginLayoutParams ÊÇ»á±ÀµôµÄ. if(params instanceof MarginLayoutParams) { info.fillMarginLayoutParams((MarginLayoutParams)params, widthHint, heightHint); } else { info.fillLayoutParams(params, widthHint, heightHint); } } } } }
ͨ¹ýÐÞ¸ÄfillMarginLayoutParams ºÍ fillLayoutParams ÐÞ¸ÄchildView µÄ LayoutParam µÄ¿í¸ß,±ß¾à,²¢½«ÐÞ¸ÄǰµÄÖµ±£´æÔÚPercentLayoutInfo µÄmPreservedParamsÖÐ.
public boolean handleMeasuredStateTooSmall() { boolean needsSecondMeasure = false; int i = 0; for(int N = this.mHost.getChildCount(); i < N; ++i) { View view = this.mHost.getChildAt(i); LayoutParams params = view.getLayoutParams(); if(Log.isLoggable("PercentLayout", 3)) { Log.d("PercentLayout", "should handle measured state too small " + view + " " + params); } if(params instanceof PercentLayoutHelper.PercentLayoutParams) { PercentLayoutHelper.PercentLayoutInfo info = ((PercentLayoutHelper.PercentLayoutParams)params).getPercentLayoutInfo(); if(info != null) { if(shouldHandleMeasuredWidthTooSmall(view, info)) { needsSecondMeasure = true; params.width = -2; } if(shouldHandleMeasuredHeightTooSmall(view, info)) { needsSecondMeasure = true; params.height = -2; } } } } if(Log.isLoggable("PercentLayout", 3)) { Log.d("PercentLayout", "should trigger second measure pass: " + needsSecondMeasure); } return needsSecondMeasure; } 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.percent >= 0 && info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT; } private static boolean shouldHandleMeasuredHeightTooSmall(View view, PercentLayoutInfo info) { int state = ViewCompat.getMeasuredHeightAndState(view) & ViewCompat.MEASURED_STATE_MASK; return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.heightPercent.percent >= 0 && info.mPreservedParams.height == ViewGroup.LayoutParams.WRAP_CONTENT; }
Õâ¸ö·½·¨ÊǼì²é childView ´óС¼ÆËãÊÇ·ñ²»ºÏÀí.
Õâ±ßViewCompat.getMeasuredWidthAndState(view)·µ»ØÖµÓÐ3¸ö
ÎÒµÄÒÉ»ó:
ÎÒµÄÎÊÌâÊÇÓÃ3¸ö·µ»ØÖµ·Ö±ðÈ¥¸úMEASURED_STATE_MASKÏàÓë·¢ÏÖÖ»ÓÐMEASURED_STATE_TOO_SMALL ¿ÉÒÔʹ state == ViewCompat.MEASURED_STATE_TOO_SMALL,
ÄÇËüΪʲô»¹Òª¸úMEASURED_STATE_TOO_SMALL ÏàÓë?
ÔÚPercentRelativeLayoutÖеÄonLayout Öе÷ÓÃÁË this.mHelper.restoreOriginalParams()·½·¨,´Ó·½·¨ÃûºÍÌø×ª½øÈ¥¿´Ô´ÂëÄã·¢ÏÖËüÊǽ«Ö®Ç°µÄ±£´æÔÚPercentLayoutInfo µÄmPreservedParams ÖØÐ¸³Öµ¸ø childView µÄÏë·¨.µ«ÊÇÕæµÄÓÐÓÃÂð,ÒòΪµ±ËüÁ¬Ðø2´ÎµÄµ÷ÓÃonMeasure()·½·¨µÄʱºò,PercentLayoutInfo µÄmPreservedParams »á±»Ìæ»»µô,µ¼ÖÂPercentLayoutInfo µÄmPreservedParamsºÍ childView ÖÐµÄ layoutParamÊÇÒ»ÑùµÄ.ËùÒÔÕâ¸öÕæµÄÓÐÓÃÂð.
onMeasure Ϊʲô»áµ÷Óöà´Î?
¿´ How Android Draws Views »ò ÖÐÎİæ