TextView設置SpanableString后,假如有ClickSpan的話,還需要設置Movemethod才能正確響應點擊事件。
假如TextView有ClickSpan并且自身還有點擊響應的話,那么只會響應ClickSpan,而自身的點擊事件則不會響應。
方案1,給TextView設置TouchListener,模擬MoveMethod的方式:
public class LinkMovementMethodOverride implements View.OnTouchListener{
//回調 點擊TextView非clickspan的地方的回調
private ForumItemLinkedMethod.IClickNoSpan mIClickNoSpan;
public LinkMovementMethodOverride(ForumItemLinkedMethod.IClickNoSpan IClickNoSpan) {
mIClickNoSpan = IClickNoSpan;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
TextView widget = (TextView) v;
CharSequence text = widget.getText();
try {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
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);
float lineRight = layout.getLineRight(line);
int off = layout.getOffsetForHorizontal(line, x);
boolean moreThentextEnd = (x-lineRight)>20;//點擊位置超過行末太遠
ClickableSpan[] link = getSpans(text,off);
if (!moreThentextEnd&&link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
}
return true;
} else {
if (action == MotionEvent.ACTION_UP) {
if(mIClickNoSpan!=null){
try {
mIClickNoSpan.onClickNoSpan();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
//根據SpannedString 或者SpannableString(注意這倆不一樣), 獲取指定位置的span
private ClickableSpan[] getSpans(CharSequence text,int off) throws Exception {
if(text instanceof SpannedString){
SpannedString buffer = (SpannedString) text;
return buffer.getSpans(off, off, ClickableSpan.class);
}else if(text instanceof SpannableString){
SpannableString buffer = (SpannableString) text;
return buffer.getSpans(off, off, ClickableSpan.class);
}else{
throw new RuntimeException("hans:not support text");
}
}
}
關鍵代碼:
//1.修正坐標
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
then:
//獲取xy坐標在文字里的offset
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
float lineRight = layout.getLineRight(line);
int off = layout.getOffsetForHorizontal(line, x);
then:
獲取指定位置的span,判斷是不是Clickspan。。。
方案2:自定義LinkMoveMethod,貌似有時候沒用,參考吧
public class ForumItemLinkedMethod extends LinkMovementMethod {
public interface IClickNoSpan{
void onClickNoSpan();
}
private IClickNoSpan mIClickNoSpan;
public ForumItemLinkedMethod(IClickNoSpan IClickNoSpan) {
mIClickNoSpan = IClickNoSpan;
}
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer,
MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
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);
float lineRight = layout.getLineRight(line);
boolean moreThentextEnd = (x-lineRight)>20;
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
if (!moreThentextEnd&&link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
return true;
} else {
Selection.removeSelection(buffer);
if (action == MotionEvent.ACTION_UP) {
if(mIClickNoSpan!=null){
try {
mIClickNoSpan.onClickNoSpan();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return true;
}
}
return Touch.onTouchEvent(widget, buffer, event);
}
}
使用:
textContent.setMovementMethod(new ForumItemLinkedMethod());