美文网首页
总结:使用Path切一个圆弧

总结:使用Path切一个圆弧

作者: wyonxue | 来源:发表于2018-01-09 21:35 被阅读0次

    总结:使用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切一个圆弧

          本文链接:https://www.haomeiwen.com/subject/kplwnxtx.html