自定义ViewGroup实现子View的padding、margin属性

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qiantanlong/article/details/82347850

当我们实现自定义的VewGroup控件时,会发现其内部的子View的margin、padding属性失效了,这是因为作为包含子View的控件,如果是直接继承自ViewGroup的话,需要自己处理子View的margin、padding的属性。如果是不包含子View的控件的话,则只要处理padding属性就可以了。这里呢,直接贴出代码,因为实现的逻辑相对简单,也在关键位置做了注释。如果对自定义控件不太熟悉的话,建议自行百度学习一下,这样才能够好的理解这个案例。

package hongzhen.com.defineviewdemo;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
 /**
      * 开发者:hongzhen
      * 创建时间: 2018/9/3 13:54
      * 公司名称:
      * 类名:MyMarginViewGroup.java
      * 描述:实现ViewGroup对子view的Margin、padding属性的支持
      */
public class MyMarginViewGroup extends ViewGroup {

    private int viewsWidth;
    private int viewsHeight;
    private int marginLeft;
    private int marginTop;
    private int marginRight;
    private int marginBottom;
    private int paddingLeft;
    private int paddingTop;
    private int paddingRight;
    private int paddingBottom;
    private int viewGroupWidth;
    private int viewGroupHeight;

    public MyMarginViewGroup(Context context) {
        this(context,null);
    }

    public MyMarginViewGroup(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public MyMarginViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr,0);
    }

    public MyMarginViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        inti();
    }

     /**
      * 初始化方法
      */
    private void inti() {

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewsWidth = 0;
        viewsHeight = 0;
        marginLeft = 0;
        marginTop = 0;
        marginRight = 0;
        marginBottom = 0;
        paddingLeft = getPaddingLeft();
        paddingTop = getPaddingTop();
        paddingRight = getPaddingRight();
        paddingBottom = getPaddingBottom();
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            //转换异常,android.view.ViewGroup$LayoutParams cannot be cast to android.view.ViewGroup$MarginLayoutParams
//            MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
            //自定义MarginLayoutParams的实现类,否则报转换异常
            CustomLayoutParams lp = (CustomLayoutParams) childView.getLayoutParams();
            /**
             * measureChild减去了 ViewGroup的padding 保证child最大可用空间
             * measureChildWithMargins减去了ViewGroup的padding和子View的margin 保证child最大可用空间
             * 这里要使用measureChildWithMargins
             */
//            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
            measureChildWithMargins(childView,widthMeasureSpec,0,heightMeasureSpec,0);
            //子view自上而下排列,左右边距求最大值,上下边距要求和
            viewsHeight += childView.getMeasuredHeight();
            viewsWidth = Math.max(viewsWidth, childView.getMeasuredWidth());
            marginLeft = Math.max(0,lp.leftMargin);//找出最大的左边距
            marginTop += lp.topMargin;//求出所有的上边距之和
            marginRight = Math.max(0,lp.rightMargin);//找出最大的右边距
            marginBottom += lp.bottomMargin;//求出所有的下边距之和
        }
        //处理ViewGroup的wrap_content情况,求出最小的大小
        viewGroupWidth = paddingLeft + viewsWidth + paddingRight + marginLeft + marginRight;
        viewGroupHeight = paddingTop + viewsHeight + paddingBottom + marginTop + marginBottom;
        setMeasuredDimension(measureWidth(widthMeasureSpec, viewGroupWidth), measureHeight
                (heightMeasureSpec, viewGroupHeight));
    }
    //margin属性配置----------------------------------------------------------------------------------
    /**
     * 生成默认的布局设置参数
     */
    @Override
    protected CustomLayoutParams generateDefaultLayoutParams() {
        return new CustomLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
    }

    /**
     * 生成布局参数 将布局参数包装成我们的自定义的MarginLayoutParams实现类
     */
    @Override
    protected android.view.ViewGroup.LayoutParams generateLayoutParams(
            android.view.ViewGroup.LayoutParams params) {
        return new CustomLayoutParams(params);
    }

    /**
     * 生成布局参数 从属性配置中生成自定义的MarginLayoutParams实现类
     */
    @Override
    public android.view.ViewGroup.LayoutParams generateLayoutParams(
            AttributeSet attrs) {
        return new CustomLayoutParams(getContext(), attrs);
    }

    /**
     * 检查当前布局参数是否是我们定义的MarginLayoutParams类型
     */
    @Override
    protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams params) {
        return params instanceof CustomLayoutParams;
    }

    /**
     *自定义MarginLayoutParams实现类,否则会出现转换异常
     *
     */
    public static class CustomLayoutParams extends MarginLayoutParams {

        public CustomLayoutParams(MarginLayoutParams source) {
            super(source);
        }

        public CustomLayoutParams(android.view.ViewGroup.LayoutParams source) {
            super(source);
        }

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

        public CustomLayoutParams(int width, int height) {
            super(width, height);
        }
    }
    //margin属性配置----------------------------------------------------------------------------------
    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        if (b) {
            int childCount = getChildCount();
            int mTop = paddingTop;
            for (int j = 0; j < childCount; j++) {
                View childView = getChildAt(j);
                MarginLayoutParams lp = (MarginLayoutParams)childView.getLayoutParams();
                int mLeft = paddingLeft + lp.leftMargin;
                mTop += lp.topMargin;
                childView.layout(mLeft, mTop, mLeft + childView.getMeasuredWidth(), mTop + childView.getMeasuredHeight());
                mTop += (childView.getMeasuredHeight() + lp.bottomMargin);
            }
        }
    }

    private int measureWidth(int measureSpec, int viewGroupWidth) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = specSize;
                break;
            case MeasureSpec.AT_MOST:
                //将剩余宽度和所有子View + padding的值进行比较,取小的作为ViewGroup的宽度 
                result = Math.min(viewGroupWidth, specSize);
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
        }
        return result;
    }

    private int measureHeight(int measureSpec, int viewGroupHeight) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = specSize;
                break;
            case MeasureSpec.AT_MOST:
                // 将剩余高度和所有子View + padding的值进行比较,取小的作为ViewGroup的高度
                result = Math.min(viewGroupHeight, specSize);
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
        }
        return result;
    }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <hongzhen.com.defineviewdemo.MyPaddingView
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_margin="10dp"
        android:padding="10dp"/>

    <hongzhen.com.defineviewdemo.MyMarginViewGroup
        android:background="#aaa"
        android:padding="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <hongzhen.com.defineviewdemo.MyPaddingView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#ddd"
            android:padding="10dp"
            android:layout_margin="15dp"/>
        <hongzhen.com.defineviewdemo.MyPaddingView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#ddd"
            android:padding="10dp"
            android:layout_margin="15dp"/>
    </hongzhen.com.defineviewdemo.MyMarginViewGroup>

</LinearLayout>

效果:请忽略上方的MyPaddingView控件,这个是实现View的padding属性的

猜你喜欢

转载自blog.csdn.net/qiantanlong/article/details/82347850