I took this solution and added the automatic gravity management. The intial solution is only for the start/left gravity. The displayed version supports Gravity.START
and Gravity.END
with LTR and RTL detection.
So this is my updated version in Kotlin:
/*
* When trying to put a background color on the text only within textview, there is no padding. This
* class allows a padding in this circumstance. The source is linked below.
* Was edited by Maxime, to add a gravity switcher because the default code in the source is only working for gravity start
* Source: https://medium.com/@tokudu/android-adding-padding-to-backgroundcolorspan-179ab4fae187
*
*/
class PaddingBackgroundColorSpan(private val mBackgroundColor: Int, private val mPadding: Int, private val gravity: Int) : LineBackgroundSpan {
private val backgroundRect = Rect()
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 textWidth = Math.round(p.measureText(text, start, end))
val paintColor = p.color
val finalTop = top - if (lnum == 0) mPadding / 2 else -(mPadding / 2)
val finalBottom = bottom + mPadding / 2
val isLeftToRight = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_LTR
// Draw the background
backgroundRect.set(when {
gravity == Gravity.LEFT || (isLeftToRight && gravity == Gravity.START) || (!isLeftToRight && gravity == Gravity.END) -> getLeftRect(left, finalTop, right, finalBottom, textWidth)
gravity == Gravity.CENTER -> getCenterRect(left, finalTop, right, finalBottom, textWidth)
gravity == Gravity.RIGHT || (isLeftToRight && gravity == Gravity.RIGHT) || (!isLeftToRight && gravity == Gravity.START) -> getRightRect(left, finalTop, right, finalBottom, textWidth)
else -> {
getLeftRect(left, finalTop, right, finalBottom, textWidth)
}
})
p.color = mBackgroundColor
c.drawRect(backgroundRect, p)
p.color = paintColor
}
/**
* Method to process the rectangle where the background (with its padding) will be drawn when the
* text gravity left
* @param left - left coordinate of the textView relative to its parent
* @param top - top coordinate of the textView relative to its parent
* @param right - right coordinate of the textView relative to its parent
* @param bottom - bottom coordinate of the textView relative to its parent
* @param textWidth - the width of the textView
*
* @return Rect - containing the coordinates to draw the background
*/
private fun getLeftRect(left: Int, top: Int, right: Int, bottom: Int, textWidth: Int): Rect {
return Rect(left - mPadding, top, left + textWidth + mPadding, bottom)
}
/**
* Method to process the rectangle where the background (with its padding) will be drawn when the
* text gravity right
* @param left - left coordinate of the textView relative to its parent
* @param top - top coordinate of the textView relative to its parent
* @param right - right coordinate of the textView relative to its parent
* @param bottom - bottom coordinate of the textView relative to its parent
* @param textWidth - the width of the textView
*
* @return Rect - containing the coordinates to draw the background
*/
private fun getRightRect(left: Int, top: Int, right: Int, bottom: Int, textWidth: Int): Rect {
return Rect(right - textWidth - mPadding, top, right + mPadding, bottom)
}
/**
* Method to process the rectangle where the background (with its padding) will be drawn when the
* text gravity is center
* @param left - left coordinate of the textView relative to its parent
* @param top - top coordinate of the textView relative to its parent
* @param right - right coordinate of the textView relative to its parent
* @param bottom - bottom coordinate of the textView relative to its parent
* @param textWidth - the width of the textView
*
* @return Rect - containing the coordinates to draw the background
*/
private fun getCenterRect(left: Int, top: Int, right: Int, bottom: Int, textWidth: Int): Rect {
val diff = abs((right - left) / 2 - textWidth / 2)
return Rect(left + diff - mPadding, top, right - diff + mPadding, bottom)
}
}
And how to use it:
private fun setBackgroundToTextOnlyInTextView(textView: TextView, text: CharSequence, @ColorRes backgroundColor: Int, @DimenRes paddingID: Int) {
val padding = context.resources.getDimensionPixelSize(paddingID)
textView.setShadowLayer(padding.toFloat() , 0.0f, 0.0f, Color.TRANSPARENT )
textView.setPadding(padding, padding, padding, padding)
val color = ContextCompat.getColor(context, backgroundColor)
val spannableString = SpannableString(text)
val backgroundSpan = PaddingBackgroundColorSpan(color, padding, textView.gravity)
spannableString.setSpan(backgroundSpan, 0, spannableString.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = spannableString
}