总结:使用Path切一个圆弧
参看:
Xfermode使用中碰到的问题
关于android中图片裁剪以及PorterDuffXfermode的使用经验小结
在使用Path切一个圆弧中发现,使用canvas.clipPath()
圆弧锯齿很明显,后来改用Paint.setXfermode()
来实现,弧比较圆滑,但要注意硬件加速问题。最后则改用BitmapShader裁减path来实现。
代码如下:
public static class TopArcDrawable extends ClipDrawable {
private Drawable mDrawable;
private int mTopMarginPx;
private float mArcDegree; // 0 - 180
private RectF mCircleRectF = new RectF();
private boolean mNeedComputeRadius = true;
private Path mPath;
private RectF mTmpRect;
private boolean mUpdatePath = true;
private Paint mPaint;
public TopArcDrawable(Drawable drawable) {
super(drawable, Gravity.NO_GRAVITY, ClipDrawable.HORIZONTAL);
mDrawable = drawable;
}
@Override
public void setDrawable(Drawable drawable) {
mDrawable = drawable;
super.setDrawable(drawable);
}
public void setTopMarginPx(int topMarginPx) {
mTopMarginPx = topMarginPx;
mUpdatePath = true;
}
// 角度 0-180
public void setArcDegree(float arcDegree) {
mArcDegree = Math.min(180, arcDegree);
mCircleRectF.setEmpty();
mUpdatePath = true;
mNeedComputeRadius = arcDegree > 0;
}
@Override
protected void onBoundsChange(@NonNull Rect bounds) {
super.onBoundsChange(bounds);
setBounds(bounds);
mUpdatePath = true;
mNeedComputeRadius = true;
mCircleRectF.setEmpty();
}
private void resetPath() {
if (!mUpdatePath) {
return;
}
if (mPath == null) {
mPath = new Path();
mTmpRect = new RectF(getBounds());
} else {
mPath.reset();
mTmpRect.set(getBounds());
}
// assert mArcDegree > 0
computeRaidus();
final float start = (float) (mCircleRectF.width() / 2 * (1 - Math.cos(Math.toRadians(mArcDegree / 2))));
// 圆弧的形状的原因,此处并需要计算出圆弧起始点而move过去。
// 只要move到0,0点,然后绘制圆弧是会自动连接到圆弧起始点,关闭path后,最终会多一条[0,0][0,start]的线而已。
mPath.moveTo(0, start);
mPath.arcTo(mCircleRectF, 270 - mArcDegree / 2, mArcDegree);
mPath.lineTo(mTmpRect.right, mTmpRect.bottom);
mPath.lineTo(0, mTmpRect.bottom);
mPath.close();
}
// assert mArcDegree > 0
private void computeRaidus() {
if (!mNeedComputeRadius) {
return;
}
float radius = (float) (getBounds().width() / 2.0f / Math.sin(Math.toRadians(mArcDegree / 2)));
mCircleRectF.set(mTmpRect.centerX() - radius, mTmpRect.top, mTmpRect.centerX() + radius, radius * 2);
mCircleRectF.offset(0, 6); // 弧向下偏移一点,解决有的手机弧顶部被切成一条直线
}
@Override
public void draw(Canvas canvas) {
if (mDrawable == null) {
return;
}
final Rect bounds = getBounds();
final int w = bounds.width();
final int h = bounds.height();
if (w > 0 && h > 0) {
// Xfermode的影响,使用saveLayer 而不是 save
final int saveCount = canvas.saveLayer(0, 0, w, h, null, Canvas.ALL_SAVE_FLAG);
canvas.translate(0, mTopMarginPx);
// 1 clipPath
//if (mArcDegree > 0) {
// resetPath();
// canvas.clipPath(mPath);
//}
mDrawable.draw(canvas);
// 2 xfermode
if (mArcDegree > 0) {
resetPath();
if (mPaint == null) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
canvas.drawPath(mPath, mPaint);
}
// 3 BitmapShader
// if (mPaint == null) {
// mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
// 此处原本想创建一个1*1大小的Bitmap,利用TileMode拓展图片,但是部分手机不支持
// bitmap.eraseColor(mColor);
// BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
// mPaint.setShader(bitmapShader);
// }
// canvas.drawPath(mPath, mPaint);
canvas.restoreToCount(saveCount);
}
}
}
网友评论