Android实现自定义圆角边框渐变

1.定义全部圆角的通用接口

public interface IRadiusLayout {
    int DEFAULT_RADIUS = 0; // 默认没有圆角
    int SOLID_TYPE_SOLID = 0; // 实线
    int SOLID_TYPE_DASH = 1;  // 虚线

    /**
     * 设置背景颜色状态列表
     *
     * @param bgColorStateList 背景颜色状态列表
     */
    void setBackgroundColor(ColorStateList bgColorStateList);

    /**
     * 设置边框虚线样式
     *
     * @param dashPathEffect 边框虚线样式
     */
    void setSolidDashPathEffect(DashPathEffect dashPathEffect);

    /**
     * 设置边框线颜色
     *
     * @param color 边框线颜色
     */
    void setSolidColor(int color);

    /**
     * 设置边框颜色状态列表
     *
     * @param solidColorStateList 边框颜色状态列表
     */
    void setSolidColor(ColorStateList solidColorStateList);

    /**
     * 设置圆角,四个圆角大小一样
     *
     * @param radius 圆角大小
     */
    void setRadius(int radius);

    /**
     * 设置圆角大小,分别设置
     *
     * @param leftTopRadius     左上角圆角大小
     * @param rightTopRadius    右上角圆角大小
     * @param rightBottomRadius 左下角圆角大小
     * @param leftBottomRadius  右下角圆角大小
     */
    void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius);

    /**
     * 设置左上角圆角大小
     *
     * @param leftTopRadius 左上角圆角大小
     */
    void setLeftTopRadius(int leftTopRadius);

    /**
     * 设置右上角圆角大小
     *
     * @param rightTopRadius 右上角圆角大小
     */
    void setRightTopRadius(int rightTopRadius);

    /**
     * 设置右下角圆角大小
     *
     * @param rightBottomRadius 右下角圆角大小
     */
    void setRightBottomRadius(int rightBottomRadius);

    /**
     * 设置左下角圆角大小
     *
     * @param leftBottomRadius 左下角圆角大小
     */
    void setLeftBottomRadius(int leftBottomRadius);

    /**
     * 设置背景渐变信息
     *
     * @param shapeType   渐变类型
     * @param shapeColors 渐变颜色数组
     */
    void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors);

    /**
     * 设置背景渐变信息
     *
     * @param shapeType               渐变类型
     * @param shapeColors             渐变颜色数组
     * @param shaderLinearOrientation 渐变方向
     */
    void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation);

    /**
     * 设置边框渐变信息
     *
     * @param shapeType   渐变类型
     * @param shapeColors 渐变颜色数组
     */
    void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors);

    /**
     * 设置边框渐变信息
     *
     * @param shapeType               渐变类型
     * @param shapeColors             渐变颜色数组
     * @param shaderLinearOrientation 渐变方向
     */
    void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation);
}

2.定义渐变工具ShaderUtils

public class ShaderUtils {
    // 定义超出颜色范围的值作为非颜色值
    public static final int COLOR_VALUE_NONE = -0xFFFFFFFF;
    // 渐变类型
    public static final int SHADER_TYPE_LINEAR = 10; // 线性渐变
    public static final int SHADER_TYPE_RADIAL = 11; // 圆形渐变
    public static final int SHADER_TYPE_SWEEP = 12;  // 扫描渐变
    public static final int SHADER_TYPE_NONE = -1;   // 不要渐变

    // 线性渐变方向
    public static final int LINEAR_ORIENTATION_TOP_TO_BOTTOM = 110; // 从上到下
    public static final int LINEAR_ORIENTATION_BOTTOM_TO_TOP = 111; // 从下到上
    public static final int LINEAR_ORIENTATION_LEFT_TO_RIGHT = 220; // 从左到右
    public static final int LINEAR_ORIENTATION_RIGHT_TO_LEFT = 221; // 从右到左
    public static final int LINEAR_ORIENTATION_LEFT_TOP_TO_RIGHT_BOTTOM = 330; // 从左上到右下
    public static final int LINEAR_ORIENTATION_RIGHT_BOTTOM_TO_LEFT_TOP = 331; // 从右下到左上
    public static final int LINEAR_ORIENTATION_RIGHT_TOP_TO_LEFT_BOTTOM = 440; // 从右上到左下
    public static final int LINEAR_ORIENTATION_LEFT_BOTTOM_TO_RIGHT_TOP = 441; // 从左下到右上

    @IntDef(value = {SHADER_TYPE_LINEAR, SHADER_TYPE_RADIAL, SHADER_TYPE_SWEEP})
    public @interface ShaderType {
    }

    @IntDef(value = {LINEAR_ORIENTATION_TOP_TO_BOTTOM, LINEAR_ORIENTATION_BOTTOM_TO_TOP,
            LINEAR_ORIENTATION_LEFT_TO_RIGHT, LINEAR_ORIENTATION_RIGHT_TO_LEFT,
            LINEAR_ORIENTATION_LEFT_TOP_TO_RIGHT_BOTTOM, LINEAR_ORIENTATION_RIGHT_BOTTOM_TO_LEFT_TOP,
            LINEAR_ORIENTATION_RIGHT_TOP_TO_LEFT_BOTTOM, LINEAR_ORIENTATION_LEFT_BOTTOM_TO_RIGHT_TOP})
    public @interface LinearOrientation {
    }

    private ShaderUtils() {
    }

    public static Shader createShader(@ShaderType int shaderType, int width, int height, int[] colors, @LinearOrientation int orientation) {
        if (shaderType == SHADER_TYPE_LINEAR) {
            return createLinearGradient(width, height, colors, orientation);
        } else if (shaderType == SHADER_TYPE_RADIAL) {
            return createRadialGradient(width, height, colors);
        } else if (shaderType == SHADER_TYPE_SWEEP) {
            return createSweepGradient(width, height, colors);
        } else {
            return createLinearGradient(width, height, colors, orientation);
        }
    }

    public static LinearGradient createLinearGradient(int width, int height, int[] colors, @LinearOrientation int orientation) {
        LinearGradient linearGradient;
        int halfWidth = width / 2;
        int halfHeight = height / 2;
        if (LINEAR_ORIENTATION_TOP_TO_BOTTOM == orientation) {
            linearGradient = new LinearGradient(halfWidth, 0, halfWidth, height, colors, null, Shader.TileMode.CLAMP);
        } else if (LINEAR_ORIENTATION_BOTTOM_TO_TOP == orientation) {
            linearGradient = new LinearGradient(halfWidth, height, halfWidth, 0, colors, null, Shader.TileMode.CLAMP);
        } else if (LINEAR_ORIENTATION_LEFT_TO_RIGHT == orientation) {
            linearGradient = new LinearGradient(0, halfHeight, width, halfHeight, colors, null, Shader.TileMode.CLAMP);
        } else if (LINEAR_ORIENTATION_RIGHT_TO_LEFT == orientation) {
            linearGradient = new LinearGradient(width, halfHeight, 0, halfHeight, colors, null, Shader.TileMode.CLAMP);
        } else if (LINEAR_ORIENTATION_LEFT_TOP_TO_RIGHT_BOTTOM == orientation) {
            linearGradient = new LinearGradient(0, 0, width, height, colors, null, Shader.TileMode.CLAMP);
        } else if (LINEAR_ORIENTATION_RIGHT_BOTTOM_TO_LEFT_TOP == orientation) {
            linearGradient = new LinearGradient(width, height, 0, 0, colors, null, Shader.TileMode.CLAMP);
        } else if (LINEAR_ORIENTATION_RIGHT_TOP_TO_LEFT_BOTTOM == orientation) {
            linearGradient = new LinearGradient(width, 0, 0, height, colors, null, Shader.TileMode.CLAMP);
        } else if (LINEAR_ORIENTATION_LEFT_BOTTOM_TO_RIGHT_TOP == orientation) {
            linearGradient = new LinearGradient(0, height, width, 0, colors, null, Shader.TileMode.CLAMP);
        } else {
            linearGradient = new LinearGradient(halfWidth, 0, halfWidth, height, colors, null, Shader.TileMode.CLAMP);
        }
        return linearGradient;
    }

    public static RadialGradient createRadialGradient(int width, int height, int[] colors) {
        int halfWidth = width / 2;
        int halfHeight = height / 2;
        return new RadialGradient(halfWidth, halfHeight, Math.min(halfWidth, halfHeight), colors, null, Shader.TileMode.CLAMP);
    }

    public static SweepGradient createSweepGradient(int width, int height, int[] colors) {
        int halfWidth = width / 2;
        int halfHeight = height / 2;
        return new SweepGradient(halfWidth, halfHeight, colors, null);
    }

    public static int[] createColorsArray(int startColor, int middleColor, int middleColor2, int middleColor3, int endColor) {
        List<Integer> colors = new ArrayList<>();
        if (startColor != COLOR_VALUE_NONE) {
            colors.add(startColor);
        }
        if (middleColor != COLOR_VALUE_NONE) {
            colors.add(middleColor);
        }
        if (middleColor2 != COLOR_VALUE_NONE) {
            colors.add(middleColor2);
        }
        if (middleColor3 != COLOR_VALUE_NONE) {
            colors.add(middleColor3);
        }
        if (endColor != COLOR_VALUE_NONE) {
            colors.add(endColor);
        }
        if (colors.size() > 0) {
            int[] shaderColors = new int[colors.size()];
            for (int i = 0; i < colors.size(); i++) {
                shaderColors[i] = colors.get(i);
            }
            return shaderColors;
        }
        return null;
    }

    public static int[] createColorsArray(int startColor, int middleColor, int endColor) {
        List<Integer> colors = new ArrayList<>();
        if (startColor != COLOR_VALUE_NONE) {
            colors.add(startColor);
        }
        if (middleColor != COLOR_VALUE_NONE) {
            colors.add(middleColor);
        }
        if (endColor != COLOR_VALUE_NONE) {
            colors.add(endColor);
        }
        if (colors.size() > 0) {
            int[] shaderColors = new int[colors.size()];
            for (int i = 0; i < colors.size(); i++) {
                shaderColors[i] = colors.get(i);
            }
            return shaderColors;
        }
        return null;
    }
}

3.圆角工具RadiusUtils

public class RadiusUtils {
    public static final int TYPE_NONE = 0x0000;  // 没有圆角
    public static final int TYPE_RADIUS = 0x0001;  // 圆角形状
    public static final int TYPE_CIRCLE = 0x0002;  // 整体为圆形
    public static final int TYPE_OVAL_LEFT = 0x0004;    // 左边为椭圆
    public static final int TYPE_OVAL_TOP = 0x0008;    // 上边为椭圆
    public static final int TYPE_OVAL_RIGHT = 0x0010;    // 右边为椭圆
    public static final int TYPE_OVAL_BOTTOM = 0x0020;    // 下边为椭圆

    /**
     * 计算背景路径 ConvexPath<br/>
     * <b>ConvexPath 这里简单理解:圆角矩形的圆角度数大于矩形的高度或宽度(上下圆角度数的边长和大于高度或者左右圆角度数的边长大于高度,那么就不是 ConvexPath 了)</b>
     *
     * @param leftTopRadius     左上角圆角大小
     * @param rightTopRadius    右上角圆角大小
     * @param leftBottomRadius  左下角圆角大小
     * @param rightBottomRadius 右下角圆角大小
     * @param width             矩形宽
     * @param height            矩形高
     * @return 结果Path
     */
    public static Path calculateBgPath(int leftTopRadius, int rightTopRadius,
                                       int leftBottomRadius, int rightBottomRadius,
                                       int width, int height) {
        return calculateBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, true);
    }

    /**
     * 计算背景路径 ConvexPath<br/>
     * <b>ConvexPath 这里简单理解:圆角矩形的圆角度数大于矩形的高度或宽度(上下圆角度数的边长和大于高度或者左右圆角度数的边长大于高度,那么就不是 ConvexPath 了)</b>
     *
     * @param leftTopRadius     左上角圆角大小
     * @param rightTopRadius    右上角圆角大小
     * @param leftBottomRadius  左下角圆角大小
     * @param rightBottomRadius 右下角圆角大小
     * @param width             矩形宽
     * @param height            矩形高
     * @param isGetType         是否需要判断类型,当为 true 时可能返回非 Convex Path
     * @return 结果Path
     */
    public static Path calculateBgPath(int leftTopRadius, int rightTopRadius,
                                       int leftBottomRadius, int rightBottomRadius,
                                       int width, int height, boolean isGetType) {
        leftTopRadius = Math.max(leftTopRadius, 0);
        rightTopRadius = Math.max(rightTopRadius, 0);
        leftBottomRadius = Math.max(leftBottomRadius, 0);
        rightBottomRadius = Math.max(rightBottomRadius, 0);
        if (isGetType) {
            int type = getType(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
            if (type == TYPE_NONE)
                return calculateRectBgPath(width, height);

            if (type == TYPE_RADIUS)
                return calculateRadiusBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);

            if (type == TYPE_CIRCLE) {
                Path resultPath = new Path();
                RectF rectF = new RectF();
                rectF.set(0, 0, width, height);
                resultPath.addCircle(rectF.centerX(), rectF.centerY(), Math.min(rectF.width(), rectF.height()) / 2f, Path.Direction.CW);
                return resultPath;
            }

            Path resultPath = new Path();
            if (((type & TYPE_OVAL_LEFT) != 0) && (type & TYPE_OVAL_RIGHT) != 0) {
                // 左右都半圆
                RectF arcRectF = new RectF();
                // 左边半圆
                arcRectF.set(0, 0, height, height);
                resultPath.addArc(arcRectF, 90, 180);
                // 上边的线
                resultPath.lineTo(width - height / 2f, 0);
                // 右边半圆
                RectF rightRectF = new RectF();
                rightRectF.set(width - height, 0, width, height);
                resultPath.addArc(rightRectF, -90, 180);
                // 下边的线
                resultPath.lineTo(height / 2f, height);
            } else if (((type & TYPE_OVAL_TOP) != 0) && (type & TYPE_OVAL_BOTTOM) != 0) {
                // 上下都半圆
                RectF arcRectF = new RectF();
                // 上边半圆
                arcRectF.set(0, 0, width, width);
                resultPath.addArc(arcRectF, 180, 180);
                // 右边的线
                resultPath.lineTo(width, height - width / 2f);
                // 下边半圆
                RectF rightRectF = new RectF();
                rightRectF.set(0, height - width, width, height);
                resultPath.addArc(rightRectF, 0, 180);
                // 左边的线
                resultPath.lineTo(0, width / 2f);
            } else if ((type & TYPE_OVAL_LEFT) != 0) {
                RectF arcRectF = new RectF();
                // 左半圆
                arcRectF.set(0, 0, height, height);
                resultPath.addArc(arcRectF, 90, 180);
                // 上边的线
                resultPath.lineTo(width - rightTopRadius, 0);
                // 右上角
                resultPath.quadTo(width, 0, width, rightTopRadius);
                // 右边线
                resultPath.lineTo(width, height - rightBottomRadius);
                // 右下角
                resultPath.quadTo(width, height, width - rightBottomRadius, height);
                // 下边的线
                resultPath.lineTo(height / 2f, height);
            } else if ((type & TYPE_OVAL_RIGHT) != 0) {
                // 上边的线
                resultPath.moveTo(leftTopRadius, 0);
                resultPath.lineTo(width - height / 2f, 0);
                // 右边半圆
                RectF rightRectF = new RectF();
                rightRectF.set(width - height, 0, width, height);
                resultPath.addArc(rightRectF, -90, 180);
                // 下边线
                resultPath.lineTo(leftBottomRadius, height);
                // 左下角
                resultPath.quadTo(0, height, 0, height - leftBottomRadius);
                // 左边线
                resultPath.lineTo(0, leftTopRadius);
                // 左上角
                resultPath.quadTo(0, 0, leftTopRadius, 0);
            } else if ((type & TYPE_OVAL_TOP) != 0) {
                RectF arcRectF = new RectF();
                // 上半圆
                arcRectF.set(0, 0, width, width);
                resultPath.addArc(arcRectF, 180, 180);
                // 右边的线
                resultPath.lineTo(width, height - rightBottomRadius);
                // 右下角
                resultPath.quadTo(width, height, width - rightBottomRadius, height);
                // 底部线
                resultPath.lineTo(leftBottomRadius, height);
                // 左下角
                resultPath.quadTo(0, height, 0, height - leftBottomRadius);
                // 左边的线
                resultPath.lineTo(0, width / 2f);
            } else if ((type & TYPE_OVAL_BOTTOM) != 0) {
                // 上边线
                resultPath.moveTo(leftTopRadius, 0);
                resultPath.lineTo(width - rightTopRadius, 0);
                // 右上角
                resultPath.quadTo(width, 0, width, rightTopRadius);
                // 右边线
                resultPath.lineTo(width, height - width / 2f);
                // 下半圆
                RectF rightRectF = new RectF();
                rightRectF.set(0, height - width, width, height);
                resultPath.addArc(rightRectF, 0, 180);
                // 左边线
                resultPath.lineTo(0, leftTopRadius);
                // 左上角
                resultPath.quadTo(0, 0, leftTopRadius, 0);
            } else {
                return calculateRadiusBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
            }

            return resultPath;
        }

        return calculateRadiusBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
    }

    /**
     * 计算圆角矩形背景路径
     */
    private static Path calculateRadiusBgPath(int leftTopRadius, int rightTopRadius, int leftBottomRadius,
                                              int rightBottomRadius, int width, int height) {
        float leftTopRadiusLeft, leftTopRadiusTop; // 左上角
        float leftBottomRadiusLeft, leftBottomRadiusBottom; // 左下角
        float rightTopRadiusRight, rightTopRadiusTop; // 右上角
        float rightBottomRadiusRight, rightBottomRadiusBottom; // 右下角
        int[] sideTop = calculateRadiusLength(leftTopRadius, rightTopRadius, width); // 上同边
        int[] sideBottom = calculateRadiusLength(leftBottomRadius, rightBottomRadius, width); // 下同边
        int[] sideLeft = calculateRadiusLength(leftTopRadius, leftBottomRadius, height); // 左同边
        int[] sideRight = calculateRadiusLength(rightTopRadius, rightBottomRadius, height); // 右同边
        leftTopRadiusTop = sideTop[0];
        rightTopRadiusTop = sideTop[1];
        leftBottomRadiusBottom = sideBottom[0];
        rightBottomRadiusBottom = sideBottom[1];
        leftTopRadiusLeft = sideLeft[0];
        leftBottomRadiusLeft = sideLeft[1];
        rightTopRadiusRight = sideRight[0];
        rightBottomRadiusRight = sideRight[1];

        Path resultPath = new Path();
        // 四个角:右上,右下,左下,左上
        resultPath.moveTo(leftTopRadiusTop, 0);
        resultPath.lineTo(width - rightTopRadiusTop, 0);
        resultPath.quadTo(width, 0, width, rightTopRadiusRight);

        resultPath.lineTo(width, height - rightBottomRadiusRight);
        resultPath.quadTo(width, height, width - rightBottomRadiusBottom, height);

        resultPath.lineTo(leftBottomRadiusBottom, height);
        resultPath.quadTo(0, height, 0, height - leftBottomRadiusLeft);

        resultPath.lineTo(0, leftTopRadiusLeft);
        resultPath.quadTo(0, 0, leftTopRadiusTop, 0);
        return resultPath;
    }

    /**
     * 计算直角矩形背景路径
     *
     * @param width  宽
     * @param height 高
     */
    private static Path calculateRectBgPath(int width, int height) {
        Path result = new Path();
        result.moveTo(0, 0);
        result.lineTo(width, 0);
        result.lineTo(width, height);
        result.lineTo(0, height);
        result.close();
        return result;
    }

    /**
     * 计算边框的边框路径 ConvexPath<br/>
     * <b>ConvexPath 这里简单理解:圆角矩形的圆角度数大于矩形的高度或宽度(上下圆角度数的边长和大于高度或者左右圆角度数的边长大于高度,那么就不是 ConvexPath 了)</b>
     *
     * @param leftTopRadius     左上角圆角大小
     * @param rightTopRadius    右上角圆角大小
     * @param leftBottomRadius  左下角圆角大小
     * @param rightBottomRadius 右下角圆角大小
     * @param width             矩形宽
     * @param height            矩形高
     * @param solidWidth        边框宽度
     * @return 结果Path 数组,path[0]:四条边 path[1]:四个角的路径
     */
    public static Path[] calculateSocketPath(int leftTopRadius, int rightTopRadius,
                                             int leftBottomRadius, int rightBottomRadius,
                                             int width, int height, int solidWidth) {
        leftTopRadius = Math.max(leftTopRadius, 0);
        rightTopRadius = Math.max(rightTopRadius, 0);
        leftBottomRadius = Math.max(leftBottomRadius, 0);
        rightBottomRadius = Math.max(rightBottomRadius, 0);
        int type = getType(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);

        if (type == TYPE_NONE)
            return new Path[]{calculateRectSocketPath(width, height, solidWidth)};

        if (type == TYPE_RADIUS)
            return calculateRadiusSocketPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, solidWidth);

        if (type == TYPE_CIRCLE) {
            Path[] result = new Path[1];
            Path resultPath = new Path();
            RectF rectF = new RectF();
            rectF.set(0, 0, width, height);
            // 对位置进行偏移线宽的一半,因为直接画线的话,有一半是画到画布外的,
            // 但是因为有圆角,圆角后面还有画布,导致角的线宽比边的线宽要宽
            // 矩形缩小边框的一半
            float newWidth = solidWidth / 2.0f;
            rectF.inset(newWidth, newWidth);
            resultPath.addCircle(rectF.centerX(), rectF.centerY(), Math.min(rectF.width(), rectF.height()) / 2f, Path.Direction.CW);
            result[0] = resultPath;
            return result;
        }


        Path[] result = new Path[1];
        Path resultPath = new Path();
        // 对位置进行偏移线宽的一半,因为直接画线的话,有一半是画到画布外的,
        // 但是因为有圆角,圆角后面还有画布,导致角的线宽比边的线宽要宽
        // 矩形缩小边框的一半
        float newWidth = solidWidth / 2.0f;
        if (((type & TYPE_OVAL_LEFT) != 0) && (type & TYPE_OVAL_RIGHT) != 0) {
            // 左边半圆
            RectF leftRectF = new RectF();
            leftRectF.set(newWidth, newWidth, height, height - newWidth);
            resultPath.addArc(leftRectF, 90, 180);
            // 增加上边的线
            resultPath.lineTo(width - height / 2f, newWidth);
            // 右边半圆
            RectF rightRectF = new RectF();
            rightRectF.set(width - height, newWidth, width - newWidth, height - newWidth);
            resultPath.addArc(rightRectF, -90, 180);
            // 增加下边的线
            resultPath.lineTo(height / 2f, height - newWidth);
        } else if (((type & TYPE_OVAL_TOP) != 0) && (type & TYPE_OVAL_BOTTOM) != 0) {
            // 上边半圆
            RectF leftRectF = new RectF();
            leftRectF.set(newWidth, newWidth, width - newWidth, width);
            resultPath.addArc(leftRectF, 180, 180);
            // 增加右边的线
            resultPath.lineTo(width - newWidth, height - width / 2f);
            // 下边半圆
            RectF rightRectF = new RectF();
            rightRectF.set(newWidth, height - width, width - newWidth, height - newWidth);
            resultPath.addArc(rightRectF, 0, 180);
            // 增加左边的线
            resultPath.lineTo(newWidth, width / 2f);
        } else if ((type & TYPE_OVAL_LEFT) != 0) {
            // 左半圆
            RectF arcRectF = new RectF();
            arcRectF.set(newWidth, newWidth, height, height - newWidth);
            resultPath.addArc(arcRectF, 90, 180);
            // 上边的线
            resultPath.lineTo(width - rightTopRadius, newWidth);
            // 右上角
            resultPath.quadTo(width, newWidth, width - newWidth, rightTopRadius);
            // 右边线
            resultPath.lineTo(width - newWidth, height - rightBottomRadius);
            // 右下角
            resultPath.quadTo(width - newWidth, height - newWidth, width - rightBottomRadius, height - newWidth);
            // 下边的线
            resultPath.lineTo(height / 2f, height - newWidth);
        } else if ((type & TYPE_OVAL_RIGHT) != 0) {
            // 上边的线
            resultPath.moveTo(leftTopRadius, newWidth);
            resultPath.lineTo(width - height / 2f, newWidth);
            // 右边半圆
            RectF rightRectF = new RectF();
            rightRectF.set(width - height, newWidth, width - newWidth, height - newWidth);
            resultPath.addArc(rightRectF, -90, 180);
            // 下边线
            resultPath.lineTo(leftBottomRadius, height - newWidth);
            // 左下角
            resultPath.quadTo(newWidth, height - newWidth, newWidth, height - leftBottomRadius);
            // 左边线
            resultPath.lineTo(newWidth, leftTopRadius);
            // 左上角
            resultPath.quadTo(newWidth, newWidth, leftTopRadius, newWidth);
        } else if ((type & TYPE_OVAL_TOP) != 0) {
            // 上半圆
            RectF arcRectF = new RectF();
            arcRectF.set(newWidth, newWidth, width - newWidth, width);
            resultPath.addArc(arcRectF, 180, 180);
            // 右边的线
            resultPath.lineTo(width - newWidth, height - rightBottomRadius);
            // 右下角
            resultPath.quadTo(width - newWidth, height - newWidth, width - rightBottomRadius, height - newWidth);
            // 底部线
            resultPath.lineTo(leftBottomRadius, height - newWidth);
            // 左下角
            resultPath.quadTo(newWidth, height - newWidth, newWidth, height - leftBottomRadius);
            // 左边的线
            resultPath.lineTo(newWidth, width / 2f);
        } else if ((type & TYPE_OVAL_BOTTOM) != 0) {
            // 上边线
            resultPath.moveTo(leftTopRadius, newWidth);
            resultPath.lineTo(width - rightTopRadius, newWidth);
            // 右上角
            resultPath.quadTo(width, newWidth, width - newWidth, rightTopRadius);
            // 右边线
            resultPath.lineTo(width - newWidth, height - width / 2f);
            // 下半圆
            RectF rightRectF = new RectF();
            rightRectF.set(newWidth, height - width, width - newWidth, height - newWidth);
            resultPath.addArc(rightRectF, 0, 180);
            // 左边线
            resultPath.lineTo(newWidth, leftTopRadius);
            // 左上角
            resultPath.quadTo(newWidth, newWidth, leftTopRadius, newWidth);
        } else {
            return calculateRadiusSocketPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, solidWidth);
        }

        result[0] = resultPath;
        return result;
    }

    /**
     * 计算圆角矩形的边框路径
     */
    private static Path[] calculateRadiusSocketPath(int leftTopRadius, int rightTopRadius,
                                                    int leftBottomRadius, int rightBottomRadius,
                                                    int width, int height, int solidWidth) {
        Path[] result = new Path[2];
        Path solidPath = new Path();
        Path radiusPath = new Path();

        float leftTopRadiusLeft, leftTopRadiusTop; // 左上角
        float leftBottomRadiusLeft, leftBottomRadiusBottom; // 左下角
        float rightTopRadiusRight, rightTopRadiusTop; // 右上角
        float rightBottomRadiusRight, rightBottomRadiusBottom; // 右下角
        int[] sideTop = calculateRadiusLength(leftTopRadius, rightTopRadius, width); // 上同边
        int[] sideBottom = calculateRadiusLength(leftBottomRadius, rightBottomRadius, width); // 下同边
        int[] sideLeft = calculateRadiusLength(leftTopRadius, leftBottomRadius, height); // 左同边
        int[] sideRight = calculateRadiusLength(rightTopRadius, rightBottomRadius, height); // 右同边
        leftTopRadiusTop = sideTop[0];
        rightTopRadiusTop = sideTop[1];
        leftBottomRadiusBottom = sideBottom[0];
        rightBottomRadiusBottom = sideBottom[1];
        leftTopRadiusLeft = sideLeft[0];
        leftBottomRadiusLeft = sideLeft[1];
        rightTopRadiusRight = sideRight[0];
        rightBottomRadiusRight = sideRight[1];

        // 对位置进行偏移线宽的一半,因为直接画线的话,有一半是画到画布外的,
        // 但是因为有圆角,圆角后面还有画布,导致角的线宽比边的线宽要宽
        float newWidth = solidWidth / 2.0f;
        // 四条边路径
        solidPath.moveTo(leftTopRadiusTop, newWidth);
        solidPath.lineTo(width - rightTopRadiusTop, newWidth);

        solidPath.moveTo(width - newWidth, rightTopRadiusRight);
        solidPath.lineTo(width - newWidth, height - rightBottomRadiusRight);

        solidPath.moveTo(width - rightBottomRadiusBottom, height - newWidth);
        solidPath.lineTo(leftBottomRadiusBottom, height - newWidth);

        solidPath.moveTo(newWidth, height - leftBottomRadiusLeft);
        solidPath.lineTo(newWidth, leftTopRadiusLeft);

        // 四个角路径
        radiusPath.moveTo(newWidth, leftTopRadiusLeft);
        radiusPath.quadTo(newWidth, newWidth, leftTopRadiusTop, newWidth);

        radiusPath.moveTo(width - rightTopRadiusTop, newWidth);
        radiusPath.quadTo(width, newWidth, width - newWidth, rightTopRadiusRight);

        radiusPath.moveTo(width - newWidth, height - rightBottomRadiusRight);
        radiusPath.quadTo(width - newWidth, height - newWidth, width - rightBottomRadiusBottom, height - newWidth);

        radiusPath.moveTo(leftBottomRadiusBottom, height - newWidth);
        radiusPath.quadTo(newWidth, height - newWidth, newWidth, height - leftBottomRadiusLeft);

        result[0] = solidPath;
        result[1] = radiusPath;
        return result;
    }

    /**
     * 计算直角矩形边框路径
     *
     * @param width      宽
     * @param height     高
     * @param solidWidth 线宽
     * @return
     */
    public static Path calculateRectSocketPath(int width, int height, int solidWidth) {
        float newWidth = solidWidth / 2.0f;
        Path result = new Path();
        result.moveTo(newWidth, newWidth);
        result.lineTo(width - newWidth, newWidth);
        result.lineTo(width - newWidth, height - newWidth);
        result.lineTo(newWidth, height - newWidth);
        result.close();
        return result;
    }

    /**
     * 根据圆角大小和边框长度将圆角矩形作为什么图形处理
     *
     * @return TYPE_CIRCLE:圆形  TYPE_RADIUS:圆角矩形  TYPE_OVAL:两端作为椭圆
     */
    private static int getType(int leftTopRadius, int rightTopRadius,
                               int leftBottomRadius, int rightBottomRadius,
                               int width, int height) {

        // 没有圆角
        if (leftTopRadius <= 0 && rightTopRadius <= 0 && leftBottomRadius <= 0 && rightBottomRadius <= 0)
            return TYPE_NONE;

        boolean topOval = leftTopRadius + rightTopRadius >= width;
        boolean bottomOval = leftBottomRadius + rightBottomRadius >= width;
        boolean leftOval = leftTopRadius + leftBottomRadius >= height;
        boolean rightOval = rightTopRadius + rightBottomRadius >= height;

        // 变为原型
        if (topOval && bottomOval && leftOval && rightOval)
            return TYPE_CIRCLE;

        // 变为圆角矩形
        if (!topOval && !bottomOval && !leftOval && !rightOval)
            return TYPE_RADIUS;
        if ((topOval || bottomOval) && (leftOval || rightOval)) return TYPE_RADIUS;

        // 处理半圆
        int result = TYPE_NONE;
        if (topOval) {
            result |= TYPE_OVAL_TOP;
        }

        if (bottomOval) {
            result |= TYPE_OVAL_BOTTOM;
        }

        if (leftOval) {
            result |= TYPE_OVAL_LEFT;
        }

        if (rightOval) {
            result |= TYPE_OVAL_RIGHT;
        }

        return result;
    }

    /**
     * 根据同边的两个圆角的分别长度和边的长度,重新计算两个圆角该有的长度(防止两个圆角的同边长度之后大于总的长度)<br/>
     * 如:给出左上角上边的长度和右上角上边的长度,以及矩形的上边边长(矩形的长),重新计算出左上角上边的长度和右上角上边的长度,
     * 防止左上角上边的长度和右上角上边的长度之和大于边长导致出错,如果大于边长时,根据比例计算。
     *
     * @param sameSide1     同边第一个值的原大小
     * @param sameSide2     同边第二个值的原大小
     * @param sameSideWidth 同边长度
     * @return int[],长度为2,int[0]:同边第一个值的最终大小 int[1]:同边第二个值的最终大小
     */
    private static int[] calculateRadiusLength(int sameSide1, int sameSide2, int sameSideWidth) {
        int[] result = new int[2];
        if (sameSide1 > 0 && sameSide2 > 0) {
            int topRadiusWidth = sameSide1 + sameSide2;
            if (topRadiusWidth > sameSideWidth) {
                result[0] = (int) ((sameSide1 * 1.0 / topRadiusWidth) * sameSideWidth);
                result[1] = (int) ((sameSide2 * 1.0 / topRadiusWidth) * sameSideWidth);
            } else {
                result[0] = sameSide1;
                result[1] = sameSide2;
            }
        } else if (sameSide1 > 0) {
            result[0] = Math.min(sameSide1, sameSideWidth);
        } else if (sameSide2 > 0) {
            result[1] = Math.min(sameSide2, sameSideWidth);
        }
        return result;
    }
}

4. 解决锯齿问题  自定义Drawable

RadiusDrawable

public class RadiusDrawable extends Drawable {
    // 背景颜色相关
    private ColorStateList mBgColorsList;
    private Shader mBgShader; // 渐变优先级更高
    private Path mBgPath;
    private Paint mBgPaint;
    private PorterDuffColorFilter mBgTintFilter;
    private ColorStateList mBgTint;
    private PorterDuff.Mode mBgTintMode;
    // 边框线相关
    private boolean mDrawSolid;
    private ColorStateList mSolidColorsList;
    private Shader mSolidShader; // 渐变优先级更高
    private List<Path> mSolidPath;
    private Paint mSolidPaint;
    private PorterDuffColorFilter mSolidTintFilter;
    private ColorStateList mSolidTint;
    private PorterDuff.Mode mSolidTintMode;

    public RadiusDrawable(ColorStateList bgColorsList, Shader bgShader, Path path) {
        this(bgColorsList, bgShader, path, 0, null, null, null, null);
    }

    public RadiusDrawable(ColorStateList bgColorsList, Shader bgShader, Path path, int solidWidth, ColorStateList solidColorsList,
                          Shader solidShader, List<Path> solidPath, DashPathEffect dashPathEffect) {
        this.mBgPath = path;
        this.mBgTintMode = PorterDuff.Mode.SRC_IN;
        this.mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        this.mBgPaint.setDither(true);
        this.mBgShader = bgShader;

        this.mDrawSolid = solidWidth > 0;
        if (mDrawSolid) {
            this.mSolidPath = solidPath == null ? new ArrayList<Path>() : solidPath;
            this.mSolidTintMode = PorterDuff.Mode.SRC_IN;
            this.mSolidPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            this.mSolidPaint.setAntiAlias(true);
            this.mSolidPaint.setDither(true);
            this.mSolidPaint.setStyle(Paint.Style.STROKE);
            this.mSolidPaint.setStrokeWidth(solidWidth);
            this.mSolidPaint.setPathEffect(dashPathEffect);
            this.mSolidShader = solidShader;
        }

        this.setBackground(bgColorsList, solidColorsList);
    }

    void setBgShader(Shader shader) {
        if (shader != null) {
            this.mBgShader = shader;
            this.invalidateSelf();
        }
    }

    void setBackground(ColorStateList bgColorsList, ColorStateList solidColorsList) {
        if (mBgShader == null) {
            this.mBgColorsList = bgColorsList == null ? ColorStateList.valueOf(0) : bgColorsList;
            this.mBgPaint.setColor(this.mBgColorsList.getColorForState(this.getState(), this.mBgColorsList.getDefaultColor()));
        }
        if (mDrawSolid && mSolidShader == null) {
            this.mSolidColorsList = solidColorsList == null ? ColorStateList.valueOf(0) : solidColorsList;
            this.mSolidPaint.setColor(this.mSolidColorsList.getColorForState(this.getState(), this.mSolidColorsList.getDefaultColor()));
        }
    }

    @Override
    public void draw(Canvas canvas) {
        Paint bgPaint = this.mBgPaint;
        boolean bgClearColorFilter = false;
        boolean hasBgShader = mBgShader != null;
        if (hasBgShader) {
            bgPaint.setShader(mBgShader);
        } else {
            if (this.mBgTintFilter != null) {
                bgPaint.setColorFilter(this.mBgTintFilter);
                bgClearColorFilter = true;
            } else {
                bgClearColorFilter = false;
            }
        }
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
        canvas.drawPath(mBgPath, bgPaint);
        if (bgClearColorFilter) {
            bgPaint.setColorFilter(null);
        }


        if (mDrawSolid) {
            Paint solidPaint = this.mSolidPaint;
            boolean solidClearColorFilter = false;
            boolean hasSolidShader = mSolidShader != null;
            if (hasSolidShader) {
                solidPaint.setShader(mSolidShader);
            } else {
                if (this.mSolidTintFilter != null) {
                    solidPaint.setColorFilter(this.mSolidTintFilter);
                    solidClearColorFilter = true;
                } else {
                    solidClearColorFilter = false;
                }
            }

            for (Path solidPath : mSolidPath) {
                canvas.drawPath(solidPath, solidPaint);
            }
            if (solidClearColorFilter) {
                solidPaint.setColorFilter(null);
            }
        }

    }

    @Override
    public void setAlpha(int alpha) {
        if (mBgShader == null) {
            this.mBgPaint.setAlpha(alpha);
        }

        if (mDrawSolid) {
            this.mSolidPaint.setAlpha(alpha);
        }
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        if (mBgShader == null) {
            this.mBgPaint.setColorFilter(cf);
        }

        if (mDrawSolid) {
            this.mSolidPaint.setColorFilter(cf);
        }
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    public void setColor(@Nullable ColorStateList backgroundColor, @Nullable ColorStateList solidColorsList) {
        this.setBackground(backgroundColor, solidColorsList);
        this.invalidateSelf();
    }

    public ColorStateList getBackGroundColor() {
        return this.mBgColorsList;
    }

    public ColorStateList getSolidColor() {
        return this.mSolidColorsList;
    }

    @Override
    public void setTintList(ColorStateList tint) {
        if (mBgShader == null) {
            this.mBgTint = tint;
            this.mBgTintFilter = this.createTintFilter(this.mBgTint, this.mBgTintMode);
        }

        if (mDrawSolid) {
            this.mSolidTint = tint;
            this.mSolidTintFilter = this.createTintFilter(this.mSolidTint, this.mSolidTintMode);
        }

        this.invalidateSelf();
    }

    @Override
    public void setTintMode(PorterDuff.Mode tintMode) {
        if (mBgShader == null) {
            this.mBgTintMode = tintMode;
            this.mBgTintFilter = this.createTintFilter(this.mBgTint, this.mSolidTintMode);
        }

        if (mDrawSolid && mSolidShader == null) {
            this.mSolidTintMode = tintMode;
            this.mSolidTintFilter = this.createTintFilter(this.mBgTint, this.mSolidTintMode);
        }

        this.invalidateSelf();
    }

    @Override
    protected boolean onStateChange(int[] stateSet) {
        boolean bgColorChanged = false;
        if (mBgShader == null) {
            int newBgColor = this.mBgColorsList.getColorForState(stateSet, this.mBgColorsList.getDefaultColor());
            bgColorChanged = newBgColor != this.mBgPaint.getColor();
            if (bgColorChanged) {
                this.mBgPaint.setColor(newBgColor);
            }
        }

        boolean solidColorChanged = false;
        if (mDrawSolid && mSolidShader == null) {
            int newSolidColor = this.mSolidColorsList.getColorForState(stateSet, this.mSolidColorsList.getDefaultColor());
            solidColorChanged = newSolidColor != this.mSolidPaint.getColor();
            if (solidColorChanged) {
                this.mSolidPaint.setColor(newSolidColor);
            }
        }


        if (this.mBgShader == null && this.mBgTint != null && this.mBgTintMode != null) {
            this.mBgTintFilter = this.createTintFilter(this.mBgTint, this.mBgTintMode);
            this.invalidateSelf();
            return true;
        } else if (this.mSolidShader == null && this.mSolidTint != null && this.mSolidTintMode != null) {
            this.mSolidTintFilter = this.createTintFilter(this.mSolidTint, this.mSolidTintMode);
            return true;
        } else {
            return bgColorChanged || solidColorChanged;
        }
    }

    @Override
    public boolean isStateful() {
        if (mDrawSolid) {
            return this.mBgTint != null && this.mBgTint.isStateful()
                    || this.mBgColorsList != null && this.mBgColorsList.isStateful()
                    || this.mSolidTint != null && this.mSolidTint.isStateful()
                    || this.mSolidColorsList != null && this.mSolidColorsList.isStateful()
                    || super.isStateful();
        } else {
            return this.mBgTint != null && this.mBgTint.isStateful()
                    || this.mBgColorsList != null && this.mBgColorsList.isStateful()
                    || super.isStateful();
        }
    }

    private PorterDuffColorFilter createTintFilter(ColorStateList tint, PorterDuff.Mode tintMode) {
        if (tint != null && tintMode != null) {
            int color = tint.getColorForState(this.getState(), 0);
            return new PorterDuffColorFilter(color, tintMode);
        } else {
            return null;
        }
    }
}

4.1创建attrs自定义属性

 <!--圆角控件属性-->
    <declare-styleable name="RadiusView">
        <!--圆角属性-->
        <attr name="rv_radius_all" format="dimension" />
        <attr name="rv_radius_leftTop" format="dimension" />
        <attr name="rv_radius_rightTop" format="dimension" />
        <attr name="rv_radius_rightBottom" format="dimension" />
        <attr name="rv_radius_leftBottom" format="dimension" />

        <!--背景颜色,系统的background将会失效,和 rv_shader_xxx 属性为互斥,rv_shader_xxx 优先级更高-->
        <attr name="rv_background_color" format="color" />
        <!--背景颜色,使用渐变,系统的background将会失效,和 rv_background_color 属性为互斥,rv_shader_xxx 优先级更高-->
        <attr name="rv_shader_start_color" format="color" />
        <attr name="rv_shader_middle_color" format="color" />
        <attr name="rv_shader_middle_color2" format="color" />
        <attr name="rv_shader_middle_color3" format="color" />
        <attr name="rv_shader_end_color" format="color" />
        <!--背景颜色,使用渐变,渐变类型(如果使用渐变,此为必须设置属性,否则不是使用渐变)。系统的background将会失效-->
        <attr name="rv_shader_type" format="enum">
            <enum name="linear" value="10" />
            <enum name="radial" value="11" />
            <enum name="sweep" value="12" />
        </attr>
        <!--背景颜色,使用渐变,线性渐变特有属性,渐变方向。系统的background将会失效-->
        <attr name="rv_shader_linear_orientation" format="enum">
            <enum name="top_to_bottom" value="110" />
            <enum name="bottom_to_top" value="111" />
            <enum name="left_to_right" value="220" />
            <enum name="right_to_left" value="221" />
            <enum name="leftTop_to_rightBottom" value="330" />
            <enum name="rightBottom_to_leftTop" value="331" />
            <enum name="rightTop_to_leftBottom" value="440" />
            <enum name="leftBottom_to_rightTop" value="441" />
        </attr>

        <!--边框属性-->
        <attr name="rv_solid_width" format="dimension" />
        <attr name="rv_solid_color" format="color" />
        <!--虚线长度-->
        <attr name="rv_solid_dashWidth" format="dimension" />
        <!--线间隔-->
        <attr name="rv_solid_dashGap" format="dimension" />
        <!--线类型 实线/虚线-->
        <attr name="rv_solid_type" format="enum">
            <!--水平线-->
            <enum name="solid" value="0" />
            <!--垂直线-->
            <enum name="dash" value="1" />
        </attr>
        <!--边框颜色,使用渐变,系统的background将会失效,和 rv_background_color 属性为互斥,rv_shader_xxx 优先级更高-->
        <attr name="rv_solid_shader_start_color" format="color" />
        <attr name="rv_solid_shader_middle_color" format="color" />
        <attr name="rv_solid_shader_middle_color2" format="color" />
        <attr name="rv_solid_shader_middle_color3" format="color" />
        <attr name="rv_solid_shader_end_color" format="color" />
        <!--边框颜色,使用渐变,渐变类型(如果使用渐变,此为必须设置属性,否则不是使用渐变)。系统的background将会失效-->
        <attr name="rv_solid_shader_type" format="enum">
            <enum name="linear" value="10" />
            <enum name="radial" value="11" />
            <enum name="sweep" value="12" />
        </attr>
        <!--边框颜色,使用渐变,线性渐变特有属性,渐变方向。系统的background将会失效-->
        <attr name="rv_solid_shader_linear_orientation" format="enum">
            <enum name="top_to_bottom" value="110" />
            <enum name="bottom_to_top" value="111" />
            <enum name="left_to_right" value="220" />
            <enum name="right_to_left" value="221" />
            <enum name="leftTop_to_rightBottom" value="330" />
            <enum name="rightBottom_to_leftTop" value="331" />
            <enum name="rightTop_to_leftBottom" value="440" />
            <enum name="leftBottom_to_rightTop" value="441" />
        </attr>
    </declare-styleable>

4.2创建自动适配布局  公共接口IAutoLayout

public interface IAutoLayout {
    int AUTO_TYPE_WIDTH = 0;
    int AUTO_TYPE_HEIGHT = 1;

    @IntDef(value = {AUTO_TYPE_WIDTH, AUTO_TYPE_HEIGHT})
    @interface AutoType {
    }

    /**
     * 设置控件宽高信息,并重新布局
     *
     * @param autoWidth
     * @param autoHeight
     */
    void setAutoViewInfo(int autoWidth, int autoHeight);

    /**
     * 设置控件宽高信息,并重新布局
     *
     * @param autoWidth
     * @param autoHeight
     */
    void setAutoViewInfo(@AutoType int autoType, int autoWidth, int autoHeight);
}

4.3创建账单适配布局AutoRelativeLayout

public class AutoRelativeLayout extends RelativeLayout implements IAutoLayout {
    // 自动适配的类型,0:宽适配 1:高适配
    private int auto_type = 1;
    private int auto_width;
    private int auto_height;

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

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

    public AutoRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public AutoRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    protected void init(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AutoWidthHeightView);
        auto_height = typedArray.getInt(R.styleable.AutoWidthHeightView_auto_view_height, 0);
        auto_width = typedArray.getInt(R.styleable.AutoWidthHeightView_auto_view_width, 0);
        auto_type = typedArray.getInt(R.styleable.AutoWidthHeightView_auto_view_type, -1);
        typedArray.recycle();
    }

    /**
     * @docRoot
     */
    @Override
    public void setAutoViewInfo(int autoWidth, int autoHeight) {
        this.auto_width = autoWidth;
        this.auto_height = autoHeight;
        requestLayout();
    }

    /**
     * @docRoot
     */
    @Override
    public void setAutoViewInfo(@AutoType int autoType, int autoWidth, int autoHeight) {
        this.auto_type = autoType;
        this.auto_width = autoWidth;
        this.auto_height = autoHeight;
        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (auto_type == -1) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        } else {
            setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));
            measureChildren(widthMeasureSpec, heightMeasureSpec);
            int viewWidth = getMeasuredWidth();
            int viewHeight = getMeasuredHeight();
            switch (auto_type) {
                case 0: // 动态计算出控件宽
                    viewWidth = (int) (viewHeight * ((auto_width * 1.0f) / auto_height));
                    break;
                case 1: // 动态计算出控件高
                    viewHeight = (int) (viewWidth * ((auto_height * 1.0f) / auto_width));
                    break;
            }
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY);
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}

其他AutoLinearLayout,AutoImageView,AutoFrameLayout布局

都是一样的

4.4创建自定义布局适配attrs

 <!--通过宽高比例自动适配的控件属性-->
    <declare-styleable name="AutoWidthHeightView">
        <attr name="auto_view_width" format="integer" />
        <attr name="auto_view_height" format="integer" />
        <attr name="auto_view_type" format="enum">
            <enum name="auto_view_width" value="0" />
            <enum name="auto_view_height" value="1" />
        </attr>
    </declare-styleable>

5.圆角相对布局 RadiusRelativeLayout

public class RadiusRelativeLayout extends AutoRelativeLayout implements IRadiusLayout {
    // 控件宽高
    private int width, height;
    // 圆角参数
    private int leftTopRadius;
    private int rightTopRadius;
    private int rightBottomRadius;
    private int leftBottomRadius;

    private RadiusDrawable radiusDrawable;
    private ColorStateList bgColorStateList;
    // 渐变背景
    private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    private int bgShaderType; // 渐变类型
    private int bgShaderLinearOrientation; // 线性渐变方向

    // 边框参数
    private int solidWidth;
    private ColorStateList solidColorStateList;
    private DashPathEffect dashPathEffect = null;
    // 渐变边框
    private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    private int solidShaderType; // 边框类型
    private int solidShaderLinearOrientation; // 线性渐变方向
    // 是否需要强制重新布局
    private boolean forceRefreshLayout;

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

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

    public RadiusRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public RadiusRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    @Override
    protected void init(Context context, AttributeSet attrs) {
        super.init(context, attrs);
        setWillNotDraw(false);
        if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        // 读取圆角配置
        TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
        int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
        leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
        rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
        rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
        leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);

        // 获取背景信息
        bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
        int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int middleColor2 = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color2, ShaderUtils.COLOR_VALUE_NONE);
        int middleColor3 = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color3, ShaderUtils.COLOR_VALUE_NONE);
        int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, middleColor2, middleColor3, endColor);
        if (bgShaderColors == null)
            bgShaderType = ShaderUtils.SHADER_TYPE_NONE;


        // 获取边框信息
        solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
        solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
        int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
        int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
        int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
        int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidMiddleColor2 = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color2, ShaderUtils.COLOR_VALUE_NONE);
        int solidMiddleColor3 = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color3, ShaderUtils.COLOR_VALUE_NONE);
        int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidMiddleColor2, solidMiddleColor3, solidEndColor);
        if (solidShaderColors == null)
            solidShaderType = ShaderUtils.SHADER_TYPE_NONE;

        radiusType.recycle();

        // 角度边长不能小于0
        if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
        //如果四个角的值没有设置,那么就使用通用的radius的值。
        if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
        if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
        if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
        if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;


        if (bgColorStateList == null) {
            bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }
        if (solidColorStateList == null) {
            solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }

        if (lineType == SOLID_TYPE_DASH) {
            dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
        } else {
            dashPathEffect = null;
        }
    }

    @Override
    public void setBackgroundColor(int color) {
        this.bgColorStateList = ColorStateList.valueOf(color);
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setBackgroundColor(ColorStateList bgColorStateList) {
        this.bgColorStateList = bgColorStateList;
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
        if (dashPathEffect != null) {
            this.dashPathEffect = dashPathEffect;
            forceRefreshLayout();
        }
    }

    @Override
    public void setSolidColor(int color) {
        this.solidColorStateList = ColorStateList.valueOf(color);
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidColor(ColorStateList solidColorStateList) {
        this.solidColorStateList = solidColorStateList;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setRadius(int radius) {
        if (radius >= 0) {
            leftTopRadius = radius;
            rightTopRadius = radius;
            rightBottomRadius = radius;
            leftBottomRadius = radius;
            forceRefreshLayout();
        }
    }

    @Override
    public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
        this.leftTopRadius = leftTopRadius;
        this.rightTopRadius = rightTopRadius;
        this.rightBottomRadius = rightBottomRadius;
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftTopRadius(int leftTopRadius) {
        this.leftTopRadius = leftTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightTopRadius(int rightTopRadius) {
        this.rightTopRadius = rightTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightBottomRadius(int rightBottomRadius) {
        this.rightBottomRadius = rightBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftBottomRadius(int leftBottomRadius) {
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.bgShaderType = shapeType;
        this.bgShaderColors = shapeColors;
        this.bgShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.solidShaderType = shapeType;
        this.solidShaderColors = shapeColors;
        this.solidShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // 没有发生改变,并且不需要强制刷新就不在重新layout
        if (!changed && !this.forceRefreshLayout) {
            return;
        }
        this.forceRefreshLayout = false;

        width = getWidth();
        height = getHeight();

        final Path bgPath = setBackground();

        // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            float elevation = Math.max(getElevation(), getTranslationZ());
            if (elevation > 0) {
                setElevation(elevation);
                setOutlineProvider(new ViewOutlineProvider() {
                    @Override
                    public void getOutline(View view, Outline outline) {
                        if (bgPath.isConvex()) {
                            outline.setConvexPath(bgPath);
                        } else {
                            outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                                    leftBottomRadius, rightBottomRadius, width, height, false));
                        }
                    }
                });
                setClipToOutline(true);
            }
        }
    }

    private void forceRefreshLayout() {
        this.forceRefreshLayout = true;
        requestLayout();
    }

    private Path setBackground() {
        Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                leftBottomRadius, rightBottomRadius, width, height);

        Shader bgShader = null;
        if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
            bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
        }

        // 边框
        if (solidWidth > 0) {
            Shader solidShader = null;
            if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
                solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
            }

            Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
                    leftBottomRadius, rightBottomRadius, width, height, solidWidth);
            List<Path> solidPath = Arrays.asList(solidPathArray);

            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
        } else {
            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
        }
        setBackground(radiusDrawable);
        return bgPath;
    }
}

6.圆角线性布局  RadiusLinearLayout

public class RadiusLinearLayout extends AutoLinearLayout implements IRadiusLayout {
    // 控件宽高
    private int width, height;
    // 圆角参数
    private int leftTopRadius;
    private int rightTopRadius;
    private int rightBottomRadius;
    private int leftBottomRadius;

    private RadiusDrawable radiusDrawable;
    private ColorStateList bgColorStateList;
    // 渐变背景
    private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    private int bgShaderType; // 渐变类型
    private int bgShaderLinearOrientation; // 线性渐变方向

    // 边框参数
    private int solidWidth;
    private ColorStateList solidColorStateList;
    private DashPathEffect dashPathEffect = null;
    // 渐变边框
    private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    private int solidShaderType; // 边框类型
    private int solidShaderLinearOrientation; // 线性渐变方向
    // 是否需要强制重新布局
    private boolean forceRefreshLayout;

    public RadiusLinearLayout(Context context) {
        this(context, null, 0);
    }

    public RadiusLinearLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RadiusLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public RadiusLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    @Override
    protected void init(Context context, AttributeSet attrs) {
        super.init(context, attrs);
        setWillNotDraw(false);
        if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        // 读取圆角配置
        TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
        int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
        leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
        rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
        rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
        leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);

        // 获取背景信息
        bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
        int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
        if (bgShaderColors == null)
            bgShaderType = ShaderUtils.SHADER_TYPE_NONE;


        // 获取边框信息
        solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
        solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
        int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
        int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
        int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
        int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
        if (solidShaderColors == null)
            solidShaderType = ShaderUtils.SHADER_TYPE_NONE;

        radiusType.recycle();

        // 角度边长不能小于0
        if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
        //如果四个角的值没有设置,那么就使用通用的radius的值。
        if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
        if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
        if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
        if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;


        if (bgColorStateList == null) {
            bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }
        if (solidColorStateList == null) {
            solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }

        if (lineType == SOLID_TYPE_DASH) {
            dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
        } else {
            dashPathEffect = null;
        }
    }

    @Override
    public void setBackgroundColor(int color) {
        this.bgColorStateList = ColorStateList.valueOf(color);
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setBackgroundColor(ColorStateList bgColorStateList) {
        this.bgColorStateList = bgColorStateList;
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
        if (dashPathEffect != null) {
            this.dashPathEffect = dashPathEffect;
            forceRefreshLayout();
        }
    }

    @Override
    public void setSolidColor(int color) {
        this.solidColorStateList = ColorStateList.valueOf(color);
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidColor(ColorStateList solidColorStateList) {
        this.solidColorStateList = solidColorStateList;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setRadius(int radius) {
        if (radius >= 0) {
            leftTopRadius = radius;
            rightTopRadius = radius;
            rightBottomRadius = radius;
            leftBottomRadius = radius;
            forceRefreshLayout();
        }
    }

    @Override
    public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
        this.leftTopRadius = leftTopRadius;
        this.rightTopRadius = rightTopRadius;
        this.rightBottomRadius = rightBottomRadius;
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftTopRadius(int leftTopRadius) {
        this.leftTopRadius = leftTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightTopRadius(int rightTopRadius) {
        this.rightTopRadius = rightTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightBottomRadius(int rightBottomRadius) {
        this.rightBottomRadius = rightBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftBottomRadius(int leftBottomRadius) {
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.bgShaderType = shapeType;
        this.bgShaderColors = shapeColors;
        this.bgShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.solidShaderType = shapeType;
        this.solidShaderColors = shapeColors;
        this.solidShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // 没有发生改变,并且不需要强制刷新就不在重新layout
        if (!changed && !this.forceRefreshLayout) {
            return;
        }
        this.forceRefreshLayout = false;

        width = getWidth();
        height = getHeight();

        final Path bgPath = setBackground();

        // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            float elevation = Math.max(getElevation(), getTranslationZ());
            if (elevation > 0) {
                setElevation(elevation);
                setOutlineProvider(new ViewOutlineProvider() {
                    @Override
                    public void getOutline(View view, Outline outline) {
                        if (bgPath.isConvex()) {
                            outline.setConvexPath(bgPath);
                        } else {
                            outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                                    leftBottomRadius, rightBottomRadius, width, height, false));
                        }
                    }
                });
                setClipToOutline(true);
            }
        }
    }

    private void forceRefreshLayout() {
        this.forceRefreshLayout = true;
        requestLayout();
    }

    private Path setBackground() {
        Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                leftBottomRadius, rightBottomRadius, width, height);

        Shader bgShader = null;
        if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
            bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
        }

        // 边框
        if (solidWidth > 0) {
            Shader solidShader = null;
            if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
                solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
            }

            Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
                    leftBottomRadius, rightBottomRadius, width, height, solidWidth);
            List<Path> solidPath = Arrays.asList(solidPathArray);

            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
        } else {
            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
        }
        setBackground(radiusDrawable);
        return bgPath;
    }
}

7.圆角TextView   RadiusTextView

public class RadiusTextView extends AppCompatTextView implements IRadiusLayout {
    // 控件宽高
    private int width, height;
    // 圆角参数
    private int leftTopRadius;
    private int rightTopRadius;
    private int rightBottomRadius;
    private int leftBottomRadius;

    private RadiusDrawable radiusDrawable;
    private ColorStateList bgColorStateList;
    // 渐变背景
    private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    private int bgShaderType; // 渐变类型
    private int bgShaderLinearOrientation; // 线性渐变方向

    // 边框参数
    private int solidWidth;
    private ColorStateList solidColorStateList;
    private DashPathEffect dashPathEffect = null;
    // 渐变边框
    private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    private int solidShaderType; // 边框类型
    private int solidShaderLinearOrientation; // 线性渐变方向
    // 是否需要强制重新布局
    private boolean forceRefreshLayout;

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

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

    public RadiusTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    protected void init(Context context, AttributeSet attrs) {
        if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        // 读取圆角配置
        TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
        int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
        leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
        rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
        rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
        leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);

        // 获取背景信息
        bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
        int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
        if (bgShaderColors == null)
            bgShaderType = ShaderUtils.SHADER_TYPE_NONE;


        // 获取边框信息
        solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
        solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
        int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
        int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
        int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
        int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
        if (solidShaderColors == null)
            solidShaderType = ShaderUtils.SHADER_TYPE_NONE;

        radiusType.recycle();

        // 角度边长不能小于0
        if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
        //如果四个角的值没有设置,那么就使用通用的radius的值。
        if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
        if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
        if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
        if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;


        if (bgColorStateList == null) {
            bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }
        if (solidColorStateList == null) {
            solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }

        if (lineType == SOLID_TYPE_DASH) {
            dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
        } else {
            dashPathEffect = null;
        }
    }

    @Override
    public void setBackgroundColor(int color) {
        this.bgColorStateList = ColorStateList.valueOf(color);
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setBackgroundColor(ColorStateList bgColorStateList) {
        this.bgColorStateList = bgColorStateList;
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
        if (dashPathEffect != null) {
            this.dashPathEffect = dashPathEffect;
            forceRefreshLayout();
        }
    }

    @Override
    public void setSolidColor(int color) {
        this.solidColorStateList = ColorStateList.valueOf(color);
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidColor(ColorStateList solidColorStateList) {
        this.solidColorStateList = solidColorStateList;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setRadius(int radius) {
        if (radius >= 0) {
            leftTopRadius = radius;
            rightTopRadius = radius;
            rightBottomRadius = radius;
            leftBottomRadius = radius;
            forceRefreshLayout();
        }
    }

    @Override
    public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
        this.leftTopRadius = leftTopRadius;
        this.rightTopRadius = rightTopRadius;
        this.rightBottomRadius = rightBottomRadius;
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftTopRadius(int leftTopRadius) {
        this.leftTopRadius = leftTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightTopRadius(int rightTopRadius) {
        this.rightTopRadius = rightTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightBottomRadius(int rightBottomRadius) {
        this.rightBottomRadius = rightBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftBottomRadius(int leftBottomRadius) {
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.bgShaderType = shapeType;
        this.bgShaderColors = shapeColors;
        this.bgShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.solidShaderType = shapeType;
        this.solidShaderColors = shapeColors;
        this.solidShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // 没有发生改变,并且不需要强制刷新就不在重新layout
        if (!changed && !this.forceRefreshLayout) {
            return;
        }
        this.forceRefreshLayout = false;

        width = getWidth();
        height = getHeight();

        final Path bgPath = setBackground();

        // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            float elevation = Math.max(getElevation(), getTranslationZ());
            if (elevation > 0) {
                setElevation(elevation);
                setOutlineProvider(new ViewOutlineProvider() {
                    @Override
                    public void getOutline(View view, Outline outline) {
                        if (bgPath.isConvex()) {
                            outline.setConvexPath(bgPath);
                        } else {
                            outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                                    leftBottomRadius, rightBottomRadius, width, height, false));
                        }
                    }
                });
                setClipToOutline(true);
            }
        }
    }

    private void forceRefreshLayout() {
        this.forceRefreshLayout = true;
        requestLayout();
    }

    private Path setBackground() {
        Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                leftBottomRadius, rightBottomRadius, width, height);

        Shader bgShader = null;
        if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
            bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
        }

        // 边框
        if (solidWidth > 0) {
            Shader solidShader = null;
            if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
                solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
            }

            Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
                    leftBottomRadius, rightBottomRadius, width, height, solidWidth);
            List<Path> solidPath = Arrays.asList(solidPathArray);

            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
        } else {
            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
        }
        setBackground(radiusDrawable);
        return bgPath;
    }
}

8.圆角Image  RadiusImageView

public class RadiusImageView extends AutoImageView {
    // 默认没有圆角
    private final int DEFAULT_RADIUS = 0;
    private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    private static final int COLOR_DRAWABLE_DIMENSION = 2;
    public static final int TYPE_SOLID = 0; // 实线
    public static final int TYPE_DASH = 1;  // 虚线

    // 控件宽高
    private int width, height;
    // 圆角参数
    private int leftTopRadius;
    private int rightTopRadius;
    private int rightBottomRadius;
    private int leftBottomRadius;

    // 画图片的画笔
    private Paint bitmapPaint;
    // 画边框的画笔
    private Paint solidPaint;
    // 3x3 矩阵,主要用于缩小放大
    private Matrix matrix;
    //渲染图像,使用图像为绘制图形着色
    private BitmapShader bitmapShader;

    // 边框参数
    private int solidWidth;
    private int solidColor;

    // 是否需要强制重新布局
    private boolean forceRefreshLayout;

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

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

    public RadiusImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @Override
    protected void init(Context context, AttributeSet attrs) {
        super.init(context, attrs);
        if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        // 读取圆角配置
        TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
        int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
        leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
        rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
        rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
        leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);

        solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
        solidColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_color, Color.TRANSPARENT);

        int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
        int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
        int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, TYPE_SOLID);
        radiusType.recycle();

        // 角度边长不能小于0
        if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
        //如果四个角的值没有设置,那么就使用通用的radius的值。
        if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
        if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
        if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
        if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;

        matrix = new Matrix();
        bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        bitmapPaint.setDither(true);

        solidPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        solidPaint.setDither(true);
        solidPaint.setStyle(Paint.Style.STROKE);
        solidPaint.setColor(solidColor);
        solidPaint.setStrokeWidth(solidWidth);
        if (lineType == TYPE_SOLID) {
            setLineTypeStyle(TYPE_SOLID, 0, 0, false);
        } else {
            setLineTypeStyle(TYPE_DASH, dashGap, dashWidth, false);
        }
    }

    // 设置线的类型和虚线样式
    private void setLineTypeStyle(int lineType, float dashGap, float dashWidth, boolean invalidate) {
        if (lineType == TYPE_DASH) {
            DashPathEffect dashPathEffect = null;
            if (dashWidth > 0) {
                dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
            }
            solidPaint.setPathEffect(dashPathEffect);
        } else {
            solidPaint.setPathEffect(null);
        }
        if (invalidate) invalidate();
    }

    public void setSolidColor(int color) {
        solidPaint.setColor(color);
        forceRefreshLayout();
    }

    public void setRadius(int radius) {
        if (radius >= 0) {
            leftTopRadius = radius;
            rightTopRadius = radius;
            rightBottomRadius = radius;
            leftBottomRadius = radius;
            forceRefreshLayout();
        }
    }

    public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
        this.leftTopRadius = leftTopRadius;
        this.rightTopRadius = rightTopRadius;
        this.rightBottomRadius = rightBottomRadius;
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    public void setLeftTopRadius(int leftTopRadius) {
        this.leftTopRadius = leftTopRadius;
        forceRefreshLayout();
    }

    public void setRightTopRadius(int rightTopRadius) {
        this.rightTopRadius = rightTopRadius;
        forceRefreshLayout();
    }

    public void setRightBottomRadius(int rightBottomRadius) {
        this.rightBottomRadius = rightBottomRadius;
        forceRefreshLayout();
    }

    public void setLeftBottomRadius(int leftBottomRadius) {
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // 没有发生改变,并且不需要强制刷新就不在重新layout
        if (!changed && !this.forceRefreshLayout) {
            return;
        }
        this.forceRefreshLayout = false;

        width = getWidth();
        height = getHeight();

        // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            float elevation = Math.max(getElevation(), getTranslationZ());
            if (elevation > 0) {
                setElevation(elevation);
                setOutlineProvider(new ViewOutlineProvider() {
                    @Override
                    public void getOutline(View view, Outline outline) {
                        Path path = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, false);
                        outline.setConvexPath(path);
                    }
                });
                setClipToOutline(true);
            }
        }
    }

    private void forceRefreshLayout() {
        this.forceRefreshLayout = true;
        requestLayout();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (leftTopRadius <= DEFAULT_RADIUS && leftBottomRadius <= DEFAULT_RADIUS &&
                rightTopRadius <= DEFAULT_RADIUS && rightBottomRadius <= DEFAULT_RADIUS) {
            super.onDraw(canvas);

            // 边框
            if (solidWidth > 0)
                canvas.drawPath(RadiusUtils.calculateRectSocketPath(width, height, solidWidth), solidPaint);
        } else {
            Path path = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height);
            Bitmap bitmap = getBitmapFromDrawable(getDrawable());
            if (bitmap != null) {
                bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
                configureBounds(getDrawable());
                // 设置变换矩阵
                bitmapShader.setLocalMatrix(matrix);
                // 设置shader
                bitmapPaint.setShader(bitmapShader);
                canvas.drawPath(path, bitmapPaint);
            }

            // 边框
            if (solidWidth > 0) {
                Path[] result = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius, leftBottomRadius, rightBottomRadius, width, height, solidWidth);
                canvas.drawPath(result[0], solidPaint);
                canvas.drawPath(result[1], solidPaint);
            }
        }
    }

    // 获取图片资源
    private Bitmap getBitmapFromDrawable(Drawable drawable) {
        if (drawable == null) {
            return null;
        }

        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        try {
            Bitmap bitmap;

            if (drawable instanceof ColorDrawable) {
                bitmap = Bitmap.createBitmap(COLOR_DRAWABLE_DIMENSION, COLOR_DRAWABLE_DIMENSION, BITMAP_CONFIG);
            } else {
                bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
            }

            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // 根据 ScaleType 进行矩阵变换
    private void configureBounds(Drawable drawable) {
        if (drawable == null) {
            return;
        }

        final ImageView.ScaleType scaleType = getScaleType();
        final int intrinsicWidth = drawable.getIntrinsicWidth();
        final int intrinsicHeight = drawable.getIntrinsicHeight();
        final int vWidth = width - getPaddingLeft() - getPaddingRight();
        final int vHeight = height - getPaddingTop() - getPaddingBottom();
        final boolean fits = (intrinsicWidth < 0 || vWidth == intrinsicWidth)
                && (intrinsicHeight < 0 || vHeight == intrinsicHeight);

        if (intrinsicWidth <= 0 || intrinsicHeight <= 0 || ImageView.ScaleType.FIT_XY == scaleType) {
            matrix = null;
        } else {
            if (ImageView.ScaleType.MATRIX == scaleType) {
                matrix = null;
            } else if (fits) {
                matrix = null;
            } else if (ImageView.ScaleType.CENTER == scaleType) {
                if (matrix == null)
                    matrix = new Matrix();
                matrix.setTranslate(Math.round((vWidth - intrinsicWidth) * 0.5f),
                        Math.round((vHeight - intrinsicHeight) * 0.5f));
            } else if (ImageView.ScaleType.CENTER_CROP == scaleType) {
                float scale;
                float dx = 0, dy = 0;

                if (intrinsicWidth * vHeight > vWidth * intrinsicHeight) {
                    scale = (float) vHeight / (float) intrinsicHeight;
                    dx = (vWidth - intrinsicWidth * scale) * 0.5f;
                } else {
                    scale = (float) vWidth / (float) intrinsicWidth;
                    dy = (vHeight - intrinsicHeight * scale) * 0.5f;
                }

                if (matrix == null)
                    matrix = new Matrix();
                matrix.setScale(scale, scale);
                matrix.postTranslate(Math.round(dx), Math.round(dy));
            } else if (ImageView.ScaleType.CENTER_INSIDE == scaleType) {
                float scale;
                float dx;
                float dy;

                if (intrinsicWidth <= vWidth && intrinsicHeight <= vHeight) {
                    scale = 1.0f;
                } else {
                    scale = Math.min((float) vWidth / (float) intrinsicWidth,
                            (float) vHeight / (float) intrinsicHeight);
                }

                dx = Math.round((vWidth - intrinsicWidth * scale) * 0.5f);
                dy = Math.round((vHeight - intrinsicHeight * scale) * 0.5f);

                if (matrix == null)
                    matrix = new Matrix();
                matrix.setScale(scale, scale);
                matrix.postTranslate(dx, dy);
            } else {
                RectF mTempSrc = new RectF();
                RectF mTempDst = new RectF();
                mTempSrc.set(0, 0, intrinsicWidth, intrinsicHeight);
                mTempDst.set(0, 0, vWidth, vHeight);
                if (matrix == null)
                    matrix = new Matrix();
                matrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.CENTER);
            }
        }
    }
}

9.圆角FrameLayout     RadiusFrameLayout

public class RadiusFrameLayout extends AutoFrameLayout implements IRadiusLayout{

    // 控件宽高
    private int width, height;
    // 圆角参数
    private int leftTopRadius;
    private int rightTopRadius;
    private int rightBottomRadius;
    private int leftBottomRadius;

    private RadiusDrawable radiusDrawable;
    private ColorStateList bgColorStateList;
    // 渐变背景
    private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    private int bgShaderType; // 渐变类型
    private int bgShaderLinearOrientation; // 线性渐变方向

    // 边框参数
    private int solidWidth;
    private ColorStateList solidColorStateList;
    private DashPathEffect dashPathEffect = null;
    // 渐变边框
    private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    private int solidShaderType; // 边框类型
    private int solidShaderLinearOrientation; // 线性渐变方向
    // 是否需要强制重新布局
    private boolean forceRefreshLayout;

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

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

    public RadiusFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public RadiusFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    @Override
    protected void init(Context context, AttributeSet attrs) {
        super.init(context, attrs);
        setWillNotDraw(false);
        if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        // 读取圆角配置
        TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
        int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
        leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
        rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
        rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
        leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);

        // 获取背景信息
        bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
        int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
        if (bgShaderColors == null)
            bgShaderType = ShaderUtils.SHADER_TYPE_NONE;


        // 获取边框信息
        solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
        solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
        int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
        int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
        int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
        int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
        if (solidShaderColors == null)
            solidShaderType = ShaderUtils.SHADER_TYPE_NONE;

        radiusType.recycle();

        // 角度边长不能小于0
        if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
        //如果四个角的值没有设置,那么就使用通用的radius的值。
        if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
        if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
        if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
        if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;


        if (bgColorStateList == null) {
            bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }
        if (solidColorStateList == null) {
            solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }

        if (lineType == SOLID_TYPE_DASH) {
            dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
        } else {
            dashPathEffect = null;
        }
    }

    @Override
    public void setBackgroundColor(int color) {
        this.bgColorStateList = ColorStateList.valueOf(color);
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setBackgroundColor(ColorStateList bgColorStateList) {
        this.bgColorStateList = bgColorStateList;
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
        if (dashPathEffect != null) {
            this.dashPathEffect = dashPathEffect;
            forceRefreshLayout();
        }
    }

    @Override
    public void setSolidColor(int color) {
        this.solidColorStateList = ColorStateList.valueOf(color);
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidColor(ColorStateList solidColorStateList) {
        this.solidColorStateList = solidColorStateList;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setRadius(int radius) {
        if (radius >= 0) {
            leftTopRadius = radius;
            rightTopRadius = radius;
            rightBottomRadius = radius;
            leftBottomRadius = radius;
            forceRefreshLayout();
        }
    }

    @Override
    public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
        this.leftTopRadius = leftTopRadius;
        this.rightTopRadius = rightTopRadius;
        this.rightBottomRadius = rightBottomRadius;
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftTopRadius(int leftTopRadius) {
        this.leftTopRadius = leftTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightTopRadius(int rightTopRadius) {
        this.rightTopRadius = rightTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightBottomRadius(int rightBottomRadius) {
        this.rightBottomRadius = rightBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftBottomRadius(int leftBottomRadius) {
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.bgShaderType = shapeType;
        this.bgShaderColors = shapeColors;
        this.bgShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.solidShaderType = shapeType;
        this.solidShaderColors = shapeColors;
        this.solidShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // 没有发生改变,并且不需要强制刷新就不在重新layout
        if (!changed && !this.forceRefreshLayout) {
            return;
        }
        this.forceRefreshLayout = false;

        width = getWidth();
        height = getHeight();

        final Path bgPath = setBackground();

        // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            float elevation = Math.max(getElevation(), getTranslationZ());
            if (elevation > 0) {
                setElevation(elevation);
                setOutlineProvider(new ViewOutlineProvider() {
                    @Override
                    public void getOutline(View view, Outline outline) {
                        if (bgPath.isConvex()) {
                            outline.setConvexPath(bgPath);
                        } else {
                            outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                                    leftBottomRadius, rightBottomRadius, width, height, false));
                        }
                    }
                });
                setClipToOutline(true);
            }
        }
    }

    private void forceRefreshLayout() {
        this.forceRefreshLayout = true;
        requestLayout();
    }

    private Path setBackground() {
        final Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                leftBottomRadius, rightBottomRadius, width, height);

        Shader bgShader = null;
        if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
            bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
        }
        // 边框
        if (solidWidth > 0) {
            Shader solidShader = null;
            if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
                solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
            }

            Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
                    leftBottomRadius, rightBottomRadius, width, height, solidWidth);
            List<Path> solidPath = Arrays.asList(solidPathArray);

            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
        } else {
            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
        }
        setBackground(radiusDrawable);
        return bgPath;
    }
}

10.圆角EditText    RadiusEditText

public class RadiusEditText extends ClearAbleEditText implements IRadiusLayout {
    // 控件宽高
    private int width, height;
    // 圆角参数
    private int leftTopRadius;
    private int rightTopRadius;
    private int rightBottomRadius;
    private int leftBottomRadius;

    private RadiusDrawable radiusDrawable;
    private ColorStateList bgColorStateList;
    // 渐变背景
    private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    private int bgShaderType; // 渐变类型
    private int bgShaderLinearOrientation; // 线性渐变方向

    // 边框参数
    private int solidWidth;
    private ColorStateList solidColorStateList;
    private DashPathEffect dashPathEffect = null;
    // 渐变边框
    private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    private int solidShaderType; // 边框类型
    private int solidShaderLinearOrientation; // 线性渐变方向
    // 是否需要强制重新布局
    private boolean forceRefreshLayout;

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

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

    public RadiusEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    protected void init(Context context, AttributeSet attrs) {
        setFocusableInTouchMode(true);
        if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        // 读取圆角配置
        TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
        int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
        leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
        rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
        rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
        leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);

        // 获取背景信息
        bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
        int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
        if (bgShaderColors == null)
            bgShaderType = ShaderUtils.SHADER_TYPE_NONE;


        // 获取边框信息
        solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
        solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
        int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
        int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
        int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
        int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
        if (solidShaderColors == null)
            solidShaderType = ShaderUtils.SHADER_TYPE_NONE;

        radiusType.recycle();

        // 角度边长不能小于0
        if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
        //如果四个角的值没有设置,那么就使用通用的radius的值。
        if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
        if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
        if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
        if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;


        if (bgColorStateList == null) {
            bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }
        if (solidColorStateList == null) {
            solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }

        if (lineType == SOLID_TYPE_DASH) {
            dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
        } else {
            dashPathEffect = null;
        }
    }

    @Override
    public void setBackgroundColor(int color) {
        this.bgColorStateList = ColorStateList.valueOf(color);
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setBackgroundColor(ColorStateList bgColorStateList) {
        this.bgColorStateList = bgColorStateList;
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
        if (dashPathEffect != null) {
            this.dashPathEffect = dashPathEffect;
            forceRefreshLayout();
        }
    }

    @Override
    public void setSolidColor(int color) {
        this.solidColorStateList = ColorStateList.valueOf(color);
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidColor(ColorStateList solidColorStateList) {
        this.solidColorStateList = solidColorStateList;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setRadius(int radius) {
        if (radius >= 0) {
            leftTopRadius = radius;
            rightTopRadius = radius;
            rightBottomRadius = radius;
            leftBottomRadius = radius;
            forceRefreshLayout();
        }
    }

    @Override
    public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
        this.leftTopRadius = leftTopRadius;
        this.rightTopRadius = rightTopRadius;
        this.rightBottomRadius = rightBottomRadius;
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftTopRadius(int leftTopRadius) {
        this.leftTopRadius = leftTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightTopRadius(int rightTopRadius) {
        this.rightTopRadius = rightTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightBottomRadius(int rightBottomRadius) {
        this.rightBottomRadius = rightBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftBottomRadius(int leftBottomRadius) {
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.bgShaderType = shapeType;
        this.bgShaderColors = shapeColors;
        this.bgShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.solidShaderType = shapeType;
        this.solidShaderColors = shapeColors;
        this.solidShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // 没有发生改变,并且不需要强制刷新就不在重新layout
        if (!changed && !this.forceRefreshLayout) {
            return;
        }
        this.forceRefreshLayout = false;

        width = getWidth();
        height = getHeight();

        final Path bgPath = setBackground();

        // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            float elevation = Math.max(getElevation(), getTranslationZ());
            if (elevation > 0) {
                setElevation(elevation);
                setOutlineProvider(new ViewOutlineProvider() {
                    @Override
                    public void getOutline(View view, Outline outline) {
                        if (bgPath.isConvex()) {
                            outline.setConvexPath(bgPath);
                        } else {
                            outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                                    leftBottomRadius, rightBottomRadius, width, height, false));
                        }
                    }
                });
                setClipToOutline(true);
            }
        }
    }

    private void forceRefreshLayout() {
        this.forceRefreshLayout = true;
        requestLayout();
    }

    private Path setBackground() {
        Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                leftBottomRadius, rightBottomRadius, width, height);

        Shader bgShader = null;
        if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
            bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
        }
        // 边框
        if (solidWidth > 0) {
            Shader solidShader = null;
            if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
                solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
            }

            Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
                    leftBottomRadius, rightBottomRadius, width, height, solidWidth);
            List<Path>  solidPath = Arrays.asList(solidPathArray);

            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
        } else {
            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
        }
        setBackground(radiusDrawable);
        return bgPath;
    }
}

11.圆角Button  RadiusButton

public class RadiusButton extends AppCompatButton implements IRadiusLayout {
    // 控件宽高
    private int width, height;
    // 圆角参数
    private int leftTopRadius;
    private int rightTopRadius;
    private int rightBottomRadius;
    private int leftBottomRadius;

    private RadiusDrawable radiusDrawable;
    private ColorStateList bgColorStateList;
    // 渐变背景
    private int[] bgShaderColors; // 背景渐变颜色值,优先级高于 bgColorStateList
    private int bgShaderType; // 渐变类型
    private int bgShaderLinearOrientation; // 线性渐变方向

    // 边框参数
    private int solidWidth;
    private ColorStateList solidColorStateList;
    private DashPathEffect dashPathEffect = null;
    // 渐变边框
    private int[] solidShaderColors; // 边框渐变颜色值,优先级高于 bgColorStateList
    private int solidShaderType; // 边框类型
    private int solidShaderLinearOrientation; // 线性渐变方向
    // 是否需要强制重新布局
    private boolean forceRefreshLayout;

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

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

    public RadiusButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    protected void init(Context context, AttributeSet attrs) {
        if (Build.VERSION.SDK_INT < 18) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        // 读取圆角配置
        TypedArray radiusType = context.obtainStyledAttributes(attrs, R.styleable.RadiusView);
        int radius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_all, DEFAULT_RADIUS);
        leftTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftTop, DEFAULT_RADIUS);
        rightTopRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightTop, DEFAULT_RADIUS);
        rightBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_rightBottom, DEFAULT_RADIUS);
        leftBottomRadius = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_radius_leftBottom, DEFAULT_RADIUS);

        // 获取背景信息
        bgColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_background_color);
        int startColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int middleColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int endColor = radiusType.getColor(R.styleable.RadiusView_rv_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        bgShaderType = radiusType.getInt(R.styleable.RadiusView_rv_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        bgShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        bgShaderColors = ShaderUtils.createColorsArray(startColor, middleColor, endColor);
        if (bgShaderColors == null)
            bgShaderType = ShaderUtils.SHADER_TYPE_NONE;


        // 获取边框信息
        solidWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_width, 0);
        solidColorStateList = radiusType.getColorStateList(R.styleable.RadiusView_rv_solid_color);
        int dashGap = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashGap, 0);
        int dashWidth = radiusType.getDimensionPixelSize(R.styleable.RadiusView_rv_solid_dashWidth, 0);
        int lineType = radiusType.getInt(R.styleable.RadiusView_rv_solid_type, SOLID_TYPE_SOLID);
        int solidStartColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_start_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidMiddleColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_middle_color, ShaderUtils.COLOR_VALUE_NONE);
        int solidEndColor = radiusType.getColor(R.styleable.RadiusView_rv_solid_shader_end_color, ShaderUtils.COLOR_VALUE_NONE);
        solidShaderType = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_type, ShaderUtils.SHADER_TYPE_NONE);
        solidShaderLinearOrientation = radiusType.getInt(R.styleable.RadiusView_rv_solid_shader_linear_orientation, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
        solidShaderColors = ShaderUtils.createColorsArray(solidStartColor, solidMiddleColor, solidEndColor);
        if (solidShaderColors == null)
            solidShaderType = ShaderUtils.SHADER_TYPE_NONE;

        radiusType.recycle();

        // 角度边长不能小于0
        if (DEFAULT_RADIUS >= radius) radius = DEFAULT_RADIUS;
        //如果四个角的值没有设置,那么就使用通用的radius的值。
        if (DEFAULT_RADIUS >= leftTopRadius) leftTopRadius = radius;
        if (DEFAULT_RADIUS >= rightTopRadius) rightTopRadius = radius;
        if (DEFAULT_RADIUS >= rightBottomRadius) rightBottomRadius = radius;
        if (DEFAULT_RADIUS >= leftBottomRadius) leftBottomRadius = radius;


        if (bgColorStateList == null) {
            bgColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }
        if (solidColorStateList == null) {
            solidColorStateList = ColorStateList.valueOf(Color.TRANSPARENT);
        }

        if (lineType == SOLID_TYPE_DASH) {
            dashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
        } else {
            dashPathEffect = null;
        }
    }

    @Override
    public void setBackgroundColor(int color) {
        this.bgColorStateList = ColorStateList.valueOf(color);
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setBackgroundColor(ColorStateList bgColorStateList) {
        this.bgColorStateList = bgColorStateList;
        this.bgShaderType = ShaderUtils.SHADER_TYPE_NONE;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidDashPathEffect(DashPathEffect dashPathEffect) {
        if (dashPathEffect != null) {
            this.dashPathEffect = dashPathEffect;
            forceRefreshLayout();
        }
    }

    @Override
    public void setSolidColor(int color) {
        this.solidColorStateList = ColorStateList.valueOf(color);
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(this.bgColorStateList, solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setSolidColor(ColorStateList solidColorStateList) {
        this.solidColorStateList = solidColorStateList;
        if (radiusDrawable != null) {
            radiusDrawable.setBackground(bgColorStateList, this.solidColorStateList);
        }
        forceRefreshLayout();
    }

    @Override
    public void setRadius(int radius) {
        if (radius >= 0) {
            leftTopRadius = radius;
            rightTopRadius = radius;
            rightBottomRadius = radius;
            leftBottomRadius = radius;
            forceRefreshLayout();
        }
    }

    @Override
    public void setRadius(int leftTopRadius, int rightTopRadius, int rightBottomRadius, int leftBottomRadius) {
        this.leftTopRadius = leftTopRadius;
        this.rightTopRadius = rightTopRadius;
        this.rightBottomRadius = rightBottomRadius;
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftTopRadius(int leftTopRadius) {
        this.leftTopRadius = leftTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightTopRadius(int rightTopRadius) {
        this.rightTopRadius = rightTopRadius;
        forceRefreshLayout();
    }

    @Override
    public void setRightBottomRadius(int rightBottomRadius) {
        this.rightBottomRadius = rightBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setLeftBottomRadius(int leftBottomRadius) {
        this.leftBottomRadius = leftBottomRadius;
        forceRefreshLayout();
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.bgShaderType = shapeType;
        this.bgShaderColors = shapeColors;
        this.bgShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors) {
        setSolidShaderInfo(shapeType, shapeColors, ShaderUtils.LINEAR_ORIENTATION_TOP_TO_BOTTOM);
    }

    @Override
    public void setSolidShaderInfo(@ShaderUtils.ShaderType int shapeType, int[] shapeColors, @ShaderUtils.LinearOrientation int shaderLinearOrientation) {
        if (shapeColors == null || shapeColors.length <= 0)
            return;
        this.solidShaderType = shapeType;
        this.solidShaderColors = shapeColors;
        this.solidShaderLinearOrientation = shaderLinearOrientation;
        forceRefreshLayout();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // 没有发生改变,并且不需要强制刷新就不在重新layout
        if (!changed && !this.forceRefreshLayout) {
            return;
        }
        this.forceRefreshLayout = false;

        width = getWidth();
        height = getHeight();

        final Path bgPath = setBackground();

        // 手动设置阴影,使用裁剪后的路径,防止阴影直角矩形显示
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            float elevation = Math.max(getElevation(), getTranslationZ());
            if (elevation > 0) {
                setElevation(elevation);
                setOutlineProvider(new ViewOutlineProvider() {
                    @Override
                    public void getOutline(View view, Outline outline) {
                        if (bgPath.isConvex()) {
                            outline.setConvexPath(bgPath);
                        } else {
                            outline.setConvexPath(RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                                    leftBottomRadius, rightBottomRadius, width, height, false));
                        }
                    }
                });
                setClipToOutline(true);
            }
        }
    }

    private void forceRefreshLayout() {
        this.forceRefreshLayout = true;
        requestLayout();
    }

    private Path setBackground() {
        Path bgPath = RadiusUtils.calculateBgPath(leftTopRadius, rightTopRadius,
                leftBottomRadius, rightBottomRadius, width, height);

        Shader bgShader = null;
        if (bgShaderType != ShaderUtils.SHADER_TYPE_NONE) {
            bgShader = ShaderUtils.createShader(bgShaderType, width, height, bgShaderColors, bgShaderLinearOrientation);
        }

        // 边框
        if (solidWidth > 0) {
            Shader solidShader = null;
            if (solidShaderType != ShaderUtils.SHADER_TYPE_NONE) {
                solidShader = ShaderUtils.createShader(solidShaderType, width, height, solidShaderColors, solidShaderLinearOrientation);
            }

            Path[] solidPathArray = RadiusUtils.calculateSocketPath(leftTopRadius, rightTopRadius,
                    leftBottomRadius, rightBottomRadius, width, height, solidWidth);
            List<Path> solidPath = Arrays.asList(solidPathArray);

            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath, solidWidth, solidColorStateList, solidShader, solidPath, dashPathEffect);
        } else {
            radiusDrawable = new RadiusDrawable(bgColorStateList, bgShader, bgPath);
        }
        setBackground(radiusDrawable);
        return bgPath;
    }
}

12.代码使用样式

 <com.sc.scxm.cs.radius.RadiusRelativeLayout
        android:layout_centerInParent="true"
        android:layout_width="@dimen/dp_100"
        android:layout_height="@dimen/dp_100"
        app:rv_radius_all="@dimen/dp_10"
        app:rv_background_color="@color/colorAccent"
        app:rv_solid_width="@dimen/dp_1"
        app:rv_solid_shader_start_color="#CDBC78"
        app:rv_solid_shader_middle_color="#F5FFE3"
        app:rv_solid_shader_middle_color2="#CCBA77"
        app:rv_solid_shader_middle_color3="@color/white"
        app:rv_solid_shader_end_color="#DDC66D"
        app:rv_solid_shader_type="linear"
        />

猜你喜欢

转载自blog.csdn.net/qq_15059163/article/details/126974784