I have a TextView in which every word is a ClickableSpan (actually a custom subclass of ClickableSpan). When a word is touched, it should be shown in bold font style. If I set textIsSelectable(false) on the TextView, it works just fine. The word is immediately bolded. But if text is selectable, then it does not work. BUT - if I touch a word and then turn the screen off and back on, when the screen display comes back on the word is bolded. I have tried everything I can think of to force a redraw (invalidate the TextView, force call Activity's onRestart(), refreshDrawableState() on the TextView, etc). What am I missing?
Here is my subclass of ClickableSpan:
public class WordSpan extends ClickableSpan
{
int id;
private boolean marking = false;
TextPaint tp;
Typeface font;
int color = Color.BLACK;
public WordSpan(int id, Typeface font, boolean marked) {
this.id = id;
marking = marked;
this.font = font;
}
@Override
public void updateDrawState(TextPaint ds) {
ds.setColor(color);
ds.setUnderlineText(false);
if (marking)
ds.setTypeface(Typeface.create(font,Typeface.BOLD));
tp = ds;
}
@Override
public void onClick(View v) {
// Empty here -- overriden in activity
}
public void setMarking(boolean m) {
marking = m;
updateDrawState(tp);
}
public void setColor(int col) {
color = col;
}
}
Here is the WordSpan instantiation code in my Activity:
... looping through words
curSpan = new WordSpan(index,myFont,index==selectedWordId) {
@Override
public void onClick(View view) {
handleWordClick(index,this);
setMarking(true);
tvText.invalidate();
}
};
... continue loop code
And here is my custom MovementMethod:
public static MovementMethod createMovementMethod ( Context context ) {
final GestureDetector detector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp ( MotionEvent e ) {
return true;
}
@Override
public boolean onSingleTapConfirmed ( MotionEvent e ) {
return false;
}
@Override
public boolean onDown ( MotionEvent e ) {
return false;
}
@Override
public boolean onDoubleTap ( MotionEvent e ) {
return false;
}
@Override
public void onShowPress ( MotionEvent e ) {
return;
}
});
return new ScrollingMovementMethod() {
@Override
public boolean canSelectArbitrarily () {
return true;
}
@Override
public void initialize(TextView widget, Spannable text) {
Selection.setSelection(text, text.length());
}
@Override
public void onTakeFocus(TextView view, Spannable text, int dir) {
if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) {
if (view.getLayout() == null) {
// This shouldn't be null, but do something sensible if it is.
Selection.setSelection(text, text.length());
}
} else {
Selection.setSelection(text, text.length());
}
}
@Override
public boolean onTouchEvent ( TextView widget, Spannable buffer, MotionEvent event ) {
// check if event is a single tab
boolean isClickEvent = detector.onTouchEvent(event);
// detect span that was clicked
if (isClickEvent) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
WordSpan[] link = buffer.getSpans(off, off, WordSpan.class);
if (link.length != 0) {
// execute click only for first clickable span
// can be a for each loop to execute every one
if (event.getAction() == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
return true;
}
else if (event.getAction() == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
return false;
}
}
else {
}
}
// let scroll movement handle the touch
return super.onTouchEvent(widget, buffer, event);
}
};
}