Android 自定义圆角+阴影布局

效果图

为了更方便的实现UI效果图里的圆角+阴影,就自定义了布局RoundShadowLayout

逻辑思路

  1. 设置布局的圆角,将子view超出圆角的区域裁剪掉
  2. 设置阴影,不改变子view的大小,将布局大小扩充到可以容纳阴影,并调整子view的位置

裁剪实现方案

  1. 使用canvas.clipXXX()方法裁剪画图区域(存在锯齿,不使用)
  2. 使用paint的Xfermode进行处理,获得需要效果(使用)
  3. 使用ViewOutLineProvider(需要21以上,不能单独设置某个圆角,不使用)

阴影实现方案

  1. 使用paint.setShadowLayer()方法

 主要实现代码

//设置布局大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    // 测量子view的最大宽高
    int width = 0;
    int height = 0;
    for (int i=0;i<getChildCount();i++) {
        View child = getChildAt(i);
        //测量子View的宽高,measureChild最终调用child.measure(w,h)
        final ViewGroup.LayoutParams lp = child.getLayoutParams();
        final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec-(int) (shadowRadius)*2, getPaddingLeft() + getPaddingRight(), lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec-(int) (shadowRadius)*2, getPaddingTop() + getPaddingBottom(), lp.height);
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

        MarginLayoutParams mlp = (MarginLayoutParams) child.getLayoutParams();
        int childWidth = child.getMeasuredWidth() + mlp.leftMargin + mlp.rightMargin;
        int childHeight = child.getMeasuredHeight() + mlp.topMargin + mlp.bottomMargin;
        width = Math.max(width, childWidth);
        height = Math.max(height, childHeight);
    }
    //如果使用阴影,则宽高加上阴影
    setMeasuredDimension(
        width + getPaddingLeft() + getPaddingRight() + (int) (shadowRadius)*2,
        height + getPaddingTop() + getPaddingBottom() + (int) (shadowRadius)*2
    );
}
//添加阴影bitmap,最后将bitmap设置为布局的background
private Bitmap createShadowBitmap(int shadowWidth, int shadowHeight, float shadowRadius,
                                      float dx, float dy, int shadowColor) {
    Bitmap output = Bitmap.createBitmap(shadowWidth, shadowHeight, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(output);

    shadowRect.set(shadowRadius,shadowRadius,shadowWidth - shadowRadius,shadowHeight - shadowRadius);

    if (dy > 0) {
        shadowRect.top += dy;
        shadowRect.bottom -= dy;
    } else if (dy < 0) {
        shadowRect.top += Math.abs(dy);
        shadowRect.bottom -= Math.abs(dy);
    }

    if (dx > 0) {
        shadowRect.left += dx;
        shadowRect.right -= dx;
    } else if (dx < 0) {
        shadowRect.left += Math.abs(dx);
        shadowRect.right -= Math.abs(dx);
    }

    shadowPaint.setAntiAlias(true);
    shadowPaint.setStyle(Paint.Style.FILL);
    shadowPaint.setColor(shadowColor);
    if (!isInEditMode()) {
        shadowPaint.setShadowLayer(shadowRadius, dx, dy, shadowColor);
    }
    shadowPath.reset();
    shadowPath.addRoundRect(shadowRect, radiusArray, Path.Direction.CW);
    canvas.drawPath(shadowPath, shadowPaint);

    return output;
}
//裁剪圆角区域
private void clipRound(Canvas canvas) {
    roundPath.reset();
    roundPath.addRoundRect(roundRect, radiusArray, Path.Direction.CW);
    //画笔设置
    roundPaint.setColor(Color.WHITE);
    roundPaint.setAntiAlias(true);
    roundPaint.setStyle(Paint.Style.FILL);
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
        roundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawPath(roundPath, roundPaint);
    } else {
        roundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        final Path path = new Path();
        path.addRect(0, 0, getWidth(), getHeight(), Path.Direction.CW);
        path.op(roundPath, Path.Op.DIFFERENCE);
        canvas.drawPath(path, roundPaint);
    }
}

 

源代码https://github.com/wudengwei/RoundShadowLayout

参考 rclayoutshadow-layout

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值