TextView fromHtml links broken on Lollipop

In our application, there were several instances of TextViews with its installed content myTv.setText(Html.fromHtml());that worked for Android 4.4.0 and below.

Starting with 4.4.2 and Lollypop, these links stopped working. The text is still displayed underlined and with a hyperlink, but clicking on them does not produce any results.

It should be said that these fields are marked as copyable, which are known to interact with these spannables.

Has anyone been able to solve this problem?

+4
source share
4 answers

, TextView Android ArrowKeyMovementMethod, , . LinkMovementMethod, , ( , Lollipop, KitKat Android).

, ArrayKeyMovementMethod onTouchEvent LinkMovementMethod 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( 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;
    }
}
+2

Buttons Textviews . AllCapsTransformationMethod, , . Spannable. , Button Lollipop, :

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" />
+1

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, .

, .

Sidenote

, http://google.com , android:autoLink="web" TextView . . <a href="..."></a>.

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()));
0

All Articles