Skip to content

Instantly share code, notes, and snippets.

@rahul01
Created February 19, 2020 09:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rahul01/8f44d730b6a5a10cc9deecfe95228352 to your computer and use it in GitHub Desktop.
Save rahul01/8f44d730b6a5a10cc9deecfe95228352 to your computer and use it in GitHub Desktop.
RoundedBackgroundColorSpan
package com.test.util
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.text.style.LineBackgroundSpan
import kotlin.math.abs
import kotlin.math.sign
class RoundedBackgroundColorSpan(backgroundColor: Int,
private val padding: Int,
private val radius: Int) : 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
private val ALIGN_CENTER = 0
private val ALIGN_START = 1
private val ALIGN_END = 2
init {
paint.color = backgroundColor
paintStroke.color = backgroundColor
}
private var align = ALIGN_CENTER
fun setAlignment(alignment: Int) {
align = alignment
}
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 width = p.measureText(text, start, end) + 2f * padding
val shiftLeft: Float
val shiftRight: Float
when (align) {
ALIGN_START -> {
shiftLeft = 0f - padding
shiftRight = width + shiftLeft
}
ALIGN_END -> {
shiftLeft = right - width + padding
shiftRight = (right + padding).toFloat()
}
else -> {
shiftLeft = (right - width) / 2
shiftRight = right - shiftLeft
}
}
rect.set(shiftLeft, top.toFloat(), shiftRight, bottom.toFloat())
if (lnum == 0) {
c.drawRoundRect(rect, radius.toFloat(), radius.toFloat(), 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 (align != ALIGN_START) {
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 (align != ALIGN_END) {
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
}
}
RoundedBackgroundColorSpan span;
private void setupTextBackground() {
int padding = SystemUtils.dpToPx(8);
int radius = SystemUtils.dpToPx(5);
span = new RoundedBackgroundColorSpan(
ContextCompat.getColor(mContext, R.color.yellow_100),
padding, radius);
mAddTextEditText.setShadowLayer(padding, 0f, 0f, 0);
mAddTextEditText.setPadding(padding, padding, padding, padding);
mAddTextEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable s) {
s.setSpan(span, 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
});
}
@chihung93
Copy link

Thanks, work with this code
Sample for someone needed
`

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
center.setOnClickListener {
mAddTextEditText.gravity = CENTER
setupTextBackground(0)

    }

    left.setOnClickListener {
        mAddTextEditText.gravity = END

        setupTextBackground(2)

    }

    start.setOnClickListener {
        mAddTextEditText.gravity = START

        setupTextBackground(1)

    }
    setupTextBackground(1)
}

private fun setupTextBackground(align:Int) {
    if(span!=null){
        mAddTextEditText.text.removeSpan(span)
    }
    val padding:Int = dpToPx(8f,this).toInt()
    val radius:Int = dpToPx(5f,this).toInt()
    span = RoundedBackgroundColorSpan(
            ContextCompat.getColor(this, R.color.colorAccent),
            padding, radius)
    span?.setAlignment(align)

    mAddTextEditText.setShadowLayer(dpToPx(8f,this), dpToPx(8f,this), dpToPx(8f,this), 0)
    mAddTextEditText.setPadding(padding, padding, padding, padding)
    mAddTextEditText.addTextChangedListener(object : TextWatcher {
        override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
        override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
        override fun afterTextChanged(s: Editable) {
            s.setSpan(span, 0, s.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        }
    })

    val s = mAddTextEditText.text
    s.setSpan(span, 0, s.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}

private fun dpToPx(dp: Float, context: Context): Float {
    return run {
        val resources = context.resources
        val metrics = resources.displayMetrics
        dp * (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
    }
}

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<EditText
    android:id="@+id/mAddTextEditText"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:text="Hello World!"
    android:gravity="start"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/linearLayout" />

<LinearLayout
    android:id="@+id/linearLayout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <Button
        android:id="@+id/center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Center" />

    <Button
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start" />

    <Button
        android:id="@+id/left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Left" />
</LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

`

@BhavyaRattan
Copy link

@rahul01 thanks for the code, it really works like a charm. I just have one quick query: There is extra padding added at both ends whenever I press enter while entering text. Do you know what might be causing it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment