TextView可以选择并包含链接吗?

oak*_*kes 19 android textview

我遇到了一个问题TextView.我可以使用它进行选择setTextIsSelectable(true),但是当我启用链接时setMovementMethod(LinkMovementMethod.getInstance()),它就不再可以选择了.

请注意,我并不是说可以点击原始链接,而是通过TextView使用类似的东西加载HTML标记来使实际单词可点击setText(Html.fromHtml("<a href='http://stackoverflow.com'>Hello World!</a>")).

小智 25

oakes的答案会在文本视图上双击导致异常

java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0...

我查看了LinkMovementMethod中的onTouchEvent实现,发现当textview不包含链接时,它会删除选择.在这种情况下,当用户尝试更改时,选择从空值开始,应用程序崩溃.

...
if (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);
}
...
Run Code Online (Sandbox Code Playgroud)

所以我重写onTouchEvent方法,它工作正常.

public class CustomMovementMethod extends LinkMovementMethod {
    @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) {
        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);
            int off = layout.getOffsetForHorizontal(line, x);

            ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

            if (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;
            }
        }

        return Touch.onTouchEvent(widget, buffer, event);
    }
}
Run Code Online (Sandbox Code Playgroud)

希望它对某人有所帮助.


oak*_*kes 14

我想到了.您需要子类化LinkMovementMethod并添加对文本选择的支持.真的很不幸的是它本身并不支持它.我只是推翻使用从该等价的相关方法的源代码ArrowKeyMovementMethod.我想这是Android开源的一个好处!

public class CustomMovementMethod extends LinkMovementMethod {
    @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());
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它,只需直接实例化它,如下所示:

textView.setMovementMethod(new CustomMovementMethod());
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,此解决方案有时会导致"java.lang.IndexOutOfBoundsException:setSpan(-1 ... -1)在0"之前开始,在某些情况下,例如在Nexus 5设备上双击. (5认同)