讓自定義ViewGroup裏的子控件支持Margin

自定義控件系列:
秒懂OnMeasure
秒懂OnLayout
讓自定義ViewGroup裏的子控件支持Margin
讓自定義ViewGroup支持Padding
自定義ViewGroup的一個綜合實踐 FlowLayout

知識點:

  1. 讓自定義ViewGroup裏的子控件支持Margin,需要自定義ViewGroup在自身的onMeasure和onLayout裏去處理Margin,

因爲子控件寫margin之後,必然會讓自定義ViewGroup的寬高受到影響(所以onMeasure需要處理)

子控件寫margin之後,自定義ViewGroup在擺放這些子控件時候,必須考慮到margin,才能擺放到正確的位置所以onLayout需要處理)

  1. 但是如何得到子控件的Margin呢,不是你想的那麼容易直接就可拿到的,必須重寫下面的三個方法,但是好在寫法固定,照抄即可,想知道爲什麼,網上搜索吧,但是我覺得這是一個結論直接記住就行了,哪有那麼多爲什麼(點到爲止)
//    支持Margin的固定寫法,下面照抄就行了,至於爲什麼,可以去看源碼,但是我覺得直接記住就ok了

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new MarginLayoutParams(p);
    }

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

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

應用

1. 繼續寫上文的MyLinearLayout,讓他的子控件支持Margin

先看療效:
在這裏插入圖片描述

第一步:支持Margin的固定寫法

//    支持Margin的固定寫法,下面照抄就行了,至於爲什麼,可以去看源碼,但是我覺得直接記住就ok了

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new MarginLayoutParams(p);
    }

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

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

第二步:onMeasure裏考慮margin

在這裏插入圖片描述

    private Point caculateAtMostSize(int widthMeasureSpec, int heightMeasureSpec) {

        int width = 0;
        int height = 0;

        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            // 測量一下子控件的寬高
            measureChild(child, widthMeasureSpec, heightMeasureSpec);

            // 得到MarginLayoutParams,margin就在這裏保存着
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            // 獲得子控件的寬高(需要加上對應的margin,讓控件的寬高包含margin,
            // 這樣才能讓自定義的viewgroup在計算自身在AtMost模式的尺寸時候考慮到這些margin)
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            // 因爲我們的自定義View模擬的是豎向的LinearLayout,所以:
            // 控件的寬度爲所有子控件裏,寬度最大的那個view的寬度,
            // 控件高度是所有子空間的高度之和
            width = Math.max(childWidth, width);
            height += childHeight;
        }


        return new Point(width, height);
    }

第三步:onLayout裏考慮Margin

在這裏插入圖片描述
代碼如下:

  @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int top = 0;
        int count = getChildCount();
        for (int i = 0; i < count; i++) {

            View child = getChildAt(i);

            // 得到MarginLayoutParams,margin就在這裏保存着
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();


            // 獲得子控件的寬高(需要加上對應的margin,讓控件的寬高包含margin)
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            // 佈局,就是設置好這個子控件的左上右下
            // 在佈局的時候,考慮控件的margin,把子view擺放好
            // (這是一個豎向的LinearLayout,想想如何佈置子控件)
            child.layout(lp.leftMargin,
                    top + lp.topMargin,
                    childWidth - lp.rightMargin,
                    top + childHeight - lp.bottomMargin);
            top += childHeight;

        }

    }

github源碼之MyLinearLayoutWithMargin

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章