圆角的约束布局

29 阅读2分钟
package com.soling.sosystemui.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;

import com.soling.sosystemui.R;

public class RoundViewGroup extends ConstraintLayout {

    protected static final String TAG = "RoundConstraintLayout";

    private Path mPath = new Path();

    private RectF mRectF = new RectF();

    private Paint mPaint = new Paint();

    private int mWidth, mHeight;

    private int mLeftTopCornerRadius = 20;

    /*
    左下角圆角半径
     */
    private int mLeftBottomCornerRadius = 20;

    /*
    右上角圆角半径
     */
    private int mRightTopCornerRadius = 20;

    /*
    右下角圆角半径
     */
    private int mRightBottomCornerRadius = 20;


    public RoundViewGroup(Context context) {
        super(context);
    }

    public RoundViewGroup(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

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


    @Override
    public void dispatchDraw(Canvas canvas) {
        mWidth = getWidth();
        mHeight = getHeight();
        drawLayer(canvas);
        super.dispatchDraw(canvas);
        drawPath(canvas);
//        drawStroke(canvas, mAttributes.mBorderColor, mAttributes.mBorderWidth);
    }

    /*
    绘制整体的圆角路径
     */
    protected void drawPath(Canvas canvas) {
        resetPaint();
        drawTopLeft(canvas);
        drawTopRight(canvas);
        drawBottomLeft(canvas);
        drawBottomRight(canvas);
        mPaint.setXfermode(null);
        canvas.restore();
    }

    protected void drawLayer(Canvas canvas) {
        canvas.saveLayer(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), mPaint, Canvas.ALL_SAVE_FLAG);
    }

    /*
    绘制内边框
     */
    protected void drawStroke(Canvas canvas) {
//        if (strokeColor == 0 || strokeWidth == 0) {
//            return;
//        }
//        mPaint.reset();
//        mPaint.setAntiAlias(true);
//        mPaint.setDither(true);
//        mPaint.setStyle(Paint.Style.STROKE);
//        canvas.drawPath(getStrokePath(), mPaint);
    }

    /*
    计算内边框的Path
     */
    protected Path getStrokePath() {
        mPath.reset();
        mPath.moveTo(mLeftTopCornerRadius + getStrokeOffset(), getStrokeOffset());
        if (mLeftTopCornerRadius > 0) {
            mRectF.set(getStrokeOffset(), getStrokeOffset(), mLeftTopCornerRadius * 2, mLeftTopCornerRadius * 2);
            mPath.arcTo(mRectF, -90, -90);
        }
        mPath.lineTo(getStrokeOffset(), mHeight - mLeftBottomCornerRadius - getStrokeOffset());
        if (mLeftBottomCornerRadius > 0) {
            mRectF.set(getStrokeOffset(), mHeight - 2 * mLeftBottomCornerRadius, mLeftBottomCornerRadius * 2, mHeight - getStrokeOffset());
            mPath.arcTo(mRectF, 180, -90);
        }
        mPath.lineTo(mWidth - mRightBottomCornerRadius - getStrokeOffset(), mHeight - getStrokeOffset());
        if (mRightBottomCornerRadius > 0) {
            mRectF.set(mWidth - 2 * mRightBottomCornerRadius, mHeight - 2 * mRightBottomCornerRadius, mWidth - getStrokeOffset(), mHeight - getStrokeOffset());
            mPath.arcTo(mRectF, 90, -90);
        }
        mPath.lineTo(mWidth - getStrokeOffset(), mRightTopCornerRadius + getStrokeOffset());
        if (mRightTopCornerRadius > 0) {
            mRectF.set(mWidth - 2 * mRightTopCornerRadius, getStrokeOffset(), mWidth - getStrokeOffset(), 2 * mRightTopCornerRadius);
            mPath.arcTo(mRectF, 0, -90);
        }
        mPath.close();
        return mPath;
    }

    /*
    由于Line线条的绘制中线其实是在View的边界上,导致边框的宽度少一半。当存在圆角时,会出现圆角处的边框宽度
    和直线边界不一致的问题。因此,在计算内边框Path时,将边界Path的位置向View中心收拢边框宽度的1/2。
     */
    protected float getStrokeOffset() {
        return 0.5f;
    }

    /*
    绘制左上角位置的圆角
     */
    protected void drawTopLeft(Canvas canvas) {
        if (mLeftTopCornerRadius > 0) {
            mPath.reset();
            mPath.moveTo(0, mLeftTopCornerRadius);
            mPath.lineTo(0, 0);
            mPath.lineTo(mLeftTopCornerRadius, 0);
            mRectF.set(0, 0, mLeftTopCornerRadius * 2, mLeftTopCornerRadius * 2);
            mPath.arcTo(mRectF, -90, -90);
            mPath.close();
            canvas.drawPath(mPath, mPaint);
        }
    }

    /*
    绘制右上角位置的圆角
     */
    protected void drawTopRight(Canvas canvas) {
        if (mRightTopCornerRadius > 0) {
            mPath.reset();
            mPath.moveTo(mWidth - mRightTopCornerRadius, 0);
            mPath.lineTo(mWidth, 0);
            mPath.lineTo(mWidth, mRightTopCornerRadius);
            mRectF.set(mWidth - 2 * mRightTopCornerRadius, 0, mWidth, mRightTopCornerRadius * 2);
            mPath.arcTo(mRectF, 0, -90);
            mPath.close();
            canvas.drawPath(mPath, mPaint);
        }
    }

    /*
    绘制左下角位置的圆角
     */
    protected void drawBottomLeft(Canvas canvas) {
        if (mLeftBottomCornerRadius > 0) {
            mPath.reset();
            mPath.moveTo(0, mHeight - mLeftBottomCornerRadius);
            mPath.lineTo(0, mHeight);
            mPath.lineTo(mLeftBottomCornerRadius, mHeight);
            mRectF.set(0, mHeight - 2 * mLeftBottomCornerRadius, mLeftBottomCornerRadius * 2, mHeight);
            mPath.arcTo(mRectF, 90, 90);
            mPath.close();
            canvas.drawPath(mPath, mPaint);
        }
    }

    /*
    绘制右下角位置的圆角
     */
    protected void drawBottomRight(Canvas canvas) {
        if (mRightBottomCornerRadius > 0) {
            mPath.reset();
            mPath.moveTo(mWidth - mRightBottomCornerRadius, mHeight);
            mPath.lineTo(mWidth, mHeight);
            mPath.lineTo(mWidth, mHeight - mRightBottomCornerRadius);
            mRectF.set(mWidth - 2 * mRightBottomCornerRadius, mHeight - 2 * mRightBottomCornerRadius, mWidth, mHeight);
            mPath.arcTo(mRectF, 0, 90);
            mPath.close();
            canvas.drawPath(mPath, mPaint);
        }
    }

    protected void resetPaint() {
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.STROKE);
              mPaint.setStyle(Paint.Style.FILL);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    }

}