总结:使用Path切一个圆弧

总结:使用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);
            }
        }
    }

你可能感兴趣的:(总结:使用Path切一个圆弧)