I have app start with Splash screen then open listactivity rows, clicking on any row will opens an activity containing a textview, two buttons (one of which opens an infinite ga
To understand what is happening here, it all starts with a design decision about what a TextView
should and shouldn't be. According to the android source for TextView:
/**
* Displays text to the user and optionally allows them to edit it. A TextView
* is a complete text editor, however the basic class is configured to not
* allow editing; see {@link EditText} for a subclass that configures the text
* view for editing.
This means that, even if all you're doing is putting a piece of text on the screen, hidden behind that are full text highlight, selection and editing facilities. What has happened here is that a little tweak to resolve an issue in them appears to have interfered with some other functionality, and created some log lines. That this is the case is given more weight by, say, Nexus 7 error "SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length", in which it would seem that on some android versions this situation can be resolved by disabling autocomplete in text (entry) fields:
View.setInputType( InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS );
This could also be done in the XML using the corresponding tags for a TextView
:
android:inputType="none"
The lines in question appear to have arrived in Android 4.1 (Jelly Bean), in the setSpan
function of SpannableStringBuilder
(eg. 2.2.3 source vs 4.1.1 source). The log line is triggered by an attempt to set a span within a string of zero length. This is why setting it so that no (sub) section of the text needs to be selected should avoid this error, either by the above tip or by using:
android:textIsSelectable="false"
android:editable="false"
A more speculative solution maybe to ensure that there is never a blank string in a TextView
by adding the character "\u200b" (a non-shown blank character of zero width) to every string you put in your TextView
s, either by overriding setText
(to simply add it to the end of the string) in a custom TextView
or by putting it in your code for every string sent to TextView
s.
For those who like delving into the android system and overriding parts of it, according to this piece of android source it looks like it mght be possible to solve the issue by the following. However, there is absolutely no guarantee that it works. The truly brave (foolhardy) can write a complete SpannableStringBuilder for themselves ....
Creating your own form of SpannableStringBuilder
which traps the condition. This example simply stops quietly, rather than stopping loudly, as per the original:
import android.text.SpannableStringBuilder;
public class MySpannableStringBuilder extends SpannableStringBuilder {
// Taken from SpannableStringBuilder.java
private static final int MARK = 1;
private static final int POINT = 2;
private static final int START_MASK = 0xF0;
private static final int END_MASK = 0x0F;
private static final int START_SHIFT = 4;
MySpannableStringBuilder() {
super();
}
MySpannableStringBuilder(CharSequence text) {
super(text);
}
MySpannableStringBuilder(CharSequence text, int start, int end) {
super(text, start, end);
}
@Override
public void setSpan(Object what, int start, int end, int flags) {
// Determine if the error is going to be triggered, if so fail silently
int flagsStart = (flags & START_MASK) >> START_SHIFT;
int flagsEnd = flags & END_MASK;
if (flagsStart == POINT && flagsEnd == MARK && start == end) {
return;
}
// All good, so call the real routine
super.setSpan(what, flagsStart, end, flagsEnd);
}
}
Creating MyEditableFactory extends Editable.Factory
and setting it to return your SpannableStringBuilder:
import android.text.Editable;
public class MyEditableFactory extends Editable.Factory {
private static MyEditableFactory sInstance = new MyEditableFactory();
/**
* Returns this Editable Factory.
*/
public static Editable.Factory getInstance() {
return sInstance;
}
public Editable newEditable(CharSequence source) {
return new MySpannableStringBuilder(source);
}
}
Other than writing a custom version of the OS and putting it on the phone, I'm not sure what else is possible.
All suggestions for improvement welcome, as well as feedback on using this code in various OS situations.