效果图
为了更方便的实现UI效果图里的圆角+阴影,就自定义了布局RoundShadowLayout
逻辑思路
- 设置布局的圆角,将子view超出圆角的区域裁剪掉
- 设置阴影,不改变子view的大小,将布局大小扩充到可以容纳阴影,并调整子view的位置
裁剪实现方案
- 使用canvas.clipXXX()方法裁剪画图区域(存在锯齿,不使用)
- 使用paint的Xfermode进行处理,获得需要效果(使用)
- 使用ViewOutLineProvider(需要21以上,不能单独设置某个圆角,不使用)
阴影实现方案
-
使用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);
}
}