最近項(xiàng)目中需要背景做成圓角溪胶,心說(shuō)這還是不是很容易的事兒搂擦,后來(lái)發(fā)現(xiàn)有性能問(wèn)題!網(wǎng)上查了一圈载荔,才發(fā)現(xiàn)圓角不同的實(shí)現(xiàn)方式盾饮,對(duì)性能竟然有這么大的影響采桃!
首先想到的就是去看看大名鼎鼎的Fresco是怎么實(shí)現(xiàn)圓角的懒熙。
其實(shí)核心就是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
}
對(duì)應(yīng)兩種不同的實(shí)現(xiàn)方式
@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
就是裁剪工扎,直接對(duì)canvas進(jìn)行操作,性能不好衔蹲,并且有很多限制
OVERLAY_COLOR
是在上面蓋了一個(gè)純色的圖層肢娘,達(dá)到圓角的效果,性能好
還有一種實(shí)現(xiàn)舆驶,是使用一個(gè)GradientDrawable
橱健,設(shè)置setCornerRadius
,用作背景沙廉。目前我不太清楚這種實(shí)現(xiàn)性能上表現(xiàn)怎么樣拘荡,據(jù)說(shuō)在某些機(jī)器上顯示效果不好,有黑邊什么的撬陵,不過(guò)我還沒(méi)有見過(guò)珊皿。
其實(shí)關(guān)于不同方式的對(duì)比,F(xiàn)resco的官方文檔圓角和圓圈里面已經(jīng)寫的很清楚了
當(dāng)使用BITMAP_ONLY(默認(rèn))模式時(shí)的限制:
并非所有的圖片分支部分都可以實(shí)現(xiàn)圓角巨税,目前只有占位圖片和實(shí)際圖片可以實(shí)現(xiàn)圓角蟋定,我們正在努力為背景圖片實(shí)現(xiàn)圓角功能。
只有BitmapDrawable 和 ColorDrawable類的圖片可以實(shí)現(xiàn)圓角草添。我們目前不支持包括NinePatchDrawable和 ShapeDrawable在內(nèi)的其他類型圖片驶兜。(無(wú)論他們是在XML或是程序中聲明的)
動(dòng)畫不能被圓角。
由于Android的BitmapShader的限制远寸,當(dāng)一個(gè)圖片不能覆蓋全部的View的時(shí)候抄淑,邊緣部分會(huì)被重復(fù)顯示,而非留白而晒。對(duì)這種情況可以使用不同的縮放類型(比如centerCrop)來(lái)保證圖片覆蓋了全部的View蝇狼。
OVERLAY_COLOR模式?jīng)]有上述限制,但由于這個(gè)模式使用在圖片上覆蓋一個(gè)純色圖層的方式來(lái)模擬圓角效果倡怎,因此只有在圖標(biāo)背景是靜止的并且與圖層同色的情況下才能獲得較好的效果迅耘。
Drawee 內(nèi)部實(shí)現(xiàn)了一個(gè)CLIPPING模式贱枣。但由于有些Canvas的實(shí)現(xiàn)并不支持路徑剪裁(Path Clipping),這個(gè)模式被禁用了且不對(duì)外開放颤专。并且由于路徑剪裁不支持反鋸齒纽哥,會(huì)導(dǎo)致圓角的邊緣呈現(xiàn)像素化的效果。
總之栖秕,如果生成臨時(shí)bitmap的方法春塌,所有的上述問(wèn)題都可以避免。但是這個(gè)方法并不被支持因?yàn)檫@會(huì)導(dǎo)致很嚴(yán)重的內(nèi)存問(wèn)題簇捍。
綜上所述只壳,在 Android 中實(shí)現(xiàn)圓角效果,沒(méi)有一個(gè)絕對(duì)好的方案暑塑,你必須在上述的方案中進(jìn)行選擇吼句。