Android实现一个圆角
最近项目中需要背景做成圆角,心说这还是不是很容易的事儿,后来发现有性能问题!网上查了一圈,才发现圆角不同的实现方式,对性能竟然有这么大的影响!
首先想到的就是去看看大名鼎鼎的Fresco是怎么实现圆角的。
其实核心就是RoundedCornersDrawable
,其中,有两种类型
public enum Type {
/**
* Draws rounded corners on top of the underlying drawable by overlaying a solid color which
* is specified by {@code setOverlayColor}. This option should only be used when the
* background beneath the underlying drawable is static and of the same solid color.
*/
OVERLAY_COLOR,
/**
* Clips the drawable to be rounded. This option is not supported right now but is expected to
* be made available in the future.
*/
CLIPPING
}
对应两种不同的实现方式
@Override
public void draw(Canvas canvas) {
Rect bounds = getBounds();
switch (mType) {
case CLIPPING:
int saveCount = canvas.save();
// clip, note: doesn't support anti-aliasing
mPath.setFillType(Path.FillType.EVEN_ODD);
canvas.clipPath(mPath);
super.draw(canvas);
canvas.restoreToCount(saveCount);
break;
case OVERLAY_COLOR:
super.draw(canvas);
mPaint.setColor(mOverlayColor);
mPaint.setStyle(Paint.Style.FILL);
mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);
canvas.drawPath(mPath, mPaint);
if (mIsCircle) {
// INVERSE_EVEN_ODD will only draw inverse circle within its bounding box, so we need to
// fill the rest manually if the bounds are not square.
float paddingH = (bounds.width() - bounds.height() + mBorderWidth) / 2f;
float paddingV = (bounds.height() - bounds.width() + mBorderWidth) / 2f;
if (paddingH > 0) {
canvas.drawRect(bounds.left, bounds.top, bounds.left + paddingH, bounds.bottom, mPaint);
canvas.drawRect(
bounds.right - paddingH,
bounds.top,
bounds.right,
bounds.bottom,
mPaint);
}
if (paddingV > 0) {
canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.top + paddingV, mPaint);
canvas.drawRect(
bounds.left,
bounds.bottom - paddingV,
bounds.right,
bounds.bottom,
mPaint);
}
}
break;
}
if (mBorderColor != Color.TRANSPARENT) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mBorderColor);
mPaint.setStrokeWidth(mBorderWidth);
mPath.setFillType(Path.FillType.EVEN_ODD);
canvas.drawPath(mBorderPath, mPaint);
}
}
CLIPPING
就是裁剪,直接对canvas进行操作,性能不好,并且有很多限制
OVERLAY_COLOR
是在上面盖了一个纯色的图层,达到圆角的效果,性能好
还有一种实现,是使用一个GradientDrawable
,设置setCornerRadius
,用作背景。目前我不太清楚这种实现性能上表现怎么样,据说在某些机器上显示效果不好,有黑边什么的,不过我还没有见过。
其实关于不同方式的对比,Fresco的官方文档圆角和圆圈里面已经写的很清楚了
当使用BITMAP_ONLY(默认)模式时的限制:
并非所有的图片分支部分都可以实现圆角,目前只有占位图片和实际图片可以实现圆角,我们正在努力为背景图片实现圆角功能。
只有BitmapDrawable 和 ColorDrawable类的图片可以实现圆角。我们目前不支持包括NinePatchDrawable和 ShapeDrawable在内的其他类型图片。(无论他们是在XML或是程序中声明的)
动画不能被圆角。
由于Android的BitmapShader的限制,当一个图片不能覆盖全部的View的时候,边缘部分会被重复显示,而非留白。对这种情况可以使用不同的缩放类型(比如centerCrop)来保证图片覆盖了全部的View。
OVERLAY_COLOR模式没有上述限制,但由于这个模式使用在图片上覆盖一个纯色图层的方式来模拟圆角效果,因此只有在图标背景是静止的并且与图层同色的情况下才能获得较好的效果。
Drawee 内部实现了一个CLIPPING模式。但由于有些Canvas的实现并不支持路径剪裁(Path Clipping),这个模式被禁用了且不对外开放。并且由于路径剪裁不支持反锯齿,会导致圆角的边缘呈现像素化的效果。
总之,如果生成临时bitmap的方法,所有的上述问题都可以避免。但是这个方法并不被支持因为这会导致很严重的内存问题。
综上所述,在 Android 中实现圆角效果,没有一个绝对好的方案,你必须在上述的方案中进行选择。