开发者

How to draw a triangle, a star, a square or a heart on the canvas?

开发者 https://www.devze.com 2023-03-27 03:53 出处:网络
I am able to draw a circle and a rectangle on canvas by using path.开发者_Python百科addCircle() and

I am able to draw a circle and a rectangle on canvas by using

path.开发者_Python百科addCircle()

and

path.addRect().

And now I am wondering how to draw a triangle or a star or a square or a heart?


For future direct answer seekers, I have drawn an almost symmetric star using canvas, as shown in the image:

How to draw a triangle, a star, a square or a heart on the canvas?

The main tool is using Paths.

Assuming you have setup:

Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);

Path path = new Path();

Then in you onDraw you can use the path like I do below. It will scale properly to any sizes canvas

@Override
protected void onDraw(Canvas canvas) {

    float mid = getWidth() / 2;
    float min = Math.min(getWidth(), getHeight());
    float fat = min / 17;
    float half = min / 2;
    float rad = half - fat;
    mid = mid - half;

    paint.setStrokeWidth(fat);
    paint.setStyle(Paint.Style.STROKE);

    canvas.drawCircle(mid + half, half, rad, paint);

    path.reset();

    paint.setStyle(Paint.Style.FILL);


        // top left
        path.moveTo(mid + half * 0.5f, half * 0.84f);
        // top right
        path.lineTo(mid + half * 1.5f, half * 0.84f);
        // bottom left
        path.lineTo(mid + half * 0.68f, half * 1.45f);
        // top tip
        path.lineTo(mid + half * 1.0f, half * 0.5f);
        // bottom right
        path.lineTo(mid + half * 1.32f, half * 1.45f);
        // top left
        path.lineTo(mid + half * 0.5f, half * 0.84f);

        path.close();
        canvas.drawPath(path, paint);

    super.onDraw(canvas);

}


For everybody that needs a heart shape:

    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Paint.Style;
    import android.graphics.Path;
    import android.view.View;

    public class Heart extends View {

        private Path path;

        private Paint paint;

        public Heart(Context context) {
            super(context);

            path = new Path();
            paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        }

            @Override
            protected void onDraw(Canvas canvas) {
                super.onDraw(canvas);

                // Fill the canvas with background color
                canvas.drawColor(Color.WHITE);
                paint.setShader(null);

                float width = getContext().getResources().getDimension(R.dimen.heart_width);
                float height = getContext().getResources().getDimension(R.dimen.heart_height);

                // Starting point
                path.moveTo(width / 2, height / 5); 

                // Upper left path
                path.cubicTo(5 * width / 14, 0,
                        0, height / 15,
                        width / 28, 2 * height / 5);

                // Lower left path
                path.cubicTo(width / 14, 2 * height / 3,
                        3 * width / 7, 5 * height / 6,
                        width / 2, height);

                // Lower right path
                path.cubicTo(4 * width / 7, 5 * height / 6,
                        13 * width / 14, 2 * height / 3,
                        27 * width / 28, 2 * height / 5);

                // Upper right path
                path.cubicTo(width, height / 15,
                        9 * width / 14, 0,
                        width / 2, height / 5);

                paint.setColor(Color.RED);
                paint.setStyle(Style.FILL);
                canvas.drawPath(path, paint);

            }
    }

Sorry for all the numbers but these worked best for me :) The result looks like this:

How to draw a triangle, a star, a square or a heart on the canvas?


You have to find out the math behind that figures. The triangle and the star are quite easy to draw. Here is how you can draw a heart: http://www.mathematische-basteleien.de/heart.htm

To draw special paths you should create them by adding points, ellipses etc. The canvas supports a clipping mask of a specified path, so you can set the clipping mask of a heart, push the paths to the matrix, draw the content of the heart, and then pop it again.

That's what I'm doing to achieve a simulated 2D page curl effect on andriod: http://code.google.com/p/android-page-curl/

Hope this helps!


this method will return a path with the number of corners given inside a square of the given width. Add more parameters to handle small radius and such things.

    private Path createStarBySize(float width, int steps) {
    float halfWidth = width / 2.0F;
    float bigRadius = halfWidth;
    float radius = halfWidth / 2.0F;
    float degreesPerStep = (float) Math.toRadians(360.0F / (float) steps);
    float halfDegreesPerStep = degreesPerStep / 2.0F;
    Path ret = new Path();
    ret.setFillType(FillType.EVEN_ODD);
    float max = (float) (2.0F* Math.PI);
    ret.moveTo(width, halfWidth);
    for (double step = 0; step < max; step += degreesPerStep) {
        ret.lineTo((float)(halfWidth + bigRadius * Math.cos(step)), (float)(halfWidth + bigRadius * Math.sin(step)));
        ret.lineTo((float)(halfWidth + radius * Math.cos(step + halfDegreesPerStep)), (float)(halfWidth + radius * Math.sin(step + halfDegreesPerStep)));
    }
    ret.close();
    return ret;
}


If you need to draw a star inside a square, you can use the code below.

posX and posY are the coordinates for the upper left corner of the square that encloses the tips of the star (the square is not actually drawn).

size is the width and height of the square.

a is the top tip of the star. The path is created clockwise.

This is by no means a perfect solution, but it gets the job done very quickly.

 public void drawStar(float posX, float posY, int size, Canvas canvas){

            int hMargin = size/9;
            int vMargin = size/4;

            Point a = new Point((int) (posX + size/2), (int) posY);
            Point b = new Point((int) (posX + size/2 + hMargin), (int) (posY + vMargin));
            Point c = new Point((int) (posX + size), (int) (posY + vMargin));
            Point d = new Point((int) (posX + size/2 + 2*hMargin), (int) (posY + size/2 + vMargin/2));
            Point e = new Point((int) (posX + size/2 + 3*hMargin), (int) (posY + size));
            Point f = new Point((int) (posX + size/2), (int) (posY + size - vMargin));
            Point g = new Point((int) (posX + size/2 - 3*hMargin), (int) (posY + size));
            Point h = new Point((int) (posX + size/2 - 2*hMargin), (int) (posY + size/2 + vMargin/2));
            Point i = new Point((int) posX, (int) (posY + vMargin));
            Point j = new Point((int) (posX + size/2 - hMargin), (int) (posY + vMargin));

            Path path = new Path();
            path.moveTo(a.x, a.y);
            path.lineTo(b.x, b.y);
            path.lineTo(c.x, c.y);
            path.lineTo(d.x, d.y);
            path.lineTo(e.x, e.y);
            path.lineTo(f.x, f.y);
            path.lineTo(f.x, f.y);
            path.lineTo(g.x, g.y);
            path.lineTo(h.x, h.y);
            path.lineTo(i.x, i.y);
            path.lineTo(j.x, j.y);
            path.lineTo(a.x, a.y);

            path.close();

            canvas.drawPath(path, paint);
}


In addition to ellipse and rectangular you will need those two (as minimum):

drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
drawLines(float[] pts, int offset, int count, Paint paint)

example: How to draw a line in android

Documentation on Canvas: http://developer.android.com/reference/android/graphics/Canvas.html


Its very good to use instance of Shape class ))

 @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    HeartShape shape = new HeartShape();
    ShapeDrawable shapeDrawable = new ShapeDrawable(shape);
    shapeDrawable.setColorFilter(new LightingColorFilter(0, Color.BLUE));

    LinearLayout linearLayout = new LinearLayout(this);
    linearLayout.setLayoutParams(new LinearLayout.LayoutParams(350 * 3, 350 * 3));
    linearLayout.setBackground(shapeDrawable);

    setContentView(linearLayout);
  }

Create a shape class which was render nice Heart

 public class HeartShape extends Shape {

  private final int INVALID_SIZE = -1;

  private Path mPath = new Path();
  private RectF mRectF = new RectF();

  private float mOldWidth = INVALID_SIZE;
  private float mOldHeight = INVALID_SIZE;

  private float mScaleX, mScaleY;

  public HeartShape() {

  }

  @Override
  public void draw(Canvas canvas, Paint paint) {
    canvas.save();
    canvas.scale(mScaleX, mScaleY);

    float width = mRectF.width();
    float height = mRectF.height();

    float halfWidth = width/2;
    float halfHeight = height/2;

    float stdDestX = 5 * width / 14;
    float stdDestY = 2 * height / 3;

    PointF point1 = new PointF(stdDestX, 0);
    PointF point2 = new PointF(0, height / 15);
    PointF point3 = new PointF(stdDestX / 5, stdDestY);
    PointF point4 = new PointF(stdDestX, stdDestY);

    // Starting point
    mPath.moveTo(halfWidth, height / 5);

    mPath.cubicTo(point1.x, point1.y, point2.x, point2.y, width / 28, 2 * height / 5);
    mPath.cubicTo(point3.x, point3.y, point4.x, point4.y, halfWidth, height);

    canvas.drawPath(mPath, paint);

    canvas.scale(-mScaleX, mScaleY, halfWidth, halfHeight);
    canvas.drawPath(mPath, paint);

    canvas.restore();
  }

  @Override
  protected void onResize(float width, float height) {
    mOldWidth = mOldWidth == INVALID_SIZE ? width : Math.max(1, mOldWidth);
    mOldHeight = mOldHeight == INVALID_SIZE ? height : Math.max(1, mOldHeight);

    width = Math.max(1, width);
    height = Math.max(1, height);

    mScaleX = width / mOldWidth;
    mScaleY = height / mOldHeight;

    mOldWidth = width;
    mOldHeight = height;


    mRectF.set(0, 0, width, height);
  }

  @Override
  public void getOutline(@NonNull Outline outline) {
    // HeartShape not supported outlines
  }

  @Override
  public HeartShape clone() throws CloneNotSupportedException {
    HeartShape shape = (HeartShape) super.clone();
    shape.mPath = new Path(mPath);
    return shape;
  }

}

How to draw a triangle, a star, a square or a heart on the canvas?


Well if you want to draw only the shapes you specify I recommend first creat a class for each shape and make each shape implement draw() method where you can pass canvas and paint object.

How to draw a triangle, a star, a square or a heart on the canvas?

For example to create multiple stars, first create a class "Star" and implement the logic there.

class Star(
        var cx: Float = 0f,
        var cy: Float = 0f,
        var radius: Float = 0f,
        var angle: Float = 0f,
        var color: Int = Color.WHITE,
        var numberOfSpikes: Int = 5,
        var depth: Float = 0.4f
    ) {

        val path: Path = Path()
        val point: PointF = PointF()

        fun init() {
            path.rewind()

            var totalAngle = 0f
            for (i in 0 until numberOfSpikes * 2) {

                val x = cx
                var y = cy
                y -= if (i % 2 != 0) (radius * depth)
                else (radius * 1f)

                StaticMethods.rotate(cx, cy, x, y, totalAngle, false, point)
                totalAngle += 360f / (numberOfSpikes * 2)
                if (i == 0) {
                    path.moveTo(point.x, point.y)
                } else {
                    path.lineTo(point.x, point.y)
                }
            }
        }

        fun draw(canvas: Canvas, paint: Paint) {

            paint.apply {
                style = Paint.Style.FILL
                color = this@Star.color
            }
            canvas.drawPath(path, paint)
        }
    }


object StaticMethods {
    
 /**
     * Rotate a point around a center with given angle
     * @param cx rotary center point x coordinate
     * @param cy rotary center point y coordinate
     * @param x x coordinate of the point that will be rotated
     * @param y y coordinate of the point that will be rotated
     * @param angle angle of rotation in degrees
     * @param anticlockWise rotate clockwise or anti-clockwise
     * @param resultPoint object where the result rotational point will be stored
     */
    fun rotate(cx: Float, cy: Float, x: Float, y: Float, angle: Float, anticlockWise: Boolean = false, resultPoint: PointF = PointF()): PointF {

        if (angle == 0f) {
            resultPoint.x = x
            resultPoint.y = y
            return resultPoint
        }

        val radians = if (anticlockWise) {
            (Math.PI / 180) * angle
        } else {
            (Math.PI / -180) * angle
        }

        val cos = Math.cos(radians)
        val sin = Math.sin(radians)
        val nx = (cos * (x - cx)) + (sin * (y - cy)) + cx
        val ny = (cos * (y - cy)) - (sin * (x - cx)) + cy

        resultPoint.x = nx.toFloat()
        resultPoint.y = ny.toFloat()
        return resultPoint
    }
    /**
     * Inline function that is called, when the final measurement is made and
     * the view is about to be draw.
     */
    inline fun View.afterMeasured(crossinline function: View.() -> Unit) {
        viewTreeObserver.addOnGlobalLayoutListener(object :
            ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                if (measuredWidth > 0 && measuredHeight > 0) {
                    viewTreeObserver.removeOnGlobalLayoutListener(this)
                    function()
                }
            }
        })
     }
}

And then create a custom view where you can draw your shapes, below is the code that creates 100 random stars and draws them on canvas.

class StarView : View {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    val stars: ArrayList<Helper.Star> = arrayListOf()

    val paint: Paint = Paint().apply {
        isAntiAlias = true
    }

    init {

        // after the view is measured and we have the width and height
        afterMeasured {

            // create 100 stars
            for (i in 0 until 100) {

                val star = Helper.Star(
                    cx = width * Math.random().toFloat(),
                    cy = height * Math.random().toFloat(),
                    radius = width * 0.1f * Math.random().toFloat(),
                )
                star.init()
                stars.add(star)
            }
        }

    }


    override fun onDraw(canvas: Canvas) {
        stars.forEach {
            it.draw(canvas, paint)
        }
    }
}

Here is the result:

How to draw a triangle, a star, a square or a heart on the canvas?


If you want to create a lot of different complex shapes you can use this library. Where you can pass svg shapes using the Path Data as a string. By first creating complex shapes using any SVG vector editor such as

  • Microsoft Expression Design
  • Adobe Illustrator
  • Inkscape

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号