二、自定義垂直ViewGroup如何設定margin

怪叔叔蘿莉控發表於2020-12-19

ViewGroup設定margin累計分為三步:
1.獲取margin
2.onMeasure裡面加上margin
3.onLayout佈局設定margin

獲取margin

首先呢,ViewGroup是自帶的MarginLayoutParams的,但是在addView時,檢視原始碼:

    public void addView(View child, int index) {
        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }
        LayoutParams params = child.getLayoutParams();
        if (params == null) {
            params = generateDefaultLayoutParams();
            if (params == null) {
                throw new IllegalArgumentException(
                        "generateDefaultLayoutParams() cannot return null");
            }
        }
        addView(child, index, params);
    }

然後點開generateDefaultLayoutParams,很明顯,這個generateDefaultLayoutParams是不支援MarginLayoutParams的,但是我們需要獲取到margin值,就需要自己來重寫這個generateDefaultLayoutParams。

    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

重寫的generateDefaultLayoutParams,為了相容xml和程式碼new出來addView.這裡我們複寫了所有構造。


    public static class VerticalLayoutParams extends MarginLayoutParams {

        public VerticalLayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }

        public VerticalLayoutParams(int width, int height) {
            super(width, height);
        }

        public VerticalLayoutParams(LayoutParams lp) {
            super(lp);
        }
    }

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

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
        return new VerticalLayoutParams(lp);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new VerticalLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

好了,準備工作已經做完了,接下來就可以來計算高度了,首先我們用手動的方式來計算一下總高度:

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int totalHeight = 0;
        int totalWidth = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            //計算總共高度
            totalHeight = child.getMeasuredHeight() + totalHeight;
            ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
            totalHeight = totalHeight+((ViewGroup.MarginLayoutParams)layoutParams).topMargin;
            //取最大的子view的寬度
            totalWidth = Math.max(child.getMeasuredWidth(), totalWidth);
        }
        float xmlMaxWidth = getResources().getDimension(R.dimen.dp250);
        float xmlMaxHeight = getResources().getDimension(R.dimen.dp100);
        Log.d(TAG,"xmlMaxWidth = "+xmlMaxWidth+"   xmlMaxHeight = "+xmlMaxHeight+"     totalWidth = "+(totalWidth)+"  totalHeight = "+totalHeight);
        setMeasuredDimension(totalWidth,totalHeight);
    }

執行試驗結果:
在這裡插入圖片描述
線面空出來了一部分黑色。這是因為我們的layout並未將margin這部分實現,那麼接下來就來實現一下吧:

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int curTop = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            VerticalLayoutParams layoutParams = (VerticalLayoutParams) child.getLayoutParams();
            curTop = curTop+layoutParams.topMargin;
            child.layout(0,curTop,child.getMeasuredWidth(),curTop+child.getMeasuredHeight());
            curTop = curTop + child.getMeasuredHeight();
        }
    }

這短程式碼僅僅增加了:

            VerticalLayoutParams layoutParams = (VerticalLayoutParams) child.getLayoutParams();
            curTop = curTop+layoutParams.topMargin;

獲取margin並累加,看實驗結果:
在這裡插入圖片描述已經實現了正常的margin邏輯。

相關文章