I have made a custom material textview class which uses annotations to style the given text. But i have ended up with this error
java.lang.ClassCastException: java.lang.String cannot be cast to android.text.SpannedString
causing my android app to crash.
I get the texts from Firebase Firestore and i have implemented the class below into my layouts directly. I am calling .setText from my viewholder and only using .setText("Text from Firebase")
there.
import android.graphics.Color
import android.text.Annotation
import android.text.SpannableString
import android.text.Spanned
import android.text.SpannedString
import android.text.style.*
import android.util.AttributeSet
import android.util.Log
import android.view.View
import com.google.android.material.textview.MaterialTextView
class SpannedAnnotationMaterialTextView : MaterialTextView {
private var mListener: OnItemClickListener? = null
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
override fun setText(text: CharSequence, type: BufferType) {
super.setText(processingText(text), type)
}
/**
* Beispieltext Deutsch
* nach Anamnese, Schmerzzustand und Untersuchungsbefunden\n \n Hochrisiko-Anamnese:\n <annotation format="bulletspan">bekanntes Marfan-Syndrom oder andere Bindegewebserkrankung</annotation>\n <annotation format="bulletspan">positive Familienanamnese für Aortenerkrankungen</annotation>\n <annotation format="bulletspan">bekannte Aortenklappenerkrankung</annotation>\n <annotation format="bulletspan">bekanntes thorakales Aortenaneurysma</annotation>\n <annotation format="bulletspan">vorausgegangene(r) Manipulation Aorta <per> Herzkatheter <per> herzchirurg. Eingriff</annotation>\n \n Hochrisiko-Schmerzsymptomatik:\n <annotation format="bulletspan">Schmerzen im Brust- <per> Rücken <per> Bauchbereich (und<per>oder)</annotation>\n <annotation format="bulletspan">abrupter Beginn</annotation>\n <annotation format="bulletspan">hohe Schmerzintensität</annotation>\n <annotation format="bulletspan">reißender Schmerzcharakter</annotation>\n \n Hochrisiko-Untersuchungsbefund:\n <annotation format="bulletspan">Pulsdefizit <per> Pulsdifferenz (evtl. Blutdruckdifferenz > 20mmHg (Arme), evtl. einseitig fehlender Radialispuls oder fehlende Pulse Leiste und Beine peripher Blutdruckdifferenz (syst. Messwert, höherer Wert zählt als realer syst. Blutdruck)</annotation>\n <annotation format="bulletspan">neurologische Symptomatik in Zusammenhang mit Schmerzauftreten</annotation>\n <annotation format="bulletspan">diastolisches Geräusch bei Auskultation über Erb'schem Punkt (neu und in Zusammenhang mit dem Schmerz)</annotation>\n <annotation bulletspan>Hypotension <per> Schocksymptomatik</annotation>\n
*
* Exampletext English
* according to anamnesis, pain condition and examination results \ n \ n high-risk anamnesis: \ n <annotation format = "bulletspan"> known Marfan syndrome or other connective tissue disease </annotation> \ n <annotation format = "bulletspan"> positive family history for aortic diseases < / annotation> \ n <annotation format = "bulletspan"> known aortic valve disease </annotation> \ n <annotation format = "bulletspan"> known thoracic aortic aneurysm </annotation> \ n <annotation format = "bulletspan"> previous Manipulation aorta <per> cardiac catheter <per> cardiac surgeon. Intervention </annotation> \ n \ n High-risk pain symptoms: \ n <annotation format = "bulletspan"> Pain in the chest <per> back <per> abdominal area (and <per> or) </annotation> \ n <annotation format = "bulletspan"> abrupt beginning </annotation> \ n <annotation format = "bulletspan"> high pain intensity </annotation> \ n <annotation format = "bulletspan"> tearing pain character </annotation> \ n \ n high risk Examination results: \ n <annotation format = "bulletspan"> Pulse deficit <per> Pulse difference (possibly blood pressure difference> 20mmHg (arms), possibly one-sided missing radial pulse or missing pulses Groin and legs peripheral blood pressure difference (system measured value, higher value counts as real syst. blood pressure) </annotation> \ n <annotation format = "bulletspan"> neurological symptoms in connection with the occurrence of pain </annotation> \ n <annotation format = "bulletspan"> diastolic noise during auscultation over Erb's point (new and in connection with the pain) </annotation> \ n <annotation bulletspan> Hypotensi on <per> Shock symptoms </annotation> \ n
*/
private fun processingText(text: CharSequence): CharSequence {
Log.e(TAG, "processing Text")
// get the text as spannableString so we can get the spans attached to the text
val fullText = text as SpannedString
val spannableString = SpannableString(fullText)
// get all the annotation spans from the text
// make sure you import android.text.Annotation
val annotations = fullText.getSpans(0, fullText.length, Annotation::class.java)
Log.e(TAG, "annotations found, size = ${annotations.size}")
// iterate through all the annotation spans
for (annotation in annotations) {
// look for the span with the key font
when (annotation.key) {
"link" -> {
spannableString.apply {
// set the span the same indices as the annotation
setSpan(
object : ClickableSpan() {
override fun onClick(widget: View) {
mListener!!.onSpanClick(annotation.value)
}
},
fullText.getSpanStart(annotation),
fullText.getSpanEnd(annotation),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
"format" -> {
when (annotation.value) {
"bulletspan" -> {
spannableString.apply {
setSpan(
BulletSpan(),
fullText.getSpanStart(annotation),
fullText.getSpanEnd(annotation),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
"bold" -> {
spannableString.apply {
setSpan(
BulletSpan(12),
fullText.getSpanStart(annotation),
fullText.getSpanEnd(annotation),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
"underline" -> {
// first underline then resize with x1.15
spannableString.apply {
setSpan(
UnderlineSpan(),
fullText.getSpanStart(annotation),
fullText.getSpanEnd(annotation),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
setSpan(
RelativeSizeSpan(1.15f),
fullText.getSpanStart(annotation),
fullText.getSpanEnd(annotation),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
}
}
"color" -> {
spannableString.apply {
setSpan(
ForegroundColorSpan(Color.parseColor(annotation.value)),
fullText.getSpanStart(annotation),
fullText.getSpanEnd(annotation),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
}
}
return spannableString
}
/**
* Method to bind the OnItemClickListener.
*
* @param listener see down below
*/
fun setOnItemClickListener(listener: OnItemClickListener?) {
mListener = listener
}
interface OnItemClickListener {
fun onSpanClick(spanText: String?)
}
companion object {
const val TAG = "SpannedAnnotationMaterialTextView"
}
}
And when i change from val fullText = text as SpannedString
to val fullText = SpannedString(text)
it wont crash but never finds any annotation.
Your error literally tells you, that compiler cannot cast String
to SpannedString
automatically.
You should do it by calling SpannedString
constructor.
So you kinda answered your own question :)