I was trying to do something similar to Instagram below -
But i want this curves like Instagram -
Now i am stuck in one more problem - When i typ
Modified @Rahul_Tiwari's version to automatically scale the padding and corner radius when the text size changes. It scales based on percent change from a default text size value. Plus setShadowLayer as needed. It also adds padding to the top and bottom of the text so the padding is equal on all sides.
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.text.style.LineBackgroundSpan
import android.view.Gravity
import android.widget.TextView
import kotlin.math.abs
import kotlin.math.sign
class BackgroundColorSpan(private val tv: TextView,
backgroundColor: Int,
private val defaultTextSizePx: Float,
private val paddingToTextSizeRatio : Float = 0.125f,
gravityAlignment: Int = Gravity.CENTER) : LineBackgroundSpan {
private val rect = RectF()
private val paint = Paint()
private val paintStroke = Paint()
private val path = Path()
private var prevWidth = -1f
private var prevLeft = -1f
private var prevRight = -1f
private var prevBottom = -1f
private var prevTop = -1f
/***
* Gravity.CENTER_HORIZONTAL
* Gravity.LEFT
* Gravity.RIGHT
*/
private var gravityAlignment : Int
init {
tv.includeFontPadding = false
paint.color = backgroundColor
paintStroke.color = backgroundColor
this.gravityAlignment = gravityAlignment and Gravity.HORIZONTAL_GRAVITY_MASK
}
private val paddingForDefaultTextSize: Float get() = defaultTextSizePx * paddingToTextSizeRatio
private fun getTextScale(currentPaint: Paint) : Float = currentPaint.textSize / defaultTextSizePx
private fun getTagWidth(text: CharSequence, start: Int, end: Int, paint: Paint, padding: Float): Float =
padding + paint.measureText(text, start, end) + padding
private fun updatePaddingAndShadowLayerRadius(padding: Float) {
if (tv.shadowRadius != padding) {
tv.setShadowLayer(padding/* radius */, 0.toFloat(), 0.toFloat(), 0 /* transparent */)
}
val paddingI= padding.toInt()
if (tv.paddingLeft != paddingI && tv.paddingRight != paddingI){
tv.setPadding(paddingI, paddingI, paddingI, paddingI)
tv.setLineSpacing(padding, 1.0f)
}
}
override fun drawBackground(
c: Canvas,
p: Paint,
left: Int,
right: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
lnum: Int) {
val paddingForTextSize = paddingForDefaultTextSize * getTextScale(p)
updatePaddingAndShadowLayerRadius(paddingForTextSize)
val width = getTagWidth(text, start, end, p, paddingForTextSize)
val shiftLeft: Float
val shiftRight: Float
val fm = p.fontMetrics
val tagBottom: Float = baseline + fm.descent + paddingForTextSize
val topPadding = if (lnum == 0 ) paddingForTextSize else 0f
val tagTop: Float = baseline + fm.ascent - topPadding
val tagHeight = tagBottom - tagTop
val radius = tagHeight / 10
when (gravityAlignment) {
Gravity.LEFT -> {
shiftLeft = 0f - paddingForTextSize
shiftRight = width + shiftLeft
}
Gravity.RIGHT -> {
shiftLeft = right - width + paddingForTextSize
shiftRight = (right + paddingForTextSize)
}
else -> {
shiftLeft = (right - width) / 2
shiftRight = right - shiftLeft
}
}
rect.set(shiftLeft, tagTop, shiftRight, tagBottom)
if (lnum == 0) {
c.drawRoundRect(rect, radius, radius, paint)
} else {
path.reset()
val difference = width - prevWidth
val diff = -sign(difference) * (2f * radius).coerceAtMost(abs(difference / 2f)) / 2f
path.moveTo(
prevLeft, prevBottom - radius
)
if (gravityAlignment != Gravity.LEFT) {
path.cubicTo(//1
prevLeft, prevBottom - radius,
prevLeft, rect.top,
prevLeft + diff, rect.top
)
} else {
path.lineTo(prevLeft, prevBottom + radius)
}
path.lineTo(
rect.left - diff, rect.top
)
path.cubicTo(//2
rect.left - diff, rect.top,
rect.left, rect.top,
rect.left, rect.top + radius
)
path.lineTo(
rect.left, rect.bottom - radius
)
path.cubicTo(//3
rect.left, rect.bottom - radius,
rect.left, rect.bottom,
rect.left + radius, rect.bottom
)
path.lineTo(
rect.right - radius, rect.bottom
)
path.cubicTo(//4
rect.right - radius, rect.bottom,
rect.right, rect.bottom,
rect.right, rect.bottom - radius
)
path.lineTo(
rect.right, rect.top + radius
)
if (gravityAlignment != Gravity.RIGHT) {
path.cubicTo(//5
rect.right, rect.top + radius,
rect.right, rect.top,
rect.right + diff, rect.top
)
path.lineTo(
prevRight - diff, rect.top
)
path.cubicTo(//6
prevRight - diff, rect.top,
prevRight, rect.top,
prevRight, prevBottom - radius
)
} else {
path.lineTo(prevRight, prevBottom - radius)
}
path.cubicTo(//7
prevRight, prevBottom - radius,
prevRight, prevBottom,
prevRight - radius, prevBottom
)
path.lineTo(
prevLeft + radius, prevBottom
)
path.cubicTo(//8
prevLeft + radius, prevBottom,
prevLeft, prevBottom,
prevLeft, rect.top - radius
)
c.drawPath(path, paintStroke)
}
prevWidth = width
prevLeft = rect.left
prevRight = rect.right
prevBottom = rect.bottom
prevTop = rect.top
}
}