PercentRelativeLayout 源码解析
ÎÄÕ³ö´¦£º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 ÓÐ:
1 ¼òÊö
ÎÒÃÇÄÜÓõĿؼþ?
ÓÐ PrecentFrameLayout ºÍ PrecentRelativeLayout
´ÓÕâ¸öÃû³ÆÉÏÃæÎÒÃÇ¿ÉÒÔºÜÇå³þµÄÖªµÀPrecentFrameLayout ¼Ì³Ð FrameLayout PrecentRelativeLayout ¼Ì³Ð RelativeLayout µÄ.
PrecentLayoutHelper ÊÇʲô?
ÆäʵÄã¿ÉÒÔ¿´³öPrecentFrameLayout ºÍPrecentRelativeLayout ÖÐʵÏÖЧ¹ûÀàËÆ,ÓÉ´Ë¿ÉÒÔ¿´³öÁ½¸öÀàÓÐ×ÅÌ«¶àÏàËƵĴúÂë,ËùÒÔ½«²Ù×÷·ÅÔÚHelperÖÐ,²»½öʹÂß¼¸üÇåÎú,ͬʱ¿ÉÒÔ½øÐÐͳһµÄά»¤,Ò»´ÎÐÞ¸Ä.Á½±ßÉúЧ.
2 Ô´Âë·ÖÎö
ÎÒÃDz鿴PercentRelativeLayout Õâ¸öÀà.ÎÒÃÇÖ±½ÓʹÓà as µÄ ctrl + Êó±ê×ó¼ü,Ö±½Ó½øÈëÔ´ÂëÈ¥²é¿´ (as Õæ°ô)
PercentRelativeLayout.class
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 ½øÐÐÀ©Õ¹.
PercentLayoutHelper.class
Õâ¸ö·½·¨Óе㳤.²»Ì«¿ÉÄÜ°ÑËùÓеĴúÂëÌù³öÀ´,ÎÒÌùÖØÒªµÄ.
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);
}
}
}
}
}
adjustChildren ·½·¨:
ͨ¹ýÐÞ¸Ä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;
}
handleMeasuredStateTooSmall ·½·¨ :
Õâ¸ö·½·¨ÊǼì²é childView ´óС¼ÆËãÊÇ·ñ²»ºÏÀí.
Õâ±ßViewCompat.getMeasuredWidthAndState(view)·µ»ØÖµÓÐ3¸ö
ÎÒµÄÒÉ»ó:
ÎÊÌâ1:
ÎÒµÄÎÊÌâÊÇÓÃ3¸ö·µ»ØÖµ·Ö±ðÈ¥¸úMEASURED_STATE_MASKÏàÓë·¢ÏÖÖ»ÓÐMEASURED_STATE_TOO_SMALL ¿ÉÒÔʹ state == ViewCompat.MEASURED_STATE_TOO_SMALL,
ÄÇËüΪʲô»¹Òª¸úMEASURED_STATE_TOO_SMALL ÏàÓë?
ÎÊÌâ2:
ÔÚPercentRelativeLayoutÖеÄonLayout Öе÷ÓÃÁË this.mHelper.restoreOriginalParams()·½·¨,´Ó·½·¨ÃûºÍÌøת½øÈ¥¿´Ô´ÂëÄã·¢ÏÖËüÊǽ«Ö®Ç°µÄ±£´æÔÚPercentLayoutInfo µÄmPreservedParams ÖØи³Öµ¸ø childView µÄÏë·¨.µ«ÊÇÕæµÄÓÐÓÃÂð,ÒòΪµ±ËüÁ¬Ðø2´ÎµÄµ÷ÓÃonMeasure()·½·¨µÄʱºò,PercentLayoutInfo µÄmPreservedParams »á±»Ìæ»»µô,µ¼ÖÂPercentLayoutInfo µÄmPreservedParamsºÍ childView ÖÐµÄ layoutParamÊÇÒ»ÑùµÄ.ËùÒÔÕâ¸öÕæµÄÓÐÓÃÂð.
onMeasure Ϊʲô»áµ÷Óöà´Î?
¿´ How Android Draws Views »ò ÖÐÎÄ°æ