Android Canvas path 的圆角处理

目录表

在开发过程中,圆角背景和圆角图片应该都算是标配了,如下面效果:顶部一个自定义的进度背景 view,下面挨着路口放大图 ImageView。

在这里插入图片描述

实现一

由于都是代码动态设置的,所以就不考虑 xml 了。设置背景首先想到的是 GradientDrawable,我们可以通过其 setCornerRadii 方法来指定 4 个圆角的弧度。float[] 的长度是 8 ,2 个一组,很灵活。最后通过 setBackgroundDrawable(Drawable background) 直接设置即可。

    private GradientDrawable getBackground(boolean isNightMode) {
        int color;
        if (isNightMode) {
            color = Color.parseColor("#000000");
        } else {
            color = Color.parseColor("#3C3F46");
        }
        float radius = NaviUtil.dp2px(getContext(), 8);
        GradientDrawable drawable = new GradientDrawable();
        drawable.setShape(GradientDrawable.RECTANGLE);
        drawable.setCornerRadii(new float[]{radius, radius, radius, radius
                ,0, 0, 0, 0});
        drawable.setColor(color);
        return drawable;
    }

实现二

可以将整个 view 通过裁剪画布的方式,在 onDraw方法中进行绘制。主要方法即 path.addRoundRect(…) ,代码见下:

    private void drawClipPathOnCanval(Canvas canvas) {
        if(mClipPath == null){
            mClipPath = new Path();
            if(mClipRectF == null)
                mClipRectF = new RectF(0, 0
                        , CommentUtils.dip2px(mContext, 150)
                        , CommentUtils.dip2px(mContext, 150));
                        
            // path为圆形矩形。裁剪圆形,弧等都同理
            /*
             * RectF:矩形轮廓
             * rx:圆角矩形的圆角的x半径
             * ry:圆角矩形的圆角的y半径
             * direction:cw:顺时针、CCW:逆时针
             */
            mClipPath.addRoundRect(mClipRectF
                    , CommentUtils.dip2px(mContext, 15)
                    , CommentUtils.dip2px(mContext, 15)
                    , Path.Direction.CW);
        }
        canvas.clipPath(mClipPath);
    }

方法 addRoundRect(RectF, rx, ry, direction) 同时绘制 4 个角弧度是非常方便的,但如果只需绘制左上和左下两个弧角呢?所幸其还有带 float[] 参数的同载方法,可以灵活设置每个角的弧度,使用方法估计一看就明:

        addRoundRect(RectF rect, float[] radii, Direction dir)

在对 canvas 进行裁剪前,我们需要锁定下画布,裁剪绘制完成之后,再恢复画布。在 onDraw方法见下:

        // 锁定当前画布
        canvas.save();
        // 裁剪画布
        drawClipPathOnCanval(canvas);
        // 正常绘制逻辑:这里包括绘制进度条等
        // 恢复画布
        canvas.restore();

实现三

如果对于进度值的精度要求不高,完全可能通过多边形 + 圆弧的方式来实现两个角的圆弧矩形。例如上图中的绿色进度实现,方法可以如下:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float currW = getWidth() * mProgress;
        if (currW < mRadus * 0.25) {
            return;
        } else if (currW <= mRadus) {
            currW = mRadus;
        } else if (currW > getWidth() - mRadus) {
            // 绘制右上角弧度
            canvas.drawArc(getRect(getWidth() - 2 * mRadus, 0
                        , getWidth(), 2 * mRadus)
                    , -90, 90
                    , true, mPbBgPaint);
            canvas.drawRect(getRect(getWidth() - mRadus, mRadus
                    , getWidth(), mPbheight), mPbBgPaint);

            currW = getWidth() - mRadus;
        }

        // 绘制左上角弧度
        canvas.drawArc(getRect(0, 0, mRadus * 2, mRadus * 2)
                , 180, 90
                , true, mPbBgPaint);

        // 绘制多边形
        mMorePath.moveTo(0, mPbheight);
        mMorePath.lineTo(0, mRadus);
        mMorePath.lineTo(mRadus, mRadus);
        mMorePath.lineTo(mRadus, 0);
        mMorePath.lineTo(currW, 0);
        mMorePath.lineTo(currW, mPbheight);
        mMorePath.close();
        canvas.drawPath(mMorePath, mPbBgPaint);
    }

实现四

对于图片 Bitmap 的裁剪,我们也可以通过 path 来生成一个两个弧角的 Bitmap,方法见下:

    public Bitmap getRoundedCornerBitmap(Bitmap bitmap, float[] rids) {
        try {
            Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
                    bitmap.getHeight(), Bitmap.Config.ARGB_4444);
            Canvas canvas = new Canvas(output);
            final Paint paint = new Paint();
            final Rect rect = new Rect(0, 0, bitmap.getWidth(),
                    bitmap.getHeight());
            final RectF rectF = new RectF(new Rect(0, 0, bitmap.getWidth(),
                    bitmap.getHeight()));
            paint.setAntiAlias(true);
            canvas.drawARGB(0, 0, 0, 0);
            paint.setColor(Color.BLACK);
            Path path = new Path();
            // float[] rids 可灵活设置每个角的弧度
            path.addRoundRect(rectF
                    , rids
                    , Path.Direction.CW);
            canvas.drawPath(path, paint);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

            final Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

            canvas.drawBitmap(bitmap, src, rect, paint);
            return output;
        } catch (OutOfMemoryError outOfMemoryError){
            outOfMemoryError.printStackTrace();
            return bitmap;
        }catch (Exception e) {
            TLog.e(TAG, TLog.USR, e);
            return bitmap;
        }
    }

猜你喜欢

转载自blog.csdn.net/MingJieZuo/article/details/108337332