我们的应用程序有几个TextViews实例,其内容由myTv.setText(Html.fromHtml());
设置,适用于Android 4.4.0及更低版本。
从4.4.2和Lollypop开始,这些链接已停止工作。文本仍然带有下划线和超链接颜色,但点击它们不会产生任何结果。
必须要说的是,这些字段被标记为可复制粘贴,已知这些字段与这些字段相互作用。
有没有人能够解决这个问题?
答案 0 :(得分:2)
问题在于,当在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);
}
别忘了打电话:
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;
}
}
答案 1 :(得分:1)
默认情况下,素材Buttons
和Textviews
的样式设置为以全大写字母显示文字。但是,AllCapsTransformationMethod中存在导致丢弃其他文本格式的错误,例如。 Spannable
。
因此,当您尝试在Lollipop上更改Button
的字体大小时,如下所示:
SpannableString span = new SpannableString(text);
span.setSpan(new AbsoluteSizeSpan(8, true), 5, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
testButton.setText(span);
它也不会起作用(仅适用于Lollipop)。
<强>解决方案强>:
针对您的案例的解决方法和描述的Spannable案例已设置为 textAllCaps
至 false
:
<TextView
...
android:textAllCaps="false" />
答案 2 :(得分:0)
final Spanned spanned = Html.fromHtml("<a href='http://google.com'>My link</a>");
textView.setText(spanned);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ClickableSpan[] links = spanned.getSpans(0, spanned.length(), ClickableSpan.class);
if (links.length > 0) {
links[0].onClick(v);
}
}
});
通过这种方式,您可以通过长按选择文本,只需单击即可打开链接。您可以实现更高级的实现,以允许双击文本选择。
问题似乎存在于以下代码中。它是默认TextView
onTouchEvent
的一部分:
if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) {
// The LinkMovementMethod which should handle taps on links has not been installed
// on non editable text that support text selection.
// We reproduce its behavior here to open links for these.
ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(),
getSelectionEnd(), ClickableSpan.class);
if (links.length > 0) {
links[0].onClick(this);
handled = true;
}
}
我们可以看到if
块的运行条件之一是mAutoLinkMask != 0
。
如果您将android:autoLink
更改为默认值0或none
以外的值,则if块会运行但没有链接。
我想这是开发人员错过的,应该在我看来是一个错误。
如果您只需要http://google.com之类的简单链接而不更改其外观,则可以在android:autoLink="web"
上设置TextView
并完成。链接将自动找到并可以点击。但这不适用于<a href="..."></a>
类型的链接。
答案 3 :(得分:0)
试试这个,它对我来说很好。我从我的资产文件夹加载html文件以满足我的要求,但你只需要设置textview的所有可链接属性,它就可以了......它会起作用......
<TextView
android:id="@+id/txt_terms_and_conditions"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_world"
android:linksClickable="true"
android:autoLink="phone|email|web" />
并在代码中尝试设置为像这样的HTML
mTxtTearmsAndConditions.setText(Html.fromHtml(total.toString()));