鴻洋AutoLayout代碼分析(五):Attr相關(guān)類


回顧

上一節(jié),我們分析了很多類,
其中,最重要的是 AutoLayoutHelper 類的2個(gè)方法
adjustChildren()getAutoLayoutInfo(Context context,AttributeSet attrs)
分別 生成 和 使用 AutoLayoutInfo 對(duì)象

而我們談到,AutoLayoutInfo類 是一個(gè)容器類
AutoLayoutInfo

public class AutoLayoutInfo
{
    private List<AutoAttr> autoAttrs = new ArrayList<>();
    public void addAttr(AutoAttr autoAttr)
    {
        autoAttrs.add(autoAttr);
    }


    public void fillAttrs(View view)
    {
        for (AutoAttr autoAttr : autoAttrs)
        {
            autoAttr.apply(view);
        }
    }

    @Override
    public String toString()
    {
        return "AutoLayoutInfo{" +
                "autoAttrs=" + autoAttrs +
                '}';
    }
}

存儲(chǔ)在 一個(gè) ArrayList中
可以通過(guò) addAttr添加對(duì)象
可以通過(guò) fillAttrs(View view) 遍歷對(duì)象的方法


AutoAttr類

作為容器中的對(duì)象,我們看一下 AutoAttr類

public abstract class AutoAttr {
    protected int pxVal;
    protected int baseWidth;
    protected int baseHeight;

    /*
    protected boolean isBaseWidth;
    protected boolean isBaseDefault;

    public AutoAttr(int pxVal)
    {
        this.pxVal = pxVal;
        isBaseDefault = true;
    }

    public AutoAttr(int pxVal, boolean isBaseWidth)
    {
        this.pxVal = pxVal;
        this.isBaseWidth = isBaseWidth;
    }
 */

    public AutoAttr(int pxVal, int baseWidth, int baseHeight) {
        this.pxVal = pxVal;
        this.baseWidth = baseWidth;
        this.baseHeight = baseHeight;
    }

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

        val = Math.max(val, 1);//for very thin divider
        execute(view, val);
    }

    protected int getPercentWidthSize() {
        return AutoUtils.getPercentWidthSizeBigger(pxVal);
    }

    protected int getPercentHeightSize() {
        return AutoUtils.getPercentHeightSizeBigger(pxVal);
    }


    protected boolean baseWidth() {
        return contains(baseWidth, attrVal());
    }

    protected boolean useDefault() {
        return !contains(baseHeight, attrVal()) && !contains(baseWidth, attrVal());
    }

    protected boolean contains(int baseVal, int flag) {
        return (baseVal & flag) != 0;
    }

    protected abstract int attrVal();

    protected abstract boolean defaultBaseWidth();

    protected abstract void execute(View view, int val);

    @Override
    public String toString() {
        return "AutoAttr{" +
                "pxVal=" + pxVal +
                ", baseWidth=" + baseWidth() +
                ", defaultBaseWidth=" + defaultBaseWidth() +
                '}';
    }
}

我們可以發(fā)現(xiàn),它是抽象類,那容器中肯定是它的子類

Paste_Image.png

我們?cè)陬惿希?find usages 一下


Paste_Image.png

可以找到17個(gè)子類, 加上1個(gè)抽象類, 1個(gè)純定義的static final的 Attrs接口
剛好是attr包中的19個(gè)類


AutoAttr類簡(jiǎn)單分析

代碼上面已經(jīng)貼了
大體看看總共方法

Paste_Image.png

3個(gè)屬性,是通過(guò)構(gòu)造傳遞
分別是 px值, baseWidth, baseHeight

這里px值,是從上面 LL的 屬性數(shù)組TypeArray,
for循環(huán),拿到值
(這里包括TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PX的判斷,之前自己都沒(méi)有接觸過(guò),所以這塊略。作者應(yīng)該也查了很多api,或者積累了很多代碼量)
通過(guò) AutoLayoutInfo的 addAttr 添加到 容器類中


這里baseWidth, baseHeight是在 AutoLayoutHelper 的getAutoLayoutInfo方法中,通過(guò) TypedArray 的getInt 獲取的
通過(guò) AutoLayoutInfo的 addAttr 添加到 容器類中
我們跟一下這個(gè)自定義的屬性,發(fā)現(xiàn)這個(gè) declare-styleable 的 attr 下面是 flag 類型
具體就不多說(shuō)了(flag這個(gè)類型自己之前也沒(méi)有用過(guò)),可以參考
http://googlers.iteye.com/blog/1122585


3個(gè)抽象方法

我們先一起看一下對(duì)應(yīng)的3個(gè)抽象方法


abstract int attrVal()

我們觀察一下子類這個(gè)方法的實(shí)現(xiàn)
發(fā)現(xiàn),都會(huì)返回 Attrs接口中的屬性
我們看看 Attrs接口

public interface Attrs
{
    public static final int WIDTH = 1;
    public static final int HEIGHT = WIDTH << 1;
    public static final int TEXTSIZE = HEIGHT << 1;
    public static final int PADDING = TEXTSIZE << 1;
    public static final int MARGIN = PADDING << 1;
    public static final int MARGIN_LEFT = MARGIN << 1;
    public static final int MARGIN_TOP = MARGIN_LEFT << 1;
    public static final int MARGIN_RIGHT = MARGIN_TOP << 1;
    public static final int MARGIN_BOTTOM = MARGIN_RIGHT << 1;
    public static final int PADDING_LEFT = MARGIN_BOTTOM << 1;
    public static final int PADDING_TOP = PADDING_LEFT << 1;
    public static final int PADDING_RIGHT = PADDING_TOP << 1;
    public static final int PADDING_BOTTOM = PADDING_RIGHT << 1;
    public static final int MIN_WIDTH = PADDING_BOTTOM << 1;
    public static final int MAX_WIDTH = MIN_WIDTH << 1;
    public static final int MIN_HEIGHT = MAX_WIDTH << 1;
    public static final int MAX_HEIGHT = MIN_HEIGHT << 1;
}

這里每個(gè)變量都是上一個(gè)變量的位運(yùn)算
個(gè)人猜測(cè),應(yīng)該和 自己寫的
dispatchTouchEvent事件分發(fā)淺析(七)requestDisallowInterceptTouchEvent
中, boolean的Flag判斷有關(guān)

我們來(lái)對(duì)比下Attrs接口 和 res/values/attrs.xml中AutoLayout_Layout的attr name="layout_auto_basewidth"的flag值

<declare-styleable name="AutoLayout_Layout">
    <attr name="layout_auto_basewidth">
        <flag name="width" value="1"></flag>
        <flag name="height" value="2"></flag>
        <flag name="textSize" value="4"></flag>
        <flag name="padding" value="8"></flag>
        <flag name="margin" value="16"></flag>
        <flag name="marginLeft" value="32"></flag>
        <flag name="marginTop" value="64"></flag>
        <flag name="marginRight" value="128"></flag>
        <flag name="marginBottom" value="256"></flag>
        <flag name="paddingLeft" value="512"></flag>
        <flag name="paddingTop" value="1024"></flag>
        <flag name="paddingRight" value="2048"></flag>
        <flag name="paddingBottom" value="4096"></flag>
    </attr>

    <attr name="layout_auto_baseheight">
        <flag name="width" value="1"></flag>
        <flag name="height" value="2"></flag>
        <flag name="textSize" value="4"></flag>
        <flag name="padding" value="8"></flag>
        <flag name="margin" value="16"></flag>
        <flag name="marginLeft" value="32"></flag>
        <flag name="marginTop" value="64"></flag>
        <flag name="marginRight" value="128"></flag>
        <flag name="marginBottom" value="256"></flag>
        <flag name="paddingLeft" value="512"></flag>
        <flag name="paddingTop" value="1024"></flag>
        <flag name="paddingRight" value="2048"></flag>
        <flag name="paddingBottom" value="4096"></flag>
        <flag name="minWidth" value="8192"></flag>
        <flag name="maxWidth" value="16384"></flag>
        <flag name="minHeight" value="32768"></flag>
        <flag name="maxHeight" value="65536"></flag>
    </attr>
</declare-styleable>

發(fā)現(xiàn),都是17個(gè)屬性,對(duì)應(yīng)的值也是一樣,并且子類也有17個(gè)
每個(gè)類的名字,和對(duì)應(yīng)的屬性名字也是一樣
而 int attrVal() 方法,返回的的值也是 Attrs接口 對(duì)應(yīng)的值


abstract boolean defaultBaseWidth()

我們可以發(fā)現(xiàn)對(duì)應(yīng)的值,返回有true, 有false
所以可以簡(jiǎn)單歸納下這17個(gè)類

boolean defaultBaseWidth()
true:
    WidthAttr
    MarginLeftAttr
    MarginRightAttr
    MaxWidthAttr
    MinWidthAttr
    PaddingLeftAttr
    PaddingRightAttr

false:
    HeightAttr
    MarginAttr
    MarginBottomAttr
    MarginTopAttr
    MaxHeightAttr
    MinHeightAttr
    PaddingAttr
    PaddingBottomAttr
    PaddingTopAttr
    TextSizeAttr

我們可以發(fā)現(xiàn),
和 左右,寬 等相關(guān)的, 都返回true
和 上下,高 等相關(guān)的,都返回false


abstract void execute(View view, int val)

這里17個(gè)類的實(shí)現(xiàn)都不一樣,自己大體分了一下類
其實(shí)也挺好理解,我們先看一下分類
寬,高 相關(guān)

WidthAttr
HeightAttr
    設(shè)置ViewGrou.LayoutParams 對(duì)象的 lp.height 和 lp.width = val

Margin相關(guān)

MarginAttr
MarginLeftAttr
MarginRightAttr
MarginBottomAttr
MarginTopAttr
    instanceof判斷類型后,強(qiáng)轉(zhuǎn)ViewGroup.MarginLayoutParams后,設(shè)置對(duì)應(yīng)的方向值
    例如: MarginTopAttr就是設(shè)置 lp.topMargin = val 等
            MarginAttr 設(shè)置 lp.leftMargin = lp.rightMargin = lp.topMargin = lp.bottomMargin = val

Min,Max寬高相關(guān)

MaxWidthAttr
MaxHeightAttr
MinWidthAttr
MinHeightAttr
    通過(guò)反射,拿到對(duì)象的方法,在執(zhí)行對(duì)象的方法
    Method method = view.getClass().getMethod("方法名", 參數(shù)類型.class)
    method.invoke(view, val);

Padding相關(guān)

PaddingAttr
PaddingLeftAttr
PaddingRightAttr
PaddingBottomAttr
PaddingTopAttr
    拿到四個(gè)方向的Padding值,四個(gè)方向獲取
    int l = view.getPaddingLeft();
    int t = view.getPaddingTop();
    int r = view.getPaddingRight();
    int b = view.getPaddingBottom();
    
    如果PaddingLeft,l = val;(Padding 則四個(gè)方向都為 val)
    依此類推
    最后設(shè)置, view.setPadding(l,t,r,b);
    也就是設(shè)置屬性的方向,用轉(zhuǎn)換后的px值去替換

其他單獨(dú)

TextSizeAttr
    設(shè)置TextSize,px值

boolean類型判斷方法

再一起看一下和boolean相關(guān)的方法
因?yàn)檫@些方法和邏輯有關(guān), 通常比較簡(jiǎn)單, 并且價(jià)值也比較高

boolean contains(int baseVal, int flag)
    protected boolean contains(int baseVal, int flag) {
        return (baseVal & flag) != 0;
    }

我們看一下,對(duì)應(yīng)的usages


Paste_Image.png

可以發(fā)現(xiàn)就2個(gè)方法中使用到了,傳遞的值分別為:


Paste_Image.png

baseHeight或者 baseWidth, 再就是 上面提到的 抽象方法attrVal()


Paste_Image.png

這里 取與,也就是xml中的flag值 和 子類attrVal()的傳遞值,再某一位上是否有相同的1,如果對(duì)應(yīng)的1都在同一位,(baseVal & flag) != 0 就為 true

boolean useDefault() 和 boolean baseWidth()

上面也說(shuō)了,這2個(gè)方法,其實(shí)就只是簡(jiǎn)單調(diào)用了boolean contains(int baseVal, int flag) 的判斷

boolean baseWidth()

    protected boolean baseWidth() {
        return contains(baseWidth, attrVal());
    }

這里baseWidth() 只是判斷了 傳入的 baseWidth和子類attrVal()值是否在同一位上有相同的1

boolean useDefault()

    protected boolean useDefault() {
        return !contains(baseHeight, attrVal()) && !contains(baseWidth, attrVal());
    }

這里useDefault() 只是分別判斷了 傳入的 baseWidth,baseHeight和子類attrVal()值是否都 沒(méi)有相同的1


getPercentWidthSize() 和 getPercentHeightSize()

這里就只是簡(jiǎn)單的計(jì)算百分比之后,對(duì)應(yīng)的值
代碼比較簡(jiǎn)單,就一起貼了

    protected int getPercentWidthSize() {
        return AutoUtils.getPercentWidthSizeBigger(pxVal);
    }

    protected int getPercentHeightSize() {
        return AutoUtils.getPercentHeightSizeBigger(pxVal);
    }

這里,分別調(diào)用幫助類的靜態(tài)方法

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

這里計(jì)算也比較簡(jiǎn)單
寬: 獲得 屏幕寬度, design寬度, 按比例計(jì)算對(duì)應(yīng)的px值, 能整出就整除,不能整除就+1
高: 獲得 屏幕高度, design高度, 按比例計(jì)算對(duì)應(yīng)的px值, 能整出就整除,不能整除就+1


void apply(View view)

最后一個(gè)方法,也是比較關(guān)鍵的方法,對(duì)應(yīng)的邏輯方法
容器類AutoLayoutInfo添加屬性之后,
遍歷屬性的時(shí)候,都需要調(diào)用這個(gè)方法

Paste_Image.png

void apply(View view)

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

        val = Math.max(val, 1);//for very thin divider
        execute(view, val);
    }

首先回顧下
useDefault(): baseWidth,baseHeight和子類attrVal()值是否都 沒(méi)有相同的1,也就是和設(shè)置的屬性不一樣
baseWidth(): baseWidth 和 子類attrVal() 值是否都 沒(méi)有相同的1,也就是只是寬的判斷
大體就是(偽代碼)

- 如果useDefault() 為 true
  - defaultBaseWidth()為true 就  val = getPercentWidthSize()
  - defaultBaseWidth()為false 就  val = getPercentHeightSize()
- 如果baseWidth() 為 true
  - val = getPercentWidthSize()
- 其他
  - val = getPercentHeightSize()

最后,會(huì)val = Math.max(val, 1);
防止值特別小的情況
最后執(zhí)行 抽象方法 execute(view, val);
也就是上面分類的 方法,不同子類不同的實(shí)現(xiàn)
ps:
TextSizeAttr 對(duì)應(yīng)的實(shí)現(xiàn)是設(shè)置 字體大小, 也就是用 AutoLayout為什么字體需要設(shè)置為px的原因了


下一篇我們可以了解鴻洋AutoLayout代碼分析(六):AutoAttr子類補(bǔ)充

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 34,638評(píng)論 18 399
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,901評(píng)論 25 709
  • 原文地址:http://www.android100.org/html/201606/06/241682.html...
    AFinalStone閱讀 1,290評(píng)論 0 1
  • 最近越來(lái)越多的人把朋友圈設(shè)置為僅三天可見(jiàn)了。 很多爆款文章都把愛(ài)發(fā)朋友圈的行為稱之為低級(jí),又有爆款文章說(shuō)朋友圈干凈...
    簡(jiǎn)淺Jian閱讀 481評(píng)論 0 1
  • 今天是怎么了,感覺(jué)做了好多的事,見(jiàn)了好多的人,學(xué)了好多的知識(shí),增長(zhǎng)了好多的人氣。 今早起床,做了三十分鐘早操,然后...
    徐小蕾閱讀 262評(píng)論 0 0

友情鏈接更多精彩內(nèi)容