代码之家  ›  专栏  ›  技术社区  ›  Jesse Wilson

如何在Android的画布上反锯齿剪辑边界?

  •  27
  • Jesse Wilson  · 技术社区  · 14 年前

    android.graphics.Canvas to draw a ring . 我的onDraw方法将画布剪裁成一个孔作为内圈,然后在孔上绘制完整的外圈:

        clip = new Path();
        clip.addRect(outerCircle, Path.Direction.CW);
        clip.addOval(innerCircle, Path.Direction.CCW);
    
        canvas.save();
        canvas.clipPath(clip);
        canvas.drawOval(outerCircle, lightGrey);
        canvas.restore();
    

    aliased

    我能做些什么来消除内边缘的锯齿?

    我不想因为在中间画一个灰色的圆圈而作弊,因为对话框是稍微透明的。这种透明度在其他背景上没有那么微妙。)

    4 回复  |  直到 7 年前
        1
  •  21
  •   double-beep Alien Li    5 年前

    据我所知,你不能剪辑区域。

    我建议改用位图掩蔽。将粉色、白色和浅灰色前景渲染到一个位图,将外/内圆遮罩(灰度alpha通道)渲染到另一个位图,然后使用 Paint.setXfermode 以遮罩作为其alpha通道渲染前景位图。

    ApiDemos 源代码 here .

        2
  •  5
  •   HRJ    11 年前

    我知道这不是一个一般的答案,但在这个特殊的情况下,你可以画一个粗笔划宽度的弧,而不是圆+遮罩。

        3
  •  3
  •   Henry    8 年前

    我也有同样的问题。我尝试使用位图掩蔽(xFermode)来修复别名,但它很重。

    对于API<19,我使用位图掩蔽方式,对于API>=19岁,我曾经 Path.Op REVERSE_DIFFERENCE path 以及 shape (属于路径类型)。您可以在API 19及更高版本的路径上执行操作。

    非常适合我!

        4
  •  0
  •   Zhang Xiang    8 年前

    您可以尝试以下代码:

    public class GrowthView extends View {
    private static final String TAG = "GrowthView";
    private int bgColor = Color.parseColor("#33485d");
    private int valColor = Color.parseColor("#ecb732");
    private int[] scores = new int[]{0, 10, 80, 180, 800, 5000, 20000, 50000, 100000};
    
    private Context mContext;
    
    private float w;
    private float h;
    
    private Paint bgPaint;
    private Paint growthPaint;
    private Paint textPaint;
    private Paint clipPaint;
    private Path bgPath;
    private Path bgClipPath;
    private Path growthPath;
    
    private int growthValue = 0;
    
    private float bgFullAngle = 240.0f;
    private float gapAngle = bgFullAngle / (scores.length - 1);
    
    private float gapRadius = 21.5f;//实际为21px 略大半个像素避免path无法缝合error
    private float outerRadius = 240.0f;
    private float innerRadius = outerRadius - gapRadius * 2;
    
    private RectF outerRecF;
    private RectF innerRecF;
    private RectF leftBoundRecF;
    private RectF rightBoundRecF;
    
    public GrowthView(Context context) {
        this(context, null);
    }
    
    public GrowthView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    public GrowthView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.mContext = context;
        init();
    }
    
    private void init() {
        Xfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.DARKEN);
    
        bgPaint = new Paint();
        bgPaint.setStyle(Paint.Style.FILL);
        bgPaint.setColor(bgColor);
        bgPaint.setStrokeWidth(0.1f);
        bgPaint.setAntiAlias(true);
    
        growthPaint = new Paint();
        growthPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        growthPaint.setColor(valColor);
        growthPaint.setStrokeWidth(1f);
        growthPaint.setAntiAlias(true);
    
        clipPaint = new Paint();
        clipPaint.setStyle(Paint.Style.FILL);
        clipPaint.setColor(Color.WHITE);
        clipPaint.setStrokeWidth(.1f);
        clipPaint.setAntiAlias(true);
        clipPaint.setXfermode(xFermode);
    
        textPaint = new Paint();
        textPaint.setTextSize(96);//todo comfirm the textSize
        textPaint.setStrokeWidth(1f);
        textPaint.setAntiAlias(true);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setColor(valColor);
    
    
    
        bgPath = new Path();
        growthPath = new Path();
    
        //todo 暂定中心点为屏幕中心
        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getMetrics(metrics);
        w = metrics.widthPixels;
        h = metrics.heightPixels;
    
        outerRecF = new RectF(w / 2 - outerRadius, h / 2 - outerRadius, w / 2 + outerRadius, h / 2 + outerRadius);
        innerRecF = new RectF(w / 2 - innerRadius, h / 2 - innerRadius, w / 2 + innerRadius, h / 2 + innerRadius);
    
        rightBoundRecF = new RectF(w / 2 + (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 - gapRadius,
                h / 2 + (innerRadius + gapRadius) / 2 - gapRadius,
                w / 2 + (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 + gapRadius,
                h / 2 + (innerRadius + gapRadius) / 2 + gapRadius);
    
        leftBoundRecF = new RectF(w / 2 - (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 - gapRadius,
                h / 2 + (innerRadius + gapRadius) / 2 - gapRadius,
                w / 2 - (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 + gapRadius,
                h / 2 + (innerRadius + gapRadius) / 2 + gapRadius);
    
        bgClipPath = new Path();
        bgClipPath.arcTo(innerRecF, 150.0f, 359.9f, true);
        bgClipPath.close();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //bg
        float startAngle = 150.0f;
        float endRecfFullAngle = 180.0f;
        bgPath.arcTo(outerRecF, startAngle, bgFullAngle, true);
        bgPath.arcTo(rightBoundRecF, 30.0f, endRecfFullAngle, true);
        bgPath.arcTo(innerRecF, startAngle, bgFullAngle);
        bgPath.arcTo(leftBoundRecF, -30.0f, endRecfFullAngle);
        bgPath.rMoveTo(w / 2 - outerRadius * (float) Math.pow(3, 0.5) / 2, h / 2 + outerRadius / 2);
        bgPath.setFillType(Path.FillType.WINDING);
        bgPath.close();
    
        //growth
        if (getGrowthVal() != 0) {
            float temp = getGrowthAngle(getGrowthVal());
            growthPath.arcTo(outerRecF, startAngle, temp, true);
            growthPath.arcTo(getDynamicRecF(getGrowthVal()), getDynamicOriginAngle(getGrowthVal()), endRecfFullAngle, true);
            growthPath.arcTo(innerRecF, startAngle, temp);
            growthPath.arcTo(leftBoundRecF, -30.0f, endRecfFullAngle);
            growthPath.rMoveTo(w / 2 - outerRadius * (float) Math.pow(3, 0.5) / 2, h / 2 + outerRadius / 2);
            growthPath.close();
        }
        canvas.drawText(formatVal(getGrowthVal()), w / 2, h / 2, textPaint);
        canvas.clipPath(bgClipPath, Region.Op.DIFFERENCE);
        canvas.drawPath(bgPath, bgPaint);
        canvas.drawPath(growthPath, growthPaint);
        canvas.drawPath(bgClipPath, clipPaint);
    }
    
    private float getDynamicOriginAngle(int growthVal) {
        return growthVal <= 30 ? getGrowthAngle(growthVal) + 150 :
                getGrowthAngle(growthVal) - 210;
    }
    
    private RectF getDynamicRecF(int growthVal) {
        float dynamicAngle = getGrowthAngle(growthVal);
        //动态圆心
        float _w = w / 2 + (float) Math.sin(Math.toRadians(dynamicAngle - 120)) * (outerRadius - gapRadius);
        float _y = h / 2 - (float) Math.sin(Math.toRadians(dynamicAngle - 30)) * (outerRadius - gapRadius);
        return new RectF(_w - gapRadius, _y - gapRadius, _w + gapRadius, _y + gapRadius);
    }
    
    private int getGrowthVal() {
        return this.growthValue;
    }
    
    public void setGrowthValue(int value) {
        if (value < 0 || value > 100000) {
            try {
                throw new Exception("成长值不在范围内");
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
                e.printStackTrace();
            }
        }
        this.growthValue = value;
        invalidate();
    }
    
    private float getGrowthAngle(int growthVal) {
        return gapAngle * (getLevel(growthVal) - 1)
                + gapAngle * (growthVal - scores[getLevel(growthVal) - 1]) /
                (scores[getLevel(growthVal)] - scores[getLevel(growthVal) - 1]);
    }
    
    private int getLevel(int score) {
        return score < 0 ? -1 : score <= 10 ? 1 : score <= 80 ? 2 : score <= 180 ? 3 : score <= 800 ?
                4 : score <= 5000 ? 5 : score <= 20000 ? 6 : score <= 50000 ? 7 : 8;
    }
    
    private String formatVal(int value) {
        StringBuilder builder = new StringBuilder(String.valueOf(value));
        return value < 1000 ? builder.toString() : builder.insert(builder.length() - 3, ',').toString();
    }
    

    Result