MLP*_*CiM 5 android spannablestring
我们的应用程序有几个 TextViews 实例,其内容设置myTv.setText(Html.fromHtml());为适用于 Android 4.4.0 及更低版本。
从 4.4.2 和 Lollypop 开始,这些链接已停止工作。文本仍然带有下划线和超链接颜色,但点击它们不会产生任何结果。
不得不说,这些字段被标记为可复制粘贴的,众所周知,它与这些可生成对象有交互。
有没有人能够解决这个问题?
问题是,当在 TextView 中启用复制和粘贴时,Android 将使用 ArrowKeyMovementMethod,它支持选择文本但不支持单击链接。当您使用 LinkMovementMethod 时,您可以单击链接,但不能选择文本(无论您使用的是 Lollipop、KitKat 还是较低的 Android 版本)。
为了解决这个问题,我扩展了 ArrayKeyMovementMethod 类,并使用 LinkMovementMethod onTouchEvent 覆盖了 onTouchEvent。为了允许文本选择,我必须删除三行代码。由于我在具有大量文本格式的富文本编辑器中使用该类,因此我还添加了逻辑来查找单击的字符,无论文本大小、缩进或文本对齐方式如何。如果您想要一个可以很好地处理纯文本的简单解决方案,请在自定义 ArrowKeyMovementMethod 类中使用它:
@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;
}
/* These are the lines of code you want to remove
else {
Selection.removeSelection(buffer);
}*/
}
return super.onTouchEvent(widget, buffer, event);
}
Run Code Online (Sandbox Code Playgroud)
不要忘记调用:myTv.setMovementMethod(new ClickAndSelectMovementMethod());
如果您想要支持各种文本格式的版本,请改用:
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ClickableSpan;
import android.text.style.LeadingMarginSpan;
import android.view.MotionEvent;
import android.widget.TextView;
/**
* ArrowKeyMovementMethod does support selection of text but not the clicking of
* links. LinkMovementMethod does support clicking of links but not the
* selection of text. This class adds the link clicking to the
* ArrowKeyMovementMethod. We basically take the LinkMovementMethod onTouchEvent
* code and remove the line Selection.removeSelection(buffer); which de-selects
* all text when no link was found.
*/
public class ClickAndSelectMovementMethod extends ArrowKeyMovementMethod {
private static Rect sLineBounds = new Rect();
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
int index = getCharIndexAt(widget, event);
if (index != -1) {
ClickableSpan[] link = buffer.getSpans(index, index, 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;
}
}
/*
* else { Selection.removeSelection(buffer); }
*/
}
return super.onTouchEvent(widget, buffer, event);
}
private int getCharIndexAt(TextView textView, MotionEvent event) {
// get coordinates
int x = (int) event.getX();
int y = (int) event.getY();
x -= textView.getTotalPaddingLeft();
y -= textView.getTotalPaddingTop();
x += textView.getScrollX();
y += textView.getScrollY();
/*
* Fail-fast check of the line bound. If we're not within the line bound
* no character was touched
*/
Layout layout = textView.getLayout();
int line = layout.getLineForVertical(y);
synchronized (sLineBounds) {
layout.getLineBounds(line, sLineBounds);
if (!sLineBounds.contains(x, y)) {
return -1;
}
}
// retrieve line text
Spanned text = (Spanned) textView.getText();
int lineStart = layout.getLineStart(line);
int lineEnd = layout.getLineEnd(line);
int lineLength = lineEnd - lineStart;
if (lineLength == 0) {
return -1;
}
Spanned lineText = (Spanned) text.subSequence(lineStart, lineEnd);
// compute leading margin and subtract it from the x coordinate
int margin = 0;
LeadingMarginSpan[] marginSpans = lineText.getSpans(0, lineLength, LeadingMarginSpan.class);
if (marginSpans != null) {
for (LeadingMarginSpan span : marginSpans) {
margin += span.getLeadingMargin(true);
}
}
x -= margin;
// retrieve text widths
float[] widths = new float[lineLength];
TextPaint paint = textView.getPaint();
paint.getTextWidths(lineText, 0, lineLength, widths);
// scale text widths by relative font size (absolute size / default size)
final float defaultSize = textView.getTextSize();
float scaleFactor = 1f;
AbsoluteSizeSpan[] absSpans = lineText.getSpans(0, lineLength, AbsoluteSizeSpan.class);
if (absSpans != null) {
for (AbsoluteSizeSpan span : absSpans) {
int spanStart = lineText.getSpanStart(span);
int spanEnd = lineText.getSpanEnd(span);
scaleFactor = span.getSize() / defaultSize;
int start = Math.max(lineStart, spanStart);
int end = Math.min(lineEnd, spanEnd);
for (int i = start; i < end; i++) {
widths[i] *= scaleFactor;
}
}
}
// find index of touched character
float startChar = 0;
float endChar = 0;
for (int i = 0; i < lineLength; i++) {
startChar = endChar;
endChar += widths[i];
if (endChar >= x) {
// which "end" is closer to x, the start or the end of the character?
int index = lineStart + (x - startChar < endChar - x ? i : i + 1);
return index;
}
}
return -1;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1302 次 |
| 最近记录: |