RoundRectHelper.java 6.98 KB
package com.wd.common.widget;

import android.annotation.SuppressLint;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Checkable;


import com.wd.fastcoding.base.R;

import java.util.Arrays;

public class RoundRectHelper {
    private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    private static final PorterDuffXfermode DST_OUT = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
    private static final PorterDuffXfermode SRC_OVER = new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);

    private Paint paint;
    private Path path;
    private Path pathOut;
    private Region region;
    private AttrSet attrSet;

    public RoundRectHelper(View view, AttributeSet attrs) {
        attrSet = new AttrSet();
        TypedArray ta = view.getContext().obtainStyledAttributes(attrs, R.styleable.RoundRectAttrs);
        attrSet.setMaskColor(getColorStateList(ta, R.styleable.RoundRectAttrs_mask_color, Color.TRANSPARENT));
        attrSet.setStrokeColor(getColorStateList(ta, R.styleable.RoundRectAttrs_stroke_color, Color.TRANSPARENT));
        attrSet.setStrokeWidth(ta.getDimensionPixelSize(R.styleable.RoundRectAttrs_stroke_width, 0));
        attrSet.setClipBackground(ta.getBoolean(R.styleable.RoundRectAttrs_clip_background, false));

        int radius = ta.getDimensionPixelSize(R.styleable.RoundRectAttrs_radius, 0);
        attrSet.setRadiusTopLeft(ta.getDimensionPixelSize(R.styleable.RoundRectAttrs_radius_top_left, radius));
        attrSet.setRadiusTopRight(ta.getDimensionPixelSize(R.styleable.RoundRectAttrs_radius_top_right, radius));
        attrSet.setRadiusBottomLeft(ta.getDimensionPixelSize(R.styleable.RoundRectAttrs_radius_bottom_left, radius));
        attrSet.setRadiusBottomRight(ta.getDimensionPixelSize(R.styleable.RoundRectAttrs_radius_bottom_right, radius));
        ta.recycle();

//        view.setLayerType(LAYER_TYPE_SOFTWARE, null);

        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeCap(Paint.Cap.ROUND);

        path = new Path();
        pathOut = new Path();
    }

    private ColorStateList getColorStateList(TypedArray ta, int index, int defaultColor) {
        ColorStateList colorStateList = ta.getColorStateList(index);
        if (colorStateList != null) {
            return colorStateList;
        }
        return new ColorStateList(new int[][]{{}}, new int[]{ta.getColor(index, defaultColor)});
    }

    public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
        refreshRegion(view, w, h);
    }

    @SuppressLint("NewApi")
    public void refreshRegion(View view, int w, int h) {
        Rect areas = new Rect(view.getPaddingLeft(), view.getPaddingTop(), w - view.getPaddingRight(), h - view.getPaddingBottom());

        path.reset();
        path.addRoundRect(areas.left, areas.top, areas.right, areas.bottom, attrSet.getRadii(), Path.Direction.CW);

        pathOut.reset();
        pathOut.addRect(0, -0.1f, w, h, Path.Direction.CW);//这里是为了解决顶部有一条线没有被裁掉的bug
        pathOut.op(path, Path.Op.DIFFERENCE);

        region = new Region();
        Region clip = new Region(areas);
        region.setPath(path, clip);
    }

    public void setStrokeWidth(float strokeWidth) {
        this.attrSet.setStrokeWidth(strokeWidth);
    }

    public void setStrokeColor(int strokeColor) {
        this.attrSet.setStrokeColor(new ColorStateList(new int[][]{{}}, new int[]{strokeColor}));
    }

    public void onDrawClip(View view, Canvas canvas) {
        //        int count = canvas.save();
//        int count = canvas.saveLayer(0, 0, width, height, null);

        paint.setStyle(Paint.Style.FILL);

        int maskColor = getStateColor(view, attrSet.getMaskColor(), Color.TRANSPARENT);
        if (maskColor != Color.TRANSPARENT) {
            paint.setColor(maskColor);
            paint.setXfermode(SRC_OVER);
            canvas.drawPath(path, paint);
        }

        if (attrSet.getStrokeWidth() > 0) {
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(attrSet.getStrokeWidth() * 2);
            paint.setColor(Color.WHITE);
            paint.setXfermode(DST_OUT);
            canvas.drawPath(path, paint);

            int strokeColor = getStateColor(view, attrSet.getStrokeColor(), Color.TRANSPARENT);
            if (strokeColor != Color.TRANSPARENT) {
                paint.setColor(strokeColor);
                paint.setXfermode(SRC_OVER);
                canvas.drawPath(path, paint);
            }
        }

        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.WHITE);

        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
            paint.setXfermode(DST_IN);
            canvas.drawPath(path, paint);
        } else {
            paint.setXfermode(DST_OUT);
            canvas.drawPath(pathOut, paint);
        }

        paint.setXfermode(null);

//        canvas.restoreToCount(count);
    }

    public int getStateColor(View view, ColorStateList colorStateList, int defaultColor) {
        if (colorStateList == null) {
            return defaultColor;
        }
        int[] states = new int[4];
        int index = 0;
        if (view.isPressed()) {
            states[index++] = android.R.attr.state_pressed;
        }
        if (view.isFocused()) {
            states[index++] = android.R.attr.state_focused;
        }
        if (view.isSelected()) {
            states[index++] = android.R.attr.state_selected;
        }
        if (view instanceof Checkable && ((Checkable) view).isChecked()) {
            states[index++] = android.R.attr.state_checked;
        }
        return colorStateList.getColorForState(Arrays.copyOf(states, index), defaultColor);
    }

    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        if (action == MotionEvent.ACTION_DOWN && !region.contains((int) ev.getX(), (int) ev.getY())) {
            return false;
        }
        return true;
    }

    public void setRadius(float radius) {
        this.attrSet.setRadius(radius);
    }

    public float getRadius() {
        return attrSet.getRadiusBottomLeft();
    }

    public void setRadiusTopLeft(int radius) {
        attrSet.setRadiusTopLeft(radius);
    }

    public void setRadiusTopRight(int radius) {
        attrSet.setRadiusTopRight(radius);
    }

    public void setRadiusBottomLeft(int radius) {
        attrSet.setRadiusBottomLeft(radius);
    }

    public void setRadiusBottomRight(int radius) {
        attrSet.setRadiusBottomRight(radius);
    }

    public void setMaskColor(int color) {
        attrSet.setMaskColor(new ColorStateList(new int[][]{{}}, new int[]{color}));
    }
}