自定義View-流式布局

[toc]

流式布局

自定義view與自定義viewgroup的區(qū)別

  • 自定義view一般繼承View等其他View,通常實現(xiàn)onMeasureonDraw方法
  • 自定義ViewGroup一般是利用現(xiàn)有的組件根據(jù)特定的布局方式來組成新的組件,大多繼承ViewGroup或各種Layout,通常實現(xiàn)onMeasureonLayout

Android坐標系說明

兩種坐標系

Android屏幕坐標系png.png
視圖坐標系.png

MeasureSpec講解

MeasureSpace是View的內部類,是int類型的,用高兩位表示mode,低30位表示size

屬性說明

  • UNSPECIFIED 不對View大小進行限制,通常為系統(tǒng)使用,不用太關注
  • EXACTLY 確切的大小,比如100dp
  • AT_MOST 大小不可超過某個數(shù)值,比如 matchParent最大不能超過父布局

普通View的Measure的創(chuàng)建規(guī)則

通過表格快查子View的mode

普通View的Measure的創(chuàng)建規(guī)則

通過系統(tǒng)代碼解析子View的MeasureSpec創(chuàng)建過程

一般是通過調用系統(tǒng)的 getChildMeasureSpec 獲取子View的MeasureSpec,代碼解析如下:

/**
 * @param spec The requirements for this view(父元素ViewGroup的MeasureSpec)
 * @param padding The padding of this view for the current dimension and margins, if applicable (父元素的padding值)
 * @param childDimension How big the child wants to be in the current dimension(需要被測量的子View的) 屬性值
 *              View.getLayoutParams().width/height
 */
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        // 獲取父View的Mode與Size
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
        // 計算父View留給子View的具體尺寸(去除padding值的尺寸)
        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;
        // 判斷父View的mode
        switch (specMode) {
        // Parent has imposed an exact size on us
        // 如果父View是 EXACTLY (確定值)
        //      子View為 具體尺寸 比如 100dp,則子view的mode為 EXACTLY,最大尺寸就是childDimension
        //      子View為 MATCH_PARENT, 則子view的mode為 EXACTLY,最大尺寸為 size(父View留給子View的最大尺寸)
        //      子View為 WRAP_CONTENT, 則子view的mode為 AT_MOST,最大尺寸為 size(父View留給子View的最大尺寸)
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        // 如果父View是 AT_MOST (大小與內容有關)
        //      子View為 具體尺寸 比如 100dp,則子view的mode為 EXACTLY,最大尺寸就是childDimension
        //      子View為 MATCH_PARENT, 則子view的mode為 AT_MOST,最大尺寸為 size(父View留給子View的最大尺寸)
        //      子View為 WRAP_CONTENT, 則子view的mode為 AT_MOST,最大尺寸為 size(父View留給子View的最大尺寸)
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        // 此為系統(tǒng)調用,暫不分析 具體同上
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        // 計算子View的 MeasureSpec 并 返回
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

getWidth與getMeasureWidth區(qū)別

  • getWidth是在onLayout以后才能獲取到,是通過視圖右邊的坐標減去左邊的坐標計算出來的
  • getMeasureWidth是在onMeasure結束以后才可以獲取到的值,是通過setMeasuredDimension設置的值

coding

代碼地址點擊我

補充說明

onMeasure調用次數(shù)

  • onMeasure的調用次數(shù)與父View的算法有關系,比如:FrameLayout調用了子View的onMeasure有2次。

FlexboxLayout推薦

Android可伸縮布局-FlexboxLayout(支持RecyclerView集成)

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

友情鏈接更多精彩內容