鸿洋AutoLayout代码分析(四):剩下的类


整体过一下认识的类

除去attr包以外,我们一起看下哪些我们说过

Paste_Image.png

我们一起回顾下

  • 入口是 AutoLayoutActivity
    • 简单处理,把
LinearLayout -> AutoLinearLayout
RelativeLayout -> AutoRelativeLayout
FrameLayout -> AutoFrameLayout
  • AutoLinearLayout,AutoRelativeLayout,AutoFrameLayout简单实现

    • 通过 关联和依赖 AutoLayoutHelper, 来连接
    • AutoLayoutInfo对象
    • (自己的静态内部类LayoutParams中 通过AutoLayoutHelper实现返回)
  • 获取Manifest中信息AutoLayoutConifg

    • 通过依赖 PackageManager, ApplicationInfo获取 design的宽高
    • 通过 ScreenUtils 来获取屏幕宽高

其他类

除去attr包以外,哪些还没有用过

Paste_Image.png
  • L
public class L
{
    public static boolean debug = false;
    private static final String TAG = "AUTO_LAYOUT";

    public static void e(String msg)
    {
        if (debug)
        {
            Log.e(TAG, msg);
        }
    }
}
  • 可以发现,只是一个打印控制类

  • UseLandscape

public interface UseLandscape
{
}
  • 只是空的,可能是预留以后写的吧
  • AutoUtils
public class AutoUtils{
    public static void auto(View view)
    {
        autoSize(view);
        autoPadding(view);
        autoMargin(view);
    }

    public static void autoMargin(View view)
    {
        if (!(view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams))
            return;

        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
        if (lp == null) return;

        Object tag = view.getTag(R.id.id_tag_autolayout_margin);
        if (tag != null) return;
        view.setTag(R.id.id_tag_autolayout_margin, "Just Identify");

        lp.leftMargin = getPercentWidthSize(lp.leftMargin);
        lp.topMargin = getPercentHeightSize(lp.topMargin);
        lp.rightMargin = getPercentWidthSize(lp.rightMargin);
        lp.bottomMargin = getPercentHeightSize(lp.bottomMargin);

    }

    public static void autoPadding(View view)
    {
        Object tag = view.getTag(R.id.id_tag_autolayout_padding);
        if (tag != null) return;
        view.setTag(R.id.id_tag_autolayout_padding, "Just Identify");

        int l = view.getPaddingLeft();
        int t = view.getPaddingTop();
        int r = view.getPaddingRight();
        int b = view.getPaddingBottom();

        l = getPercentWidthSize(l);
        t = getPercentHeightSize(t);
        r = getPercentWidthSize(r);
        b = getPercentHeightSize(b);

        view.setPadding(l, t, r, b);
    }

    public static void autoSize(View view)
    {
        ViewGroup.LayoutParams lp = view.getLayoutParams();

        if (lp == null) return;

        Object tag = view.getTag(R.id.id_tag_autolayout_size);
        if (tag != null) return;

        view.setTag(R.id.id_tag_autolayout_size, "Just Identify");

        if (lp.width > 0)
        {
            int screenWidth = AutoLayoutConifg.getInstance().getScreenWidth();
            int designWidth = AutoLayoutConifg.getInstance().getDesignWidth();
            lp.width = (int) (lp.width * 1.0f / designWidth * screenWidth);
        }

        if (lp.height > 0)
        {
            int screenHeight = AutoLayoutConifg.getInstance().getScreenHeight();
            int designHeight = AutoLayoutConifg.getInstance().getDesignHeight();
            lp.height = (int) (lp.height * 1.0f / designHeight * screenHeight);
        }
    }

    public static int getPercentWidthSize(int val)
    {
        int screenWidth = AutoLayoutConifg.getInstance().getScreenWidth();
        int designWidth = AutoLayoutConifg.getInstance().getDesignWidth();

        return (int) (val * 1.0f / designWidth * screenWidth);
    }

    public static int getPercentWidthSizeBigger(int val)
    {
        int screenWidth = AutoLayoutConifg.getInstance().getScreenWidth();
        int designWidth = AutoLayoutConifg.getInstance().getDesignWidth();

        int res = val * screenWidth;
        if (res % designWidth == 0)
        {
            return res / designWidth;
        } else
        {
            return res / designWidth + 1;
        }

    }

    public static int getPercentHeightSizeBigger(int val)
    {
        int screenHeight = AutoLayoutConifg.getInstance().getScreenHeight();
        int designHeight = AutoLayoutConifg.getInstance().getDesignHeight();

        int res = val * screenHeight;
        if (res % designHeight == 0)
        {
            return res / designHeight;
        } else
        {
            return res / designHeight + 1;
        }
    }

    public static int getPercentHeightSize(int val)
    {
        int screenHeight = AutoLayoutConifg.getInstance().getScreenHeight();
        int designHeight = AutoLayoutConifg.getInstance().getDesignHeight();

        return (int) (val * 1.0f / designHeight * screenHeight);
    }
}

我们发现都是静态方法,应该就只是一个帮助类了

  • MetroLayout
public class MetroLayout extends ViewGroup
{

    private final AutoLayoutHelper mHelper = new AutoLayoutHelper(this);

    private static class MetroBlock
    {
        int left;
        int top;
        int width;
    }

    private List<MetroBlock> mAvailablePos = new ArrayList<>();
    private int mDivider;

    public MetroLayout(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MetroLayout);
        mDivider = a.getDimensionPixelOffset(R.styleable.MetroLayout_metro_divider, 0);
        mDivider = AutoUtils.getPercentWidthSizeBigger(mDivider);
        a.recycle();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {

        if (true)
            randomColor();

        if (!isInEditMode())
            mHelper.adjustChildren();

        measureChildren(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }

    private void randomColor()
    {
        Random r = new Random(255);

        for (int i = 0, n = getChildCount(); i < n; i++)
        {
            View v = getChildAt(i);

            v.setBackgroundColor(Color.argb(100, r.nextInt(), r.nextInt(), r.nextInt()));
        }
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {

        initAvailablePosition();

        int left = 0;
        int top = 0;
        int divider = mDivider;

        for (int i = 0, n = getChildCount(); i < n; i++)
        {
            View v = getChildAt(i);
            if (v.getVisibility() == View.GONE) continue;

            MetroBlock newPos = findAvailablePos(v);
            left = newPos.left;
            top = newPos.top;

            int childWidth = v.getMeasuredWidth();
            int childHeight = v.getMeasuredHeight();

            int right = left + childWidth;
            int bottom = top + childHeight;

            v.layout(left, top, right, bottom);

            if (childWidth + divider < newPos.width)
            {
                newPos.left += childWidth + divider;
                newPos.width -= childWidth + divider;
            } else
            {
                mAvailablePos.remove(newPos);
            }

            MetroBlock p = new MetroBlock();
            p.left = left;
            p.top = bottom + divider;
            p.width = childWidth;
            mAvailablePos.add(p);

            mergeAvailablePosition();

        }
    }

    private void mergeAvailablePosition()
    {
        if (mAvailablePos.size() <= 1) return;

        List<MetroBlock> needRemoveBlocks = new ArrayList<>();

        MetroBlock one = mAvailablePos.get(0);
        MetroBlock two = mAvailablePos.get(1);

        for (int i = 1, n = mAvailablePos.size(); i < n - 1; i++)
        {
            if (one.top == two.top)
            {
                one.width = one.width + two.width;
                needRemoveBlocks.add(one);
                two.left = one.left;
                two = mAvailablePos.get(i + 1);
            } else
            {
                one = mAvailablePos.get(i);
                two = mAvailablePos.get(i + 1);
            }
        }

        mAvailablePos.removeAll(needRemoveBlocks);

    }

    private void initAvailablePosition()
    {
        mAvailablePos.clear();
        MetroBlock first = new MetroBlock();
        first.left = getPaddingLeft();
        first.top = getPaddingTop();
        first.width = getMeasuredWidth();
        mAvailablePos.add(first);
    }

    private MetroBlock findAvailablePos(View view)
    {
        MetroBlock p = new MetroBlock();
        if (mAvailablePos.size() == 0)
        {
            p.left = getPaddingLeft();
            p.top = getPaddingTop();
            p.width = getMeasuredWidth();
            return p;
        }
        int min = mAvailablePos.get(0).top;
        MetroBlock minHeightPos = mAvailablePos.get(0);
        for (MetroBlock _p : mAvailablePos)
        {
            if (_p.top < min)
            {
                min = _p.top;
                minHeightPos = _p;
            }
        }
        return minHeightPos;
    }


    @Override
    public MetroLayout.LayoutParams generateLayoutParams(AttributeSet attrs)
    {
        return new LayoutParams(getContext(), attrs);
    }

    public static class LayoutParams extends ViewGroup.MarginLayoutParams
            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);
        }

        public LayoutParams(LayoutParams source)
        {
            this((ViewGroup.LayoutParams) source);
            mAutoLayoutInfo = source.mAutoLayoutInfo;
        }

        @Override
        public AutoLayoutInfo getAutoLayoutInfo()
        {
            return mAutoLayoutInfo;
        }


    }

}

也和前面的AutoXXXLayout类似,
都只是通过 关联和依赖 AutoLayoutHelper ,
Override对应的
onMeasure(int widthMeasureSpec, int heightMeasureSpec) 和 generateLayoutParams(AttributeSet attrs)
通过静态内部类 的接口, 传递 AutoLayoutHelper的实现
返回 AutoLayoutInfo 对象
这里没有使用,所以暂时不考虑

  • AutoLayoutHelper
    • 这里就是 AutoLayoutHelper 最复杂
    • 很多类都会 关联 和 依赖 它
    • 后面单独分析这个类

AutoLayoutHelper

这里比较复杂,

Paste_Image.png

我们分别看一下这里面的接口和方法

AutoLayoutParams
public interface AutoLayoutParams
{
    AutoLayoutInfo getAutoLayoutInfo();
}

上一篇我们看到对应的几种AutoXXXLayout,静态内部类LayoutParams,都会实现这个接口
对应的实现,就是 静态方法 getAutoLayoutInfo(Context context,AttributeSet attrs)

AutoLayoutHelper构造 和 initAutoLayoutConfig
    private static AutoLayoutConifg mAutoLayoutConifg;

    public AutoLayoutHelper(ViewGroup host)
    {
        mHost = host;

        if (mAutoLayoutConifg == null)
        {
            initAutoLayoutConfig(host);
        }

    }

    private void initAutoLayoutConfig(ViewGroup host)
    {
        mAutoLayoutConifg = AutoLayoutConifg.getInstance();
        mAutoLayoutConifg.init(host.getContext());
    }

这里,可以发现,构造只是
传递ViewGroup对象,

调用initAutoLayoutConfig方法,初始化 懒汉单例的AutoLayoutConifg对象


剩下的 2个前面用到的方法

在AutoXXXLayout中
都只是通过 关联和依赖 这里的AutoLayoutHelper
onMeasure(int widthMeasureSpec, int heightMeasureSpec) 中
用到了 mHelper.adjustChildren()

generateLayoutParams(AttributeSet attrs)中
调用的静态内部类,接口的实现,其实是构造中
mAutoLayoutInfo = AutoLayoutHelper.getAutoLayoutInfo(c, attrs) 实现的
这里,我们分别看下这2个方法

getAutoLayoutInfo(Context context,AttributeSet attrs)
    public static AutoLayoutInfo getAutoLayoutInfo(Context context,AttributeSet attrs)
    {

        AutoLayoutInfo info = new AutoLayoutInfo();

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoLayout_Layout);
        int baseWidth = a.getInt(R.styleable.AutoLayout_Layout_layout_auto_basewidth, 0);
        int baseHeight = a.getInt(R.styleable.AutoLayout_Layout_layout_auto_baseheight, 0);
        a.recycle();

        TypedArray array = context.obtainStyledAttributes(attrs, LL);

        int n = array.getIndexCount();


        for (int i = 0; i < n; i++)
        {
            int index = array.getIndex(i);
//            String val = array.getString(index);
//            if (!isPxVal(val)) continue;

            if (!isPxVal(array.peekValue(index))) continue;

            int pxVal = 0;
            try
            {
                pxVal = array.getDimensionPixelOffset(index, 0);
            } catch (Exception ignore)//not dimension
            {
                continue;
            }
            switch (index)
            {
                case INDEX_TEXT_SIZE:
                    info.addAttr(new TextSizeAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_PADDING:
                    info.addAttr(new PaddingAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_PADDING_LEFT:
                    info.addAttr(new PaddingLeftAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_PADDING_TOP:
                    info.addAttr(new PaddingTopAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_PADDING_RIGHT:
                    info.addAttr(new PaddingRightAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_PADDING_BOTTOM:
                    info.addAttr(new PaddingBottomAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_WIDTH:
                    info.addAttr(new WidthAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_HEIGHT:
                    info.addAttr(new HeightAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_MARGIN:
                    info.addAttr(new MarginAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_MARGIN_LEFT:
                    info.addAttr(new MarginLeftAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_MARGIN_TOP:
                    info.addAttr(new MarginTopAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_MARGIN_RIGHT:
                    info.addAttr(new MarginRightAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_MARGIN_BOTTOM:
                    info.addAttr(new MarginBottomAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_MAX_WIDTH:
                    info.addAttr(new MaxWidthAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_MAX_HEIGHT:
                    info.addAttr(new MaxHeightAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_MIN_WIDTH:
                    info.addAttr(new MinWidthAttr(pxVal, baseWidth, baseHeight));
                    break;
                case INDEX_MIN_HEIGHT:
                    info.addAttr(new MinHeightAttr(pxVal, baseWidth, baseHeight));
                    break;
            }
        }
        array.recycle();
        L.e(" getAutoLayoutInfo " + info.toString());
        return info;
    }

这里类中定义了很多静态的int值,用于switch,
也有定义的LL静态数组,对应android中常用的属性

Paste_Image.png

这里获得这些常用属性以后,for循环会通过int的静态值,switch对应的地方
在添加到 容器类 AutoLayoutInfo 的对象中
(也就是 前面提到的接口实现返回的 AutoLayoutInfo 对象)
这里,每个switch,对应的一个 Attr 属性类, 存放在 attr 包中(之前分析单独忽略的那个package)
具体逻辑,之后再一起分析

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);
            }
        }
    }
}

这个类,相对于上一个方法,简单一点
这里会得到对应ViewGroup的 子View 个数,
for循环获取每个 子View的 LayoutParams
如果 params instanceof AutoLayoutParams
也就是说, 是我们前面实现的 接口类型的时候
转型获取前面接口实现的 AutoLayoutInfo 对象
(就是 上一个 getAutoLayoutInfo(Context context,AttributeSet attrs) 返回的对象)
最后 遍历 每个子View的 AutoLayoutInfo中每个常用属性


下一篇我们可以了解鸿洋AutoLayout代码分析(五):Attr相关类

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容