Delete entire ImageSpan when the part of it is del

2019-08-27 06:24发布

I'm trying to implement Emoji's in my app and I should use short codes (like :dog:, :cat:), not unicode. I have two devices to test on and two different behaviours of EditText and ImageSpan in it.

First: Meizu PRO 6, Android 6.0 (API 23)

Everything works as I want. When you tap backspace on the spanned text, it disappears from EditText completely, because full spanned part of the string was removed.

For example, you have a "Hello :dog:" in your EditText (:dog: is replaced with a picture of the dog), you press backspace, your EditText contains only "Hello " now.

Second: Google Pixel XL, Android 9.0 (API 28)

When you tap backspace on the spanned text, you just remove the : symbol, making picture stay in EditText because it doesn't remove all spanned part of your string. But I want to remove it.

What did I try

I found this code in the other question here:

Android - Delete entire ImageSpan when part of it is deleted?

private val watcher = object : TextWatcher {
    private var spanLength = -1

    override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
        if (start == 0) return
        if (count > after) {
            val spans =
                editableText.getSpans(start + count, start + count, ImageSpan::class.java)
            if (spans == null || spans.isEmpty()) return

            for (i in spans.indices) {
                val end = editableText.getSpanEnd(spans[i])
                if (end != start + count) continue
                val text = spans[i].source
                spanLength = text!!.length - 1
                editableText.removeSpan(spans[i])
            }
        }
    }

    override fun onTextChanged(s: CharSequence, start: Int, before: Int, after: Int) {
        if (spanLength > -1) {
            val length = spanLength
            spanLength = -1
            editableText.replace(start - length, start, "")
        }
    }

    override fun afterTextChanged(s: Editable) {}
}

It works as intended for Google Pixel XL, but completely brakes Meizu removing 2-3 pictures or even not spanned text, sometimes it throws an Exception because start - length < 0.

Is there any way to solve this?

1条回答
smile是对你的礼貌
2楼-- · 2019-08-27 07:03

I ended up with this TextWatcher

private val watcher = object : TextWatcher {
    private var spanLength = -1
    private var spanStart = -1

    override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
        if (start == 0) return
        if (count > after) {
            val spans =
                editableText.getSpans(start + count, start + count, EmojiSpan::class.java)
            if (spans == null || spans.isEmpty()) return

            for (i in spans.indices) {
                val end = editableText.getSpanEnd(spans[i])
                if (end != start + count) continue
                val text = spans[i].getSource()
                spanLength = text.length - 1
                spanStart = editableText.getSpanStart(spans[i])
                editableText.removeSpan(spans[i])
            }
        }
    }

    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        if (spanLength > -1 && spanStart != -1 && count < before) {
            val startSpan = spanStart
            val endSpan = spanStart + spanLength

            if (startSpan < 0 || endSpan > editableText.length) {
                return
            }

            spanLength = -1
            spanStart = -1
            editableText.replace(startSpan, endSpan, "")
        }
    }

    override fun afterTextChanged(s: Editable) {}
}

Looks like it works with all devices I've tried.

查看更多
登录 后发表回答