Android中螢幕適配框架AutoLayout原始碼解讀
個人推薦
隨著越來越多的開發者使用hongyang大神的AutoLayout框架進行Android螢幕適配,我就去看了看這套框架做了哪些東西,結果發現AutoLayout框架本身程式碼量很少,但是思路值得我們開發者學習.
原始碼部落格地址
當我們做螢幕適配的時候,我們適配的是什麼
在Android中做螢幕適配的時候,我們適配的其實是各種控制元件的大小,間距等引數,所以適配的時候最重要的是拿到當前頁面的各種尺寸引數,而我們知道,Android中Activity在建立的時候,會走onCreateView回撥,表示接下來就要進行view的建立步驟,方法中傳過來三個引數,一個是name,一個是context,還有一個是Attributeset.寫過自定義控制元件的朋友都知道,任何一個view的構造引數中都有一個引數是Attributeset,這個Attributeset顧名思義,就是Attribute + set,即當前頁面的各種屬性引數集合,利用這個物件我們就可以構建當前頁面的各種佈局間距,完美進行螢幕適配.AutoLayoutActivity程式碼如下:
public class AutoLayoutActivity extends AppCompatActivity
{
private static final String LAYOUT_LINEARLAYOUT = “LinearLayout”;
private static final String LAYOUT_FRAMELAYOUT = “FrameLayout”;
private static final String LAYOUT_RELATIVELAYOUT = “RelativeLayout”;
@Override public View onCreateView(String name, Context context, AttributeSet attrs) { View view = null; if (name.equals(LAYOUT_FRAMELAYOUT)) { view = new AutoFrameLayout(context, attrs); } if (name.equals(LAYOUT_LINEARLAYOUT)) { view = new AutoLinearLayout(context, attrs); } if (name.equals(LAYOUT_RELATIVELAYOUT)) { view = new AutoRelativeLayout(context, attrs); } if (view != null) return view; return super.onCreateView(name, context, attrs); }
}
在使用框架的時候,我們的Activity需要繼承自AutoLayoutActivity,而這個Autoactivity內部,只有一個onCreateView方法,是不是非常簡單,這個方法就是拿到當前頁面的Attributeset,傳給對應的Layout佈局,這個Layout佈局就是我們頁面的根佈局,有LinearLayout,RelativeLayout,FrameLayout三種.所以當我們自己的Activity在走到onCreateView的時候,會呼叫AutoLayout的該方法,從而拿到的佈局從我們自己的LinearLayout,RelativeLayout,FrameLayout變成了AutoLinearLayout ,AutoRelativeLayout, AutoFrameLayout.接下來我們看看AutoLinearLayout,AutoRelativeLayout,AutoFrameLayout這些佈局裡面都幹了些什麼事呢,以AutoRelativeLayout為例,程式碼如下:
public class AutoRelativeLayout extends RelativeLayout
{
private final AutoLayoutHelper mHelper = new AutoLayoutHelper(this);
public AutoRelativeLayout(Context context)
{
super(context);
}
public AutoRelativeLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public AutoRelativeLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public AutoRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new LayoutParams(getContext(), attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (!isInEditMode())
mHelper.adjustChildren();
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);
}
public static class LayoutParams extends RelativeLayout.LayoutParams
implements AutoLayoutHelper.AutoLayoutParams
{
private AutoLayoutInfo mAutoLayoutInfo;
public LayoutParams(Context c, AttributeSet attrs)
{
super(c, attrs);
mAutoLayoutInfo = AutoLayoutHelper.getAutoLayoutInfo(c, attrs);
}
public LayoutParams(int width, int height)
{
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source)
{
super(source);
}
public LayoutParams(MarginLayoutParams source)
{
super(source);
}
@Override
public AutoLayoutInfo getAutoLayoutInfo()
{
return mAutoLayoutInfo;
}
}
}
我們可以看到,AutoRelativeLayout繼承自RelativeLayout,同時構造了內部類LayoutParams也是繼承自RelativeLayout的LayoutParams.我們先來看下LayoutParams的改動,因為我們知道LayoutParams表示當前Layout的佈局引數.在建構函式裡面,我們通過AutoLayoutHelper的getautolayoutinfo方法,生成了AutolayoutInfo,這個AutolayoutInfo代表的就是當前Layout的引數資訊.再來看看AutoRelativeLayout的改動,可以看到改動就兩個地方,一個是generateLayoutParams這個方法,返回的是他自己的內部類LayoutParams,另一個是在onmeasure方法中,呼叫了AutoLayoutHelper的adjustchildren方法,而這個方法是啟動AutoLayout佈局適配的方法,從以上步驟也可以看出,框架是通過一個AutoLayoutHelper類幫助我們去協調各個模組的運作.接下來我們將沿著adjustchildren這個方法一步一步揭曉適配的祕密.
public void adjustChildren()
{
AutoLayoutConifg.getInstance().checkParams();
for (int i = 0, n = mHost.getChildCount(); i < n; i++)
{
View view = mHost.getChildAt(i);
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params instanceof AutoLayoutParams)
{
AutoLayoutInfo info =
((AutoLayoutParams) params).getAutoLayoutInfo();
if (info != null)
{
info.fillAttrs(view);
}
}
}
}
可以看到,首先通過checkparams檢查清單檔案是否配置metadata,沒有配置的話將會報錯.然後,拿到LayoutParams中的AutoLayoutInfo,就是我們之前看到過的.AutoLayoutInfo有一個fillattrs方法,看看這個方法做了什麼:
public void fillAttrs(View view)
{
for (AutoAttr autoAttr : autoAttrs)
{
autoAttr.apply(view);
}
}
再看看Autoattr 和 apply方法是什麼:
public void apply(View view)
{
boolean log = view.getTag() != null && view.getTag().toString().equals("auto");
if (log)
{
L.e(" pxVal = " + pxVal + " ," + this.getClass().getSimpleName());
}
int val;
if (useDefault())
{
val = defaultBaseWidth() ? getPercentWidthSize() : getPercentHeightSize();
if (log)
{
L.e(" useDefault val= " + val);
}
} else if (baseWidth())
{
val = getPercentWidthSize();
if (log)
{
L.e(" baseWidth val= " + val);
}
} else
{
val = getPercentHeightSize();
if (log)
{
L.e(" baseHeight val= " + val);
}
}
if (val > 0)
val = Math.max(val, 1);//for very thin divider
execute(view, val);
}
原來,autoattr是一個抽象基類,子類是各種實現他的attr,比如heightattr,padingattr,widthattr等等,apply方法就是在基類中把引數的值先做螢幕適配轉換,再傳給子類,子類通過execute方法把這個值跟自己的layoutparams中的變數具體去賦值,這樣做子類就不用一一去做螢幕適配轉換.
至此,autolayout內部適配流程大致捋清.通過對LayoutParams中的各種attr的值,根據螢幕去做適配,從而完成整個Aativity的適配,其中AutoLayoutHelper負責各個類之間的協調工作,生成當前LayoutInfo引數資訊,比如啟動Layout適配.
聯絡作者
如果對Android適配有問題歡迎聯絡作者
QQ 562759989 微信 haikelei