自定义CardView

自定义CardView

1.支持自定义阴影设置(颜色 模糊半径 x/y轴偏移)
2.支持四角分别圆角
3.支持内容背景填充色
4.继承自FrameLayout,通过对内容视图进行路径裁裁剪绘制实现圆角及阴影
绘制效果图

源码

public class ShadowCardView extends FrameLayout {

    private int bgColor;
    private boolean hasShadow;//是否有阴影
    private boolean hasRadius;//是否有圆角
    private final float[] radiusList = new float[8];// 矩阵四角圆角 两个一组分别为一角的x轴半径y轴半径  四组分别为 上左 上右  下右  下左
    private float shadowRadius = 0f;//阴影模糊半径
    private Paint shadowPaint;
    private float shadowOffsetY = 0f;
    private float shadowOffsetX = 0f;
    private int shadowColor;
    private RectF rectF;//内容绘制矩阵
    private Path path;//阴影绘制路径/内容裁剪路径
    private int left, top, right, bottom;//用户指定padding

    public ShadowCardView(@NonNull Context context) {
        this(context, null);
    }

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

    public ShadowCardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShadowCardView);

        shadowColor = getResources().getColor(R.color.shadowColor);
        bgColor = getResources().getColor(R.color.white);
        left = getPaddingLeft();
        right = getPaddingRight();
        top = getPaddingRight();
        bottom = getPaddingRight();

        if (a != null) {
            float radius = a.getDimension(R.styleable.ShadowCardView_android_radius, 0);
            float shadowOffset = a.getDimension(R.styleable.ShadowCardView_shadowOffset, 0);
            float dx = a.getDimension(R.styleable.ShadowCardView_android_x, shadowOffset);
            float dy =  a.getDimension(R.styleable.ShadowCardView_android_y, shadowOffset); 
            shadowColor =  a.getColor(R.styleable.ShadowCardView_android_shadowColor, shadowColor);
            bgColor = a.getColor(R.styleable.ShadowCardView_cardBackgroundColor, bgColor);

            if (radius > 0) {
                setRadius(radius);
            } else {
                float topLeft = a.getDimension(R.styleable.ShadowCardView_android_topLeftRadius, 0);
                float topRight = a.getDimension(R.styleable.ShadowCardView_android_topRightRadius, 0);
                float bottomRight = a.getDimension(R.styleable.ShadowCardView_android_bottomRightRadius, 0);
                float bottomLeft = a.getDimension(R.styleable.ShadowCardView_android_bottomLeftRadius, 0);
                setRadius(topLeft, topRight, bottomRight, bottomLeft);
            }
            setShadowOffSet(dx, dy);
            setShadowRadius(a.getDimension(R.styleable.ShadowCardView_shadowRadius, 0));
            a.recycle();
        }
    }

    public void setCardBackgroundColor(int bgColor) {
        this.bgColor = bgColor;
        if (shadowPaint != null) {
            shadowPaint.setColor(bgColor);
        }
        invalidate();
    }

    public void setShadowRadius(float shadowRadius) {
        this.shadowRadius = shadowRadius;
        hasShadow = shadowRadius > 0;
        if (hasShadow) {
            if (shadowPaint == null) {
                shadowPaint = new Paint();
                shadowPaint.setStyle(Paint.Style.FILL);
                shadowPaint.setAntiAlias(true);
            }
            shadowPaint.setColor(bgColor);
            setShadowLayer();
            setPaddingRadius();
        }
        if (isLaidOut()) {
            invalidate();
        }
    }

    public void setShadowColor(int color) {
        this.shadowColor = color;
        setShadowLayer();
        invalidate();
    }

    public void setShadowDx(float dx) {
        shadowOffsetX = dx;
        setShadowOffSetImpl();
    }

    public void setShadowDy(float dy) {
        shadowOffsetY = dy;
        setShadowOffSetImpl();
    }

    public void setShadowOffSet(float offSet) {
        setShadowOffSet(offSet, offSet);
    }

    public void setShadowOffSet(float dx, float dy) {
        shadowOffsetX = dx;
        shadowOffsetY = dy;
        setShadowOffSetImpl();
    }


    private void setShadowOffSetImpl() {
        setShadowLayer();
        if (isLaidOut()) {
            invalidate();
        }
    }

    private void setShadowLayer() {
        if (shadowPaint != null) {
            shadowPaint.setShadowLayer(shadowRadius, shadowOffsetX, shadowOffsetY, shadowColor);
        }
    }

    private void setPaddingRadius() {
        float left = this.left;
        float top = this.top;
        float right = this.right;
        float bottom = this.bottom;
        if (hasShadow) {
            left += shadowRadius;
            top += shadowRadius;
            right += shadowRadius;
            bottom += shadowRadius;
        }
        super.setPadding((int) left,
                (int) top,
                (int) right,
                (int) bottom);
    }

    @Override
    public void setPadding(int left, int top, int right, int bottom) {
        this.left = left;
        this.right = right;
        this.top = top;
        this.bottom = bottom;
        setPaddingRadius();
    }

    @Deprecated
    public void setShadowRound(float shadowRound) {
        setRadius(shadowRound);
    }

    public void setRadius(float radius) {
        setRadius(radius, radius, radius, radius);
    }

    public void setRadius(float topLeft, float topRight, float bottomRight, float bottomLeft) {
        radiusList[0] = topLeft;
        radiusList[1] = topLeft;
        radiusList[2] = topRight;
        radiusList[3] = topRight;
        radiusList[4] = bottomRight;
        radiusList[5] = bottomRight;
        radiusList[6] = bottomLeft;
        radiusList[7] = bottomLeft;
        hasRadius = topLeft > 0 || topRight > 0 || bottomRight > 0 || bottomLeft > 0;
        if (hasRadius) {
            rectF = new RectF();
            path = new Path();
        }
        if (isLaidOut()) {
            invalidate();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (hasShadow || hasRadius) {
            if (rectF == null) {
                rectF = new RectF();
            }
            if (path == null) {
                path = new Path();
            }
            rectF.left = shadowRadius;
            rectF.top = shadowRadius;
            rectF.right = w - shadowRadius;
            rectF.bottom = h - shadowRadius;
            if (hasRadius) {
                if (!path.isEmpty()) {
                    path.reset();
                }
                path.addRoundRect(rectF, radiusList, Path.Direction.CW);
            }
        }
    }

    //  关键代码 提前裁剪/绘制背景
    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (hasShadow || hasRadius) {
            if (rectF != null && shadowPaint != null && hasShadow) {
                // 绘制阴影
                canvas.drawPath(path, shadowPaint);
            }
            if (hasRadius && path != null) {
                // 裁剪圆角
                canvas.clipPath(path);
                // 填充底色
                canvas.drawColor(bgColor);
            }
        }
        super.dispatchDraw(canvas);
    }
}


attrs资源

  <declare-styleable name="ShadowCardView">
        
        <!--圆角度-->
        <attr name="android:radius" />
        <attr name="android:topLeftRadius" />
        <attr name="android:topRightRadius" />
        <attr name="android:bottomLeftRadius" />
        <attr name="android:bottomRightRadius" />
        <!--阴影的X轴偏移-->
        <attr name="android:x" />
        <!--阴影的Y轴偏移-->
        <attr name="android:y" />
         <!--阴影的X+Y轴偏移-->
        <attr name="shadowOffset" format="dimension" />
        <!--阴影颜色-->
        <attr name="android:shadowColor" />
        <attr name="cardBackgroundColor" format="color" />
        <!--阴影半径,越大越模糊 阴影越宽-->
       <attr name="shadowRadius" format="dimension" /> 
    </declare-styleable>
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值