Egis
Egis

Reputation: 5141

How to draw filled triangle on android Canvas

I have class MyView that extends View class. MyView should draw filled triangle. I drew a triangle but I cannot get it filled. This is my onDraw() method:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint paint = new Paint();

    paint.setColor(android.graphics.Color.BLACK);
    canvas.drawPaint(paint);

    paint.setStrokeWidth(4);
    paint.setColor(android.graphics.Color.RED);
    paint.setStyle(Paint.Style.FILL_AND_STROKE);
    paint.setAntiAlias(true);

    Point a = new Point(0, 0);
    Point b = new Point(0, 100);
    Point c = new Point(87, 50);

    Path path = new Path();
    path.setFillType(FillType.EVEN_ODD);
    path.moveTo(a.x, a.y);
    path.lineTo(b.x, b.y);
    path.moveTo(b.x, b.y);
    path.lineTo(c.x, c.y);
    path.moveTo(c.x, c.y);
    path.lineTo(a.x, a.y);
    path.close();

    canvas.drawPath(path, paint);
}

This is what I get as a result:

enter image description here

Upvotes: 23

Views: 30972

Answers (5)

Jarvis
Jarvis

Reputation: 402

I recently created a small demo app drawing various shapes, including triangles, rectangles and spirals. Here is the code for drawing the triangles. For full context, reference the project. In this app, the user can draw a triangle as a single drawing or a composition of two triangles forming into one as the user scrolls. The increasing triangle growth is accomplished through the principles of Triangle Similarity, with each smaller triangle similar to the large triangle in the end.

Project: https://github.com/jdgreene2008/android_custom_views

Source Excerpt: https://github.com/jdgreene2008/android_custom_views/blob/master/CustomUiComponents/app/src/main/java/com/jarvis/dragdropresearch/views/FlashShapeView.java

 private void drawTriangleShape(Canvas canvas, RectF bounds,
            TriangleInterpolator triangleInterpolator, Paint paint) {
        paint.setStyle(Paint.Style.FILL);

        float baseInterpolation = triangleInterpolator
                .getInterpolatedValues()[TriangleInterpolator.INTERPOLATION_VALUES_BASE];
        float altitudeInterpolation = triangleInterpolator
                .getInterpolatedValues()[TriangleInterpolator.INTERPOLATION_VALUES_ALTITUDE];

        // *** Construct the Left Triangle ** //

        // Bottom left vertex
        float bottomLeftX = bounds.left;
        float bottomLeftY = bounds.bottom;

        // Bottom right corner
        float bottomRightX = bottomLeftX + baseInterpolation;
        float bottomRightY = bounds.bottom;

        //Top Vertex
        float topX = bottomRightX;
        float topY = bottomRightY - altitudeInterpolation;

        Path leftTriangle = new Path();
        leftTriangle.lineTo(bottomLeftX, bottomLeftY);
        leftTriangle.lineTo(bottomRightX, bottomRightY);
        leftTriangle.lineTo(topX, topY);
        leftTriangle.lineTo(bottomLeftX, bottomLeftY);

        canvas.drawPath(leftTriangle, paint);

        if (triangleInterpolator.isSymmetric()) {
            // *** Construct the Right Triangle ** //

            bottomLeftX = bounds.right - baseInterpolation;
            bottomLeftY = bounds.bottom;

            bottomRightX = bounds.right;
            bottomRightY = bounds.bottom;

            topX = bottomLeftX;
            topY = bottomRightY - altitudeInterpolation;

            Path rightTriangle = new Path();
            rightTriangle.lineTo(bottomLeftX, bottomLeftY);
            rightTriangle.lineTo(bottomRightX, bottomRightY);
            rightTriangle.lineTo(topX, topY);
            rightTriangle.lineTo(bottomLeftX, bottomLeftY);

            canvas.drawPath(rightTriangle, paint);
        }
    }

Upvotes: 1

Joe Maher
Joe Maher

Reputation: 5460

This answer provides a bit of clarity on where the numbers given in the answer by @Egis come from. (this will draw an upside down equilateral triangle and is written in kotlin)

class TriangleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {

    val paint = Paint()
    val path = Path()

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas ?: return
        canvas.drawPath(configurePath(canvas.width.toFloat(), path), configurePaint(paint))
    }

    fun getHeight(width: Double): Float {
        return Math.sqrt((Math.pow(width, 2.0) - Math.pow((width / 2), 2.0))).toFloat()
    }

    fun configurePaint(paint: Paint): Paint {
        paint.color = android.graphics.Color.WHITE
        paint.isAntiAlias = true

        return paint
    }

    fun configurePath(width: Float, path: Path): Path {
        path.lineTo((width / 2f), getHeight(width.toDouble()))
        path.lineTo(width, 0F)
        path.lineTo(0f, 0f)

        return path
    }
}

The get height function is Pythagoras' Theorem and will always find the height of an equilateral triangle to be ~87% of its side length

Gist can be found here, it contains code for the other direction

Upvotes: 1

Bismeet Singh
Bismeet Singh

Reputation: 65

I would like to point out that you should never initiialize an object from onDraw() as it gets called multiple times and leads to performance problems.

Upvotes: 0

Egis
Egis

Reputation: 5141

I've found the answer

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint paint = new Paint();

    paint.setColor(android.graphics.Color.BLACK);
    canvas.drawPaint(paint);

    paint.setStrokeWidth(4);
    paint.setColor(android.graphics.Color.RED);
    paint.setStyle(Paint.Style.FILL_AND_STROKE);
    paint.setAntiAlias(true);

    Point a = new Point(0, 0);
    Point b = new Point(0, 100);
    Point c = new Point(87, 50);

    Path path = new Path();
    path.setFillType(FillType.EVEN_ODD);
    path.lineTo(b.x, b.y);
    path.lineTo(c.x, c.y);
    path.lineTo(a.x, a.y);
    path.close();

    canvas.drawPath(path, paint);
}

Upvotes: 29

Related Questions