Android drawable tooltip arrow box

4.9k Views Asked by At

I tried the below code to create a popup window with a tooltip-like arrow on top. picture attached. But I am getting something different as a result.
popup inflater :

LayoutInflater inflater = (LayoutInflater)
                        getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
              View  view = inflater.inflate(R.layout.popup, null);
              mypopupWindow = new PopupWindow(view,500, RelativeLayout.LayoutParams.WRAP_CONTENT, true);            
              mypopupWindow.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
              mypopupWindow.showAsDropDown(v,-153,0);

popup layout :

<?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="wrap_content">
<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="50dp"
        android:layout_marginTop="50dp"
        android:background="@drawable/shadow_recta"
        android:orientation="vertical"
        android:gravity="center">
     <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="text long text" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button"/>
</LinearLayout>

drawable file :

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:gravity="top|center_horizontal" >
        <rotate android:fromDegrees="0" android:toDegrees="-45"
            android:pivotX="0%" android:pivotY="50%" >
            <shape android:shape="rectangle">
                <size android:width="24dp" android:height="24dp" />
                <stroke android:color="@android:color/holo_blue_bright" android:width="1dp"/>
            </shape>
        </rotate>
    </item>
    <item>
        <shape android:shape="rectangle">
            <size android:width="206dp" android:height="76dp" />
            <solid android:color="@android:color/white" />
            <stroke android:color="@android:color/holo_blue_bright"  android:width="1dp"/>
            <corners android:radius="2dp" />
        </shape>
    </item>

    <item android:gravity="top|center_horizontal">
        <rotate
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fromDegrees="-45"
            android:pivotX="-50%"
            android:pivotY="60%"
            android:toDegrees="35">
            <shape android:shape="rectangle">
                <solid android:color="@android:color/white" />
                <size
                    android:width="24dp"
                    android:height="24dp" />
                <stroke
                    android:width="1dp"
                    android:color="@android:color/holo_blue_bright" />
            </shape>
        </rotate>
    </item>
</layer-list>

Expected image : enter image description here

Output i am getting : enter image description here

Please help me to create a box as in the expected image.

3

There are 3 best solutions below

3
On BEST ANSWER

It's not that difficult, key points are three to make this layer-list Drawable:

  • The tooltip (triangle) must be fixed size.

  • The background must NOT be fixed size, since the content size is unknown.

  • Adjust the margin and padding (inside the Drawable).


1. Create the layer-list Drawable:

<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item                      //background part
        android:top="17dp">    //margin top, half of the 【rotated】 rectangle height

        <shape
            android:shape="rectangle">

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

            <corners 
                android:radius="8dp" />

            <padding                    //important part
                android:top="17dp" />   //offset the margin top, shows up the tooltip perfectly

        </shape>

    </item>

    <item                       //tooltip part
        android:width="24dp"    //size must be fixed
        android:height="24dp"
        android:end="32dp"     //margin end (right)
        android:gravity="end"  //place wherever you want 
        android:top="-12dp">   //margin top, negative half of its height, display half of this rotated rectangle

        <rotate 
            android:fromDegrees="45">

            <shape 
                android:shape="rectangle">

                <solid 
                    android:color="@color/purple_500" />    //change back to background part color after your experiment and testing

            </shape>

        </rotate>

    </item>

</layer-list>

2. Apply to View:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hi, I'm Sam"
    android:textColor="@color/black"
    android:textSize="40sp"
    android:background="@drawable/chat_background"    //here
    android:paddingHorizontal="16dp"/>                //apply padding bottom in Drawable, not in here

<TextView                                             //just for comparison
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    android:text="Hi, I'm Sam"
    android:textColor="@color/black"
    android:textSize="40sp"
    android:background="@color/purple_200"
    android:paddingHorizontal="16dp"/>

Result:

enter image description here


Little Math:

  • Un-rotated rectangle height is 24dp.
  • Rotated rectangle height is square root of (24^2)*2 = 33.9411.
  • Half of the Rotated rectangle height is 16.9706, so we take 17.

Tips For applying vertical padding:

  • Modify the padding top of Background part (ex: 17dp + 8dp = 25dp).

  • Modify the margin top of Tooltip (rotated rectangle) part to cancel out (ex: -12dp - 8dp = -20dp).

3
On

This is your demand:

  • Stroke style
  • Show the arrow top portion only

1. Change my example answer from solid color style to stroke style:

  • Apply to both Background part and the Tooltip part.

    <stroke
        android:color="@color/purple_200"
        android:width="2dp"/>    //border thickness, keep this in mind
    
    <solid
        android:color="@color/white" />    //white background color, must have
    
  • Now you notice that the bottom half of the rotated rectangle is also shown up, how to hide it? Well, the idea is to make something to Cover/Overlay it - but not completely. We need one more layer.

2. Duplicate the tooltip part to make it as the third layer, and all we need to do is just moving it down the distance of border width + 1dp = 3dp, and give it a solid white background color:

<item
    android:width="24dp"
    android:height="24dp"
    android:end="32dp"
    android:gravity="end"
    android:top="-9dp">    //here, -12 + 3 = -9

    <rotate
        android:fromDegrees="45">

        <shape
            android:shape="rectangle">

            <solid
                android:color="@color/purple_500" />    //change back to white after your testing

        </shape>

    </rotate>

</item>

Result:

enter image description here

enter image description here

1
On

For bottom:

enter image description here


For left:

enter image description here

("sp" was mistake, should use "dp" instead.)