Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android check if Spanned objects are equal

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. :)

like image 932
Tudor Luca Avatar asked Oct 17 '13 09:10

Tudor Luca


1 Answers

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.

like image 184
alexbirkett Avatar answered Oct 19 '22 22:10

alexbirkett