During development, we may often encounter a requirement style like this:

This picture is a screenshot of the pop-up box of jingdong message notification. We can see that there is a triangle bubble effect on the upper right, which is just one of them. The triangle direction can also be up, down, left and right.

It can be seen from the screenshot that the bubble is composed of regular triangles and rounded rectangles, so the effect of triangular bubbles can be formed through combination. The following three ways are used to achieve this effect.

Implementation method: 1. Through.9 figure; 2. Through shape; 3, through the way of custom view;

Implementation logic:

1. Through the.9 figure

This way needless to say, find your UI sister cut a.9 figure, can be used, but this way the picture needs to account for a certain volume oh.

2, through shape

  • Is a 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:pivotX="40%"
            android:pivotY="80%">
            <shape android:shape="rectangle">
                <size
                    android:width="15dp"
                    android:height="15dp" />
                <solid android:color="#ffffff" />
            </shape>
        </rotate>
    </item>
</layer-list>
Copy the code
  • del
<? xml version="1.0" encoding="utf-8"? > <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item>
        <rotate
            android:fromDegrees="45"
            android:pivotX="135%"
            android:pivotY="15%">
            <shape android:shape="rectangle">
                <size
                    android:width="15dp"
                    android:height="15dp" />
                <solid android:color="#ffffff" />
            </shape>
        </rotate>
    </item>
</layer-list>
Copy the code
  • The left 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:pivotX="85%"
            android:pivotY="35%">>
            <shape android:shape="rectangle">
                <size
                    android:width="15dp"
                    android:height="15dp" />
                <solid android:color="#ffffff" />
            </shape>
        </rotate>
    </item>
</layer-list>
Copy the code
  • A right 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:pivotX="15%"
            android:pivotY="135%">>
            <shape android:shape="rectangle">
                <size
                    android:width="15dp"
                    android:height="15dp" />
                <solid android:color="#ffffff" />
            </shape>
        </rotate>
    </item>
</layer-list>
Copy the code

The above is through the shape way to achieve each direction of the code, this way is more obvious shortcomings, if you want to change the position of different corners need to write a different layout.

3, through the way to customize the view

I’m not going to show you how to do each one because it’s a little bit easier, but you can just copy it and use it, right

  • Add custom attributes
<declare-styleable name="TriangleView">
        <attr name="trv_color" format="color" />
        <attr name="trv_direction">
            <enum name="top" value="0" />
            <enum name="bottom" value="1" />
            <enum name="right" value="2" />
            <enum name="left" value="3" />
        </attr>
 </declare-styleable>
Copy the code
  • Custom code files

public class TriangleView extends View {
    private static final int TOP = 0;
    private static final int BOTTOM = 1;
    private static final int RIGHT = 2;
    private static final int LEFT = 3;
    private static final int DEFUALT_WIDTH = 10;
    private static final int DEFUALT_HEIGHT = 6;
    private static final int DEFUALT_COLOR = R.color.FFF;
    private Paint mPaint;
    private int mColor;
    private int mWidth;
    private int mHeight;
    private int mDirection;
    private Path mPath;

    public TriangleView(final Context context) {
        this(context, null);
    }

    public TriangleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TriangleView(final Context context, final AttributeSet attrs, final int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TriangleView, 0.0);
        mColor = typedArray.getColor(R.styleable.TriangleView_trv_color, ContextCompat.getColor(getContext(), DEFUALT_COLOR));
        mDirection = typedArray.getInt(R.styleable.TriangleView_trv_direction, mDirection);
        typedArray.recycle();
        mPaint.setColor(mColor);
    }

    private void init(a) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPath = new Path();
        mDirection = TOP;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (mWidth == 0|| widthMode ! = MeasureSpec.EXACTLY) { mWidth = (int) PixelUtil.dp2px(DEFUALT_WIDTH);
        }
        if (mHeight == 0|| heightMode ! = MeasureSpec.EXACTLY) { mHeight = (int) PixelUtil.dp2px(DEFUALT_HEIGHT);
        }
        setMeasuredDimension(mWidth, mHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        switch (mDirection) {
            case TOP:
                mPath.moveTo(0, mHeight);
                mPath.lineTo(mWidth, mHeight);
                mPath.lineTo(mWidth / 2.0);
                break;
            case BOTTOM:
                mPath.moveTo(0.0);
                mPath.lineTo(mWidth / 2, mHeight);
                mPath.lineTo(mWidth, 0);
                break;
            case RIGHT:
                mPath.moveTo(0.0);
                mPath.lineTo(0, mHeight);
                mPath.lineTo(mWidth, mHeight / 2);
                break;
            case LEFT:
                mPath.moveTo(0, mHeight / 2);
                mPath.lineTo(mWidth, mHeight);
                mPath.lineTo(mWidth, 0);
                break;
            default:
                break; } mPath.close(); canvas.drawPath(mPath, mPaint); }}Copy the code
  • Layout file addition
<com.sjl.keeplive.triange.TriangleView
        android:layout_width="10dp"
        android:layout_height="6dp"
        app:trv_color="@color/FFF"
        app:trv_direction="top" />
Copy the code

You can customize the four directions, and you can also use them in code, dynamically adding, dynamically changing colors, or a better way.

And I’m done here.