1. 程式人生 > >Android中螢幕適配框架AutoLayout原始碼解讀

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