How can I check if two Spanned objects are equal (they have the same content and spans applied)? I rather not implement the equals(Spanned span)
method. :)
The span classes in Android lack equals
and hashCode
methods. I don't know why. Maybe it was just an oversight? There is also a bug in the SpannableStringBuilder.equals()
method.
The workaround is exactly what you feared. If you use, for example, AbsoluteSizeSpan
you need to extend it and add equals
and hashCode
methods. Use your version instead of the framework's version when adding spans to the SpannableStringBuilder
:
import android.os.Parcel;
public class AbsoluteSizeSpan extends android.text.style.AbsoluteSizeSpan {
public AbsoluteSizeSpan(int size) {
super(size);
}
public AbsoluteSizeSpan(int size, boolean dip) {
super(size, dip);
}
public AbsoluteSizeSpan(Parcel src) {
super(src);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbsoluteSizeSpan that = (AbsoluteSizeSpan) o;
if (getSize() != that.getSize()) return false;
return getDip() == that.getDip();
}
@Override
public int hashCode() {
int result = getSize();
result = 31 * result + (getDip() ? 1 : 0);
return result;
}
}
In the Android framework's SpannableStringBuilder.equals()
method, a list of sorted spans in compared to a list of unsorted spans 🤦. To fix this, override SpannableStringBuilder.equals()
:
public class SpannableStringBuilder extends android.text.SpannableStringBuilder {
@Override
public boolean equals(Object o) {
if (o instanceof Spanned &&
toString().equals(o.toString())) {
Spanned other = (Spanned) o;
// Check span data
Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
Object[] spans = getSpans(0, length(), Object.class);
if (spans.length == otherSpans.length) {
for (int i = 0; i < spans.length; ++i) {
Object thisSpan = spans[i];
Object otherSpan = otherSpans[i];
if (thisSpan == this) {
if (other != otherSpan ||
getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
return false;
}
} else if (!thisSpan.equals(otherSpan) ||
getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
return false;
}
}
return true;
}
}
return false;
}
}
Add your override versions of the span classes to your version of SpannableStringBuilder
.