4

Here is the drawable code:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:top="0dp" android:bottom="10dp">
        <shape xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle">

            <solid android:color="@android:color/holo_blue_light" />
            <stroke
                android:width="1dp"
                android:color="#40adc2" />
            <corners android:radius="4dp" />
        </shape>
    </item>

    <item android:gravity="center_horizontal|bottom">
        <rotate
            android:fromDegrees="45"
            android:toDegrees="45"
            android:pivotX="75%"
            android:pivotY="47%">
            <shape android:shape="rectangle">
                <solid android:color="@android:color/holo_blue_light" />
                <size
                    android:height="20dp"
                    android:width="15dp"/>
            </shape>
        </rotate>
    </item>
</layer-list>

And here are two pictures showing the issue:enter image description here enter image description here

The first image is the incorrect one in API 19 and the second image is the correct one in android 9

Tried searching everywhere couldn't find a solution.

Thanks in advance

Gk Mohammad Emon
  • 6,084
  • 3
  • 42
  • 42
nick isra
  • 99
  • 1
  • 9
  • Have you tried [this](https://stackoverflow.com/questions/42885410/vector-drawable-in-layer-list-on-older-android-versions) solution? If it is work then let me know. – Gk Mohammad Emon Dec 13 '19 at 10:28
  • From what the op mentioned, he gets incorrect behaviour for younger API - 19, old one is the correct one in that case. – kolboc Dec 13 '19 at 16:26
  • Please add ` android:height="20dp" android:width="15dp" ` this in your 2nd `item` and let me know. – Gk Mohammad Emon Dec 14 '19 at 04:32
  • I think what we are missing there to be able to solve that is full xml of that layout, I tested that drawable in a fresh project and can't reproduce the issue mentioned by op. – kolboc Dec 14 '19 at 22:13
  • @kolboc i can post full layout but it is really simple, mu TextView background is set to this drawable, and its width and height are wrap_content – nick isra Dec 16 '19 at 21:52

1 Answers1

1

The layer-list drawable looks bad for API level 19 because

<size android:height="20dp"
      android:width="15dp"/> 

for the small rectangle is ignored. (You can test this if you remove the rotation and let the rectangle with the rounded corners have a transparent color)

For older API levels you have to set values for android:top, android:bottom, android:left and android:right. If you know the dimensions of the TextView in advance, you can calculate the values as follows (unit is always dp):

  • top = (textview height) - 20
  • left = (50% of textview width) - 10
  • right = (50% of textview width) - 10
  • bottom = 0

For example if your TextView has a width of 200dp and a height of 80dp:

<item 
    android:top="60dp" android:bottom="0dp"
    android:left="90dp" android:right="90dp">
    <rotate
        android:fromDegrees="45"
        android:toDegrees="45"
        android:pivotX="75%"
        android:pivotY="47%">
        <shape android:shape="rectangle">
            <solid android:color="@android:color/holo_blue_light" />
        </shape>
    </rotate>
</item>

Please note that if you use this approach you don't need to set the size or the gravity.

Sometimes one can't be sure about the size of the TextView (e.g. because its content may vary across different languages or on different screens). In this case you can create a custom TextView which will draw the bubble as its background:

class BubbleTextView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : TextView(context, attrs, defStyleAttr) {

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val rectPath = Path()
    private val trianglePath = Path()

    private val rectF = RectF()
    private val triangleSize = resources.getDimensionPixelSize(R.dimen.triangle_size_20dp).toFloat()
    private val cornerRadius = resources.getDimensionPixelSize(R.dimen.corner_radius_4dp).toFloat()

    constructor(context: Context?):this(context, null, 0)
    constructor(context: Context?, attrs: AttributeSet?):this(context, attrs, 0)


    init{
        paint.style = Paint.Style.FILL
        paint.color = Color.CYAN
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        val myWidth = (right - left).toFloat()
        val myHeight = (bottom - top).toFloat()
        val centerX = myWidth / 2f
        val lowerEdgeY = myHeight * 0.8f

        rectF.set(0f, 0f, myWidth, lowerEdgeY)
        rectPath.addRoundRect(rectF,cornerRadius, cornerRadius, Path.Direction.CW )

        val delta = triangleSize * 0.5f
        trianglePath.moveTo(centerX - delta, lowerEdgeY)
        trianglePath.lineTo(centerX + delta, lowerEdgeY)
        trianglePath.lineTo(centerX, myHeight)
        trianglePath.close()
    }

    override fun onDraw(canvas: Canvas?) {
        canvas?.drawPath(rectPath, paint)
        canvas?.drawPath(trianglePath, paint)
        super.onDraw(canvas)
    }
}
Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
  • Sorry i didnt fully understand, how can i get my text bubble width. i just set it to wrap content? I can post whole layout xml if needed. Also i am thinking of using 9 patch image and get rid of this annoying problem what do you recommend? – nick isra Dec 16 '19 at 21:43
  • @nick isra - with the approach in this answer you'd have to use a fixed width. That's why I would prefer either a 9-patch image or a custom TextView which will draw its background using a Path. The TextView means more effort but it may look better, especially if you want it to have a shadow from elevation (OTOH a fixed width in your case could be OK if it's always the same message) – Bö macht Blau Dec 17 '19 at 05:32
  • I guess you are absolutely right, i have 1 more last question: if i set its width to 280dp and height to 60dp i am using ConstraintLayout as well. Should i add this TextView to a relative layout so it gets resized? I mean maybe on a small device it will get bigger than the screen if i have a fixed width – nick isra Dec 17 '19 at 11:05
  • @nick isra - how small will the device be? And one can provide resources depending on screen size – Bö macht Blau Dec 17 '19 at 14:46
  • Of course, one custom TextView may be less trouble than managing lots of resource files. If you're interested, I can show you how to do that – Bö macht Blau Dec 17 '19 at 21:31
  • Yes please. But having the text view inside a relativelayout it would scale right? Even with fixed width – nick isra Dec 17 '19 at 23:44
  • @nick isra - being inside a RelativeLayout is no guarantee for anything. If you tell lthe View to be 200dp wide and have a margin of 100dp overall then it may look nice on a tablet but it will vanish altogether on a small handheld – Bö macht Blau Dec 18 '19 at 06:39
  • sorry to bother you again, but extending TextView requires api 21 and higher, that brings me bacm to the initial problem. my problem was with drawable on devices below api 21 :/ – nick isra Dec 21 '19 at 12:26
  • @nick isra - AFAIK only the 4 parameter constructor does not work for lower API levels. Extending TextView is very definitely always possible – Bö macht Blau Dec 21 '19 at 17:41
  • thank you for your help. So in this case i must just ignore to error? Or is there some code i must add? – nick isra Dec 23 '19 at 10:09
  • Also forgot to mention, the error says: android.widget.TextView() requires api level 21 – nick isra Dec 23 '19 at 13:02
  • @nick isra - sorry but I don't understand - the above code works with an emulator running API level 19 and my Android Studio(3.5) does not show any error messages. Could you edit your question (or ask a new one and post the link here since this *is* in fact a new problem) to clarify what exactly you are trying to do and what does not work? – Bö macht Blau Dec 23 '19 at 16:52
  • alright thank you i will link it to you when i post a new question – nick isra Dec 23 '19 at 19:14
  • Here is the post: https://stackoverflow.com/questions/59462791/how-to-fix-android-widget-textview-requires-api-21-error/59462948#59462948 – nick isra Dec 24 '19 at 14:41
  • @nick isra - I really must apologize: I tested the TextView with API level 19 but I switched between writing the answer and writing code, so I kept the 4 parameter constructor in this answer but not in my sample code. In addition to that, I answered your comments from my cell phone (which is rather small) so I did not even try to re-read what I posted until today. In a nutshell: the 4 parameter constructor only works for API levels 21+. So we don't use it when writing code for older devices. On devices with API levels 21+ the 4 parameter c'tor is optional, so all is well.(cont) – Bö macht Blau Dec 25 '19 at 10:25
  • (cont)The question "but why are there four parameters if nobody really needs them?" is answered by [A deep dive into Android View constructors](https://blog.danlew.net/2016/07/19/a-deep-dive-into-android-view-constructors/) by Dan Lew. – Bö macht Blau Dec 25 '19 at 10:25
  • @nick isra - I removed the 4 parameter constructor from my solution, please see the edited code – Bö macht Blau Dec 25 '19 at 10:35