Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
358 views
in Technique[技术] by (71.8m points)

path - How to Clip a Star in android ? But the appearance of the star is clear

First to see the below images.

1.Clipped star

2.Painted star

package com.syncfusion.rating;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Region;
import android.view.View;
/**
 * Created by chozarajan.pandiyarajan on 10/9/2015.
 */

public class SfRatingItem extends View {

private   int fillColor,minDim,topXPoint,topYPoint;
private double bigHypot,bigA,bigB,littleHypot,littleA,littleB,value;
private  Paint starPaint;
private Path path;

public SfRatingItem(Context context) {
    super(context);
    starPaint=new Paint();
    fillPaint=new Paint();
    path = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
    starPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    starPaint.setAntiAlias(true);

    minDim = Math.min(this.getWidth() - this.getPaddingLeft() -this.getPaddingRight(), this.getHeight() - this.getPaddingTop() - this.getPaddingBottom());

    bigHypot = (minDim / Math.cos(Math.toRadians(18)));
    bigB = minDim;
    bigA = Math.tan(Math.toRadians(18)) * bigB;

    littleHypot = bigHypot / (2 + Math.cos(Math.toRadians(72)) + Math.cos(Math.toRadians(72)));
    littleA = Math.cos(Math.toRadians(72)) * littleHypot;
    littleB = Math.sin(Math.toRadians(72)) * littleHypot;

    topXPoint = (this.getWidth() - this.getPaddingLeft() -this.getPaddingRight()) / 2;
    topYPoint =this.getPaddingTop();

    path.moveTo(topXPoint, topYPoint);
    path.lineTo((int) (topXPoint + bigA), (int) (topYPoint + bigB));
    path.lineTo((int) (topXPoint - littleA - littleB), (int) (topYPoint + littleB));
    path.lineTo((int) (topXPoint + littleA + littleB), (int) (topYPoint + littleB));
    path.lineTo((int) (topXPoint - bigA), (int) (topYPoint + bigB));
    path.lineTo(topXPoint, topYPoint);
    path.close();
    starPaint.setColor(Color.RED); 

 //Use below code to paint the star
 canvas.drawPath(path, starPaint);

 // Use below code to clip the star path.
     // canvas.clipPath(path, Region.Op.DIFFERENCE);
     //  canvas.drawColor(Color.WHITE);
    super.onDraw(canvas);
 } 

If you see the above images, you know the difference of this two images.

First one is clipped image. The clipped star of the edges is not clear.

Second one is Painted image. The painted star of the edges are clear.

Because in painted image, i am use to set true of setAntiAlise() property.

My question is how to get the clipped image edges are clear?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It seems that you can't perform Anti Aliasing with clipPath. Instead of using clipPath for masking, try bitmap masking (it's a bit more complicated that clipPath, but it gives a clear edges for the star). I have modified your code to use bitmap masking instead of clip path.

Code:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by chozarajan.pandiyarajan on 10/9/2015.
 */

public class SfRatingItem extends View {

    private int fillColor, minDim, topXPoint, topYPoint;
    private double bigHypot, bigA, bigB, littleHypot, littleA, littleB, value;
    private Paint starPaint;
    private Path path;
    private Bitmap starBitmap;
    private Bitmap backBitmap;

    public SfRatingItem(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public SfRatingItem(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize();
    }

    public SfRatingItem(Context context) {
        super(context);
        initialize();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        initialDraw();
        Paint q = new Paint(Paint.ANTI_ALIAS_FLAG);

        //canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),q); // expensive call, instead set a hardware layer
        setLayerType(LAYER_TYPE_HARDWARE, q);

        canvas.drawBitmap(backBitmap, 0, 0, q);
        q.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(starBitmap, 0, 0, q);
        q.setXfermode(null);

    }

    private void initialDraw() {
        starPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        starPaint.setAntiAlias(true);

        minDim = Math.min(this.getWidth() - this.getPaddingLeft() - this.getPaddingRight(), this.getHeight() - this.getPaddingTop() - this.getPaddingBottom());

        bigHypot = (minDim / Math.cos(Math.toRadians(18)));
        bigB = minDim;
        bigA = Math.tan(Math.toRadians(18)) * bigB;

        littleHypot = bigHypot / (2 + Math.cos(Math.toRadians(72)) + Math.cos(Math.toRadians(72)));
        littleA = Math.cos(Math.toRadians(72)) * littleHypot;
        littleB = Math.sin(Math.toRadians(72)) * littleHypot;

        topXPoint = (this.getWidth() - this.getPaddingLeft() - this.getPaddingRight()) / 2;
        topYPoint = this.getPaddingTop();

        path.moveTo(topXPoint, topYPoint);
        path.lineTo((int) (topXPoint + bigA), (int) (topYPoint + bigB));
        path.lineTo((int) (topXPoint - littleA - littleB), (int) (topYPoint + littleB));
        path.lineTo((int) (topXPoint + littleA + littleB), (int) (topYPoint + littleB));
        path.lineTo((int) (topXPoint - bigA), (int) (topYPoint + bigB));
        path.lineTo(topXPoint, topYPoint);
        path.close();

        // Draw the STAR mask in a bitmap
        RectF bounds = new RectF();
        path.computeBounds(bounds, true);
        starBitmap = Bitmap.createBitmap((int) bounds.width(), (int) bounds.height(), Bitmap.Config.ARGB_8888);
        Canvas starCanvas = new Canvas(starBitmap);
        starPaint.setColor(Color.BLACK);
        starCanvas.drawPath(path, starPaint);

        // Draw the background rectangle in a bitmap
        starPaint.setColor(Color.RED);
        backBitmap = Bitmap.createBitmap((int) bounds.width(), (int) bounds.height(), Bitmap.Config.ARGB_8888);
        Canvas backCanvas = new Canvas(backBitmap);
        final Rect backRect = new Rect(0, 0, backBitmap.getWidth(), backBitmap.getHeight());
        backCanvas.drawRect(backRect, starPaint);
    }

    private void initialize() {
        starPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        path = new Path();
    }
}

Here I have created two bitmaps, one for the star mask and the other for the background rectangle. Then using Paint.setXfermode(), I have masked one bitmap over the other. You can dynamically show the partial filling by modifying the width of the background rectangle (i.e, width of backRect). Currently I am creating the bitmaps in the onDraw() method, but this is a bad idea and you need to do this in the `constructor itself, based on the size of the star required.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...