[android] Making a triangle shape using xml definitions?

Is there a way that I can specify a triangle shape in an xml file?

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="triangle">
  <stroke android:width="1dip" android:color="#FFF" />
  <solid android:color="#FFF" />
</shape>

can we do this with a path shape or something? I just need an equilateral triangle.

Thanks

This question is related to android xml

The answer is


Using vector drawable:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
   android:width="24dp"
   android:height="24dp"
   android:viewportWidth="24.0"
   android:viewportHeight="24.0">
   <path
        android:pathData="M0,0 L24,0 L0,24 z"
        android:strokeColor="@color/color"
        android:fillColor="@color/color"/>
</vector>

enter image description here


The solution of Jacek Milewski works for me and, based on his solution, if you need and inversed triangle you can use this:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <rotate
            android:fromDegrees="45"
            android:toDegrees="45"
            android:pivotX="135%" 
            android:pivotY="15%">
            <shape android:shape="rectangle">    
                <solid android:color="@color/aquamarine" />
            </shape>
        </rotate>
    </item>
</layer-list>

You can add following triangle in background using following xml

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="100dp"
    android:width="100dp"
    android:viewportHeight="100"
    android:viewportWidth="100" >
    <group
        android:name="triableGroup">
        <path
            android:name="triangle"
            android:fillColor="#848af8"
            android:pathData="M 0,20 L 0,0 L 100,0 L 100,20 L 54,55  l -1,0.6  l -1,0.4  l -1,0.2  l -1,0   l -1,-0  l -1,-0.2  l -1,-0.4  l -1,-0.6    L 46,55   L 0,20 -100,-100 Z" />
    </group>
</vector>

The whole logic to customize xml design is in pathData. Consider top-left as (0,0) and design the layout as per your requirement. Check this answer.


In this post I describe how to do it. And here is the XML defining triangle:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <rotate
            android:fromDegrees="45"
            android:toDegrees="45"
            android:pivotX="-40%"
            android:pivotY="87%" >
            <shape
                android:shape="rectangle" >
                <stroke android:color="@color/transparent" android:width="10dp"/>
                <solid
                    android:color="@color/your_color_here" />
            </shape>
        </rotate>
    </item>
</layer-list>

Refer to my post if something is unclear or you need explanation how it is built. It is rotated an cutout rectangle :) it is very smart and well working solution.

EDIT: to create an arrow pointing like --> use:

...
android:fromDegrees="45"
android:toDegrees="45"
android:pivotX="13%"
android:pivotY="-40%" >
...

And to create an arrow pointing like <-- use:

android:fromDegrees="45"
android:toDegrees="45"
android:pivotX="87%"
android:pivotY="140%" >

See answer here: Custom arrows without image: Android

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="32dp"
        android:height="24dp"
        android:viewportWidth="32.0"
        android:viewportHeight="24.0">
    <path android:fillColor="#e4e4e8"
          android:pathData="M0 0 h32 l-16 24 Z"/>
</vector>

arrow


Google provides a Equilateral triangle here.
Choose VectorDrawable so the size is flexible.
It' integrated into Android Studio as plugin.

If you have an SVG image, you can use this to convert it to VectorDrawable too.

Once you have a VectorDrawable, changing its colour and rotation is easy like others have mentioned.


<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <rotate
            android:fromDegrees="45"
            android:toDegrees="45"
            android:pivotX="-40%"
            android:pivotY="87%" >
            <shape
                android:shape="rectangle" >
                <stroke android:color="@android:color/transparent" android:width="0dp"/>
                <solid
                    android:color="#fff" />
            </shape>
        </rotate>
    </item>
</layer-list>

<TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="?"/>

You can get here more options.


For those who want a right triangle arrow, here you go:

STEP 1: Create a drawable XML file, copy and paste the following XML content into your drawable XML. (Please be informed that you can use any name for your drawable XML file. For my case, I name it "v_right_arrow")

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item >
        <rotate
            android:fromDegrees="45"
            android:toDegrees="-45"
            android:pivotX="15%"
            android:pivotY="-36%" >
            <shape
                android:shape="rectangle"  >
                <stroke android:color="@android:color/transparent" android:width="1dp"/>
                <solid
                    android:color="#000000"  />
            </shape>
        </rotate>
    </item>
</layer-list>

STEP 2: In your layout's XML, create a View and bind its background to the drawable XML that you have just created in STEP 1. For my case, I bind v_right_arrow to my View's background property.

<View
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="@drawable/v_right_arrow">
</View>

Sample output:

enter image description here

Hope this helps, good luck!


May I help you without using XML ?


Simply,

Custom Layout ( Slice ) :

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.graphics.Point;
import android.util.AttributeSet;
import android.view.View;

public class Slice extends View {

    Paint mPaint;

    Path mPath;

    public enum Direction {
        NORTH, SOUTH, EAST, WEST
    }

    public Slice(Context context) {
        super(context);
        create();
    }

    public Slice(Context context, AttributeSet attrs) {
        super(context, attrs);
        create();
    }

    public void setColor(int color) {
        mPaint.setColor(color);
        invalidate();
    }

    private void create() {
        mPaint = new Paint();
        mPaint.setStyle(Style.FILL);
        mPaint.setColor(Color.RED);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mPath = calculate(Direction.SOUTH);
        canvas.drawPath(mPath, mPaint);
    }

    private Path calculate(Direction direction) {
        Point p1 = new Point();
        p1.x = 0;
        p1.y = 0;

        Point p2 = null, p3 = null;

        int width = getWidth();

        if (direction == Direction.NORTH) {
            p2 = new Point(p1.x + width, p1.y);
            p3 = new Point(p1.x + (width / 2), p1.y - width);
        } else if (direction == Direction.SOUTH) {
            p2 = new Point(p1.x + width, p1.y);
            p3 = new Point(p1.x + (width / 2), p1.y + width);
        } else if (direction == Direction.EAST) {
            p2 = new Point(p1.x, p1.y + width);
            p3 = new Point(p1.x - width, p1.y + (width / 2));
        } else if (direction == Direction.WEST) {
            p2 = new Point(p1.x, p1.y + width);
            p3 = new Point(p1.x + width, p1.y + (width / 2));
        }

        Path path = new Path();
        path.moveTo(p1.x, p1.y);
        path.lineTo(p2.x, p2.y);
        path.lineTo(p3.x, p3.y);

        return path;
    }
}

Your Activity ( Example ) :

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;

public class Layout extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Slice mySlice = new Slice(getApplicationContext());
        mySlice.setBackgroundColor(Color.WHITE);
        setContentView(mySlice, new LinearLayout.LayoutParams(
                LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
    }
}

Working Example :

enter image description here


Another absolutely simple Calculate function you may interested in ..

private Path Calculate(Point A, Point B, Point C) {
    Path Pencil = new Path();
    Pencil.moveTo(A.x, A.y);
    Pencil.lineTo(B.x, B.y);
    Pencil.lineTo(C.x, C.y);
    return Pencil;
}

Using the solution of Jacek Milewski I made an oriented down angle with a transparent background.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <rotate
            android:fromDegrees="135"
            android:pivotX="65%"
            android:pivotY="20%"
            android:toDegrees="135"
            >
            <shape android:shape="rectangle">
                <stroke
                    android:width="1dp"
                    android:color="@color/blue"
                    />
                <solid android:color="@color/transparent" />
            </shape>
        </rotate>
    </item>
</layer-list>

You can change android:pivotX and android:pivotY to shift the angle.

Usage:

<ImageView
    android:layout_width="10dp"
    android:layout_height="10dp"
    android:src="@drawable/ic_angle_down"
    />

enter image description here

Parameters depend on the size of the image. For instance, if ImageView has size 100dp*80dp, you should use these constants:

<rotate
    android:fromDegrees="135"
    android:pivotX="64.5%"
    android:pivotY="19%"
    android:toDegrees="135"
    >

enter image description here


I would definetely go for implementing a View in this case:

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

public class TriangleShapeView extends View {

    public TriangleShapeView(Context context) {
        super(context);
    }

    public TriangleShapeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

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

        int w = getWidth() / 2;

        Path path = new Path();
        path.moveTo( w, 0);
        path.lineTo( 2 * w , 0);
        path.lineTo( 2 * w , w);
        path.lineTo( w , 0);
        path.close();

        Paint p = new Paint();
        p.setColor( Color.RED );

        canvas.drawPath(path, p);
    }
}

Make use of it in your layouts as follows:

<TriangleShapeView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff487fff">
</TriangleShapeView>

Using this implementation will give you the following result:

enter image description here


 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item>
            <rotate
                android:fromDegrees="45"
                android:pivotX="135%"
                android:pivotY="1%"
                android:toDegrees="45">
                <shape android:shape="rectangle">
                    <stroke
                        android:width="-60dp"
                        android:color="@android:color/transparent" />
                    <solid android:color="@color/orange" />
                </shape>
            </rotate>
        </item>
    </layer-list>

  <LinearLayout
        android:id="@+id/ly_fill_color_shape"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center"
        android:background="@drawable/shape_triangle"
        android:gravity="center"
        android:orientation="horizontal" >
    </LinearLayout>

<item>
    <rotate
        android:fromDegrees="45"
        android:pivotX="-15%"
        android:pivotY="77%"
        android:toDegrees="45" >
        <shape android:shape="rectangle" >
            <stroke
                android:width="2dp"
                android:color="@color/black_color" />

            <solid android:color="@color/white" />

            <padding android:left="1dp" />
        </shape>
    </rotate>
</item>
<item android:top="200dp">
    <shape android:shape="line" >
        <stroke
            android:width="1dp"
            android:color="@color/black_color" />

        <solid android:color="@color/white" /> 
    </shape>
</item>


I have never done this, but from what I understand you can use the PathShape class: http://developer.android.com/reference/android/graphics/drawable/shapes/PathShape.html


I my be late to party and I came across same problem and Google pointed me to this StackOverflow thread as first result.

I tried using xml way to add triangle and find out a problem that the triangle shape via xml approach is taking more space than it appears.

See screen shot with layout bounds on

enter image description here

So ended up making this custom view class which can draws Triangle of any of following types:-

  1. up pointing
  2. down pointing
  3. left pointing &
  4. right pointing

asas

package com.hiteshsahu.materialupvotewidget;    
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;

public class TriangleShapeView extends View {

    private int colorCode = Color.DKGRAY;

    public int getColorCode() {
        return colorCode;
    }

    public void setColorCode(int colorCode) {
        this.colorCode = colorCode;
    }

    public TriangleShapeView(Context context) {
        super(context);
        if (isInEditMode())
            return;
    }

    public TriangleShapeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (isInEditMode())
            return;
    }

    public TriangleShapeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        if (isInEditMode())
            return;
    }


    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int w = getWidth() / 2;
        int h = getHeight() / 2;

        //Choose what type of triangle you want here
        Path path = getLeftTriangle(w, h);

        path.close();
        Paint p = new Paint();
        p.setColor(colorCode);
        p.setAntiAlias(true);

        canvas.drawPath(path, p);
    }

    @NonNull
    /**
     * Return Path for down facing triangle
     */
    private Path getInvertedTriangle(int w, int h) {
        Path path = new Path();
        path.moveTo(0, 0);
        path.lineTo(w, 2 * h);
        path.lineTo(2 * w, 0);
        path.lineTo(0, 0);
        return path;
    }

    @NonNull
    /**
     * Return Path for Up facing triangle
     */
    private Path getUpTriangle(int w, int h) {
        Path path = new Path();
        path.moveTo(0, 2 * h);
        path.lineTo(w, 0);
        path.lineTo(2 * w, 2 * h);
        path.lineTo(0, 2 * h);
        return path;
    }

    @NonNull
    /**
     * Return Path for Right pointing triangle
     */
    private Path getRightTriangle(int w, int h) {
        Path path = new Path();
        path.moveTo(0, 0);
        path.lineTo(2 * w, h);
        path.lineTo(0, 2 * h);
        path.lineTo(0, 0);
        return path;
    }

    @NonNull
    /**
     * Return Path for Left pointing triangle
     */
    private Path getLeftTriangle(int w, int h) {
        Path path = new Path();
        path.moveTo(2 * w, 0);
        path.lineTo(0, h);
        path.lineTo(2 * w, 2 * h);
        path.lineTo(2 * w, 0);
        return path;
    }
}

You can Simply use it in xml layout like this

 <com.hiteshsahu.materialupvote.TriangleShapeView
            android:layout_width="50dp"
            android:layout_height="50dp"></com.hiteshsahu.materialupvote.TriangleShapeView>

I know OP want solutions in xml solution but as I pointed out problem with xml approach . I Hope it might help somebody.


I provide this customView below if you don't want to hack xml. Please have a try.


/**
 * TriangleView
 *
 * @author Veer
 * @date 2020-09-03
 */
class TriangleView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    private var triangleColor: Int = 0
    private var direction = Direction.Bottom

    private val paint by lazy {
        Paint().apply {
            isAntiAlias = true
            style = Paint.Style.FILL
            color = triangleColor
        }
    }

    init {
        initStyle(context, attrs, defStyleAttr)
    }

    private fun initStyle(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int
    ) {
        val ta = context.obtainStyledAttributes(attrs, R.styleable.TriangleView, defStyleAttr, 0)
        with(ta) {
            triangleColor =
                getColor(R.styleable.TriangleView_triangle_background, Color.parseColor("#000000"))

            val directionValue =
                getInt(R.styleable.TriangleView_triangle_direction, Direction.Bottom.value)
            direction = when (directionValue) {
                Direction.Top.value -> Direction.Top
                Direction.Bottom.value -> Direction.Bottom
                Direction.Left.value -> Direction.Left
                Direction.Right.value -> Direction.Right
                else -> Direction.Bottom
            }

            recycle()
        }
    }

    override fun onDraw(canvas: Canvas) {
        calculatePath(direction).let {
            canvas.drawPath(it, paint)
        }
    }

    private fun calculatePath(direction: Direction): Path {
        var p1: Point? = null
        var p2: Point? = null
        var p3: Point? = null

        val width = width
        val height = height

        when (direction) {
            Direction.Top -> {
                p1 = Point(0, height)
                p2 = Point(width / 2, 0)
                p3 = Point(width, height)
            }
            Direction.Bottom -> {
                p1 = Point(0, 0)
                p2 = Point(width / 2, height)
                p3 = Point(width, 0)
            }
            Direction.Left -> {
                p1 = Point(width, 0)
                p2 = Point(0, height / 2)
                p3 = Point(width, height)
            }
            Direction.Right -> {
                p1 = Point(0, 0)
                p2 = Point(width, height / 2)
                p3 = Point(0, height)
            }
        }

        val path = Path()
        path.moveTo(p1.x.toFloat(), p1.y.toFloat())
        path.lineTo(p2.x.toFloat(), p2.y.toFloat())
        path.lineTo(p3.x.toFloat(), p3.y.toFloat())
        return path
    }

    private enum class Direction(val value: Int) {
        Top(0),
        Bottom(1),
        Left(2),
        Right(3)
    }
}

<declare-styleable name="TriangleView">
    <attr name="triangle_direction" format="enum">
        <enum name="top" value="0" />
        <enum name="bottom" value="1" />
        <enum name="left" value="2" />
        <enum name="right" value="3" />
    </attr>
    <attr name="triangle_background" format="reference|color" />
</declare-styleable>

You can use vector to make triangle like this

ic_triangle_right.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:pathData="M0,12l0,12 11.5,-5.7c6.3,-3.2 11.5,-6 11.5,-6.3 0,-0.3 -5.2,-3.1 -11.5,-6.3l-11.5,-5.7 0,12z"
        android:strokeColor="#00000000"
        android:fillColor="#000000"/>
</vector>

Then use it like

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/ic_triangle_right"
    />

For change the color and direction, use android:tint and android:rotation

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/ic_triangle_right"
    android:rotation="180" // change direction
    android:tint="#00f" // change color
    />

Result

enter image description here

For change the shape of vector, you can change the width/height of vector. Example change width to 10dp

<vector 
        android:width="10dp"
        android:height="24dp"
        >
       ...
</vector>

enter image description here


I try to create triangle image like back button of android navigation bar but I haven't found any solution.

Now, I found the solution with myself and would like to share it.

triangle navigation bar android

use xml below

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:bottom="20dp"
    android:left="480dp"
    android:right="60dp"
    android:top="20dp">
    <shape>
        <size android:width="60dp" />
        <corners android:radius="80dp"/>
        <solid android:color="#AAA" />
    </shape>
</item>
<item
    android:bottom="480dp"
    android:right="70dp"
    android:top="20dp">
    <rotate
        android:fromDegrees="-28"
        android:pivotX="96%"
        android:pivotY="50%"
        android:toDegrees="-20">
        <shape>
            <corners android:radius="80dp"/>
            <size android:height="60dp" />
            <solid android:color="#AAA" />
        </shape>
    </rotate>
</item>

<item
    android:bottom="20dp"
    android:right="70dp"
    android:top="480dp">
    <rotate
        android:fromDegrees="28"
        android:pivotX="96%"
        android:pivotY="50%"
        android:toDegrees="-20">
        <shape>
            <corners android:radius="80dp"/>
            <size android:height="60dp" />
            <solid android:color="#AAA" />
        </shape>
    </rotate>
</item>


You can use vector drawables.

If your minimum API is lower than 21, Android Studio automatically creates PNG bitmaps for those lower versions at build time (see Vector Asset Studio). If you use the support library, Android even manages "real vectors" down to API 7 (more on that in the update of this post at the bottom).

A red upwards pointing triangle would be:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="100dp"
    android:width="100dp"
    android:viewportHeight="100"
    android:viewportWidth="100" >
    <group
        android:name="triableGroup">
        <path
            android:name="triangle"
            android:fillColor="#FF0000"
            android:pathData="m 50,0 l 50,100 -100,0 z" />
    </group>
</vector>

Add it to your layout and remember to set clipChildren="false" if you rotate the triangle.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false">

    <ImageView
        android:layout_width="130dp"
        android:layout_height="100dp"
        android:rotation="0"
        android:layout_centerInParent="true"
        android:background="@drawable/triangle"/>

</RelativeLayout>

enter image description here

Change the size (width/height) of the triangle by setting the Views layout_width/layout_height attributes. This way you can also get an eqilateral triagle if you do the math correct.

UPDATE 25.11.2017

If you use the support library you can use real vectors (instead if bitmap creation) as far back as API 7. Simply add:

vectorDrawables.useSupportLibrary = true

do your defaultConfig in your module's build.gradle.

Then set the (vector xml) drawable like this:

<ImageView
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    app:srcCompat="@drawable/triangle" />

Everything is documented very nicely on the Vector Asset Studio page.

Ever since this feature I've been working entirely without bitmaps in terms of icons. This also reduces APK size quite a bit.