Android中屏幕适配框架AutoLayout源码解读

个人推荐

随着越来越多的开发者使用hongyang大神的AutoLayout框架进行Android屏幕适配,我就去看了看这套框架做了哪些东西,结果发现AutoLayout框架本身代码量很少,但是思路值得我们开发者学习.

源码博客地址

http://blog.csdn.net/lmj623565791/article/details/49990941/

当我们做屏幕适配的时候,我们适配的是什么

在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

你可能感兴趣的:(Android中屏幕适配框架AutoLayout源码解读)