I'm developing a small native Android project using Kotlin. I intend to create a small text editor with the various buttons bold, italic, underline and so on. Unfortunately I've been really trying for days; also doing research and questioning the AI too, but I can't figure it out. I don't understand how the remove property of the SpannableStringBuilder class works.
Let's start with a simple idea, of which I will also include the code: I have a button and an EditText. When you press the button, subsequent characters will be underlined. When you press the button again, the underline should disappear for subsequent characters.
val editTextContent : EditText = findViewById(R.id.editTextContent)
val btnUnderline: Button = findViewById(R.id.button)
var isUnderlineApplied = false
btnUnderline.setOnClickListener {
val startSelection = editTextContent.selectionStart
val endSelection = editTextContent.selectionEnd
val spannableString = SpannableStringBuilder(editTextContent.text)
if (isUnderlineApplied) {
// Remove the underline
spannableString.removeSpan(UnderlineSpan())
} else {
// Apply underlining
spannableString.setSpan(
UnderlineSpan(),
startSelection,
endSelection,
Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
}
editTextContent.text = spannableString
editTextContent.setSelection(endSelection)
isUnderlineApplied = !isUnderlineApplied
}
This doesn't happen here. I do not understand why. I also tried to make a totally new project, believing that there were some settings inserted incorrectly, but nothing. This is why I ask you, please, how does this remover work and what should I do to make it work properly? Thanks for your attention.
I found the solution! The idea of the user Cheticamp is correct, we need to preserve the span and not create a new one. Once this was applied, there was the problem of when the remove was performed: everything was deleted.
In the end I found a solution: the span, when added, does so in a certain range; so why not save these ranges?
Therefore every time I have to deactivate a span, I save the range, given by the initial value in which I applied the span (here I called it "start") and at the end of the selection (called "endSelection"). At this point I remove the span and through the loop I apply the span to all the ranges. Doing so gives the illusion of span activation/deactivation (in this case the underline).
I don't know if it was the optimal solution, if it was best practice but it works. I'm sharing the final code with you all.
val editTextContent: EditText = findViewById(R.id.editTextContent)
val btnUnderline: Button = findViewById(R.id.button)
var isUnderlineApplied = false
var underlineSpan: UnderlineSpan? = null
data class Range(val start: Int, val end: Int)
val rangeList = mutableListOf<Range>()
var start = 0
btnUnderline.setOnClickListener {
val startSelection = editTextContent.selectionStart
val endSelection = editTextContent.selectionEnd
val spannableString = SpannableStringBuilder(editTextContent.text)
if (isUnderlineApplied) {
// Remove the underline
spannableString.removeSpan(underlineSpan)
val newRange = Range(start, endSelection)
rangeList.add(newRange)
for (range in rangeList) {
spannableString.setSpan(
UnderlineSpan(),
range.start,
range.end,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
)
}
} else {
start = startSelection
// Apply underlining
underlineSpan = UnderlineSpan()
spannableString.setSpan(
underlineSpan,
startSelection,
endSelection,
Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
}
editTextContent.text = spannableString
editTextContent.setSelection(endSelection)
isUnderlineApplied = !isUnderlineApplied
}