handle textview link click in my android app

前端 未结 12 905
迷失自我
迷失自我 2020-11-22 04:33

I\'m currently rendering HTML input in a TextView like so:

tv.setText(Html.fromHtml(\"test\"));

The HTML b

相关标签:
12条回答
  • 2020-11-22 05:03

    Just to share an alternative solution using a library I created. With Textoo, this can be achieved like:

    TextView locNotFound = Textoo
        .config((TextView) findViewById(R.id.view_location_disabled))
        .addLinksHandler(new LinksHandler() {
            @Override
            public boolean onClick(View view, String url) {
                if ("internal://settings/location".equals(url)) {
                    Intent locSettings = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                    startActivity(locSettings);
                    return true;
                } else {
                    return false;
                }
            }
        })
        .apply();
    

    Or with dynamic HTML source:

    String htmlSource = "Links: <a href='http://www.google.com'>Google</a>";
    Spanned linksLoggingText = Textoo
        .config(htmlSource)
        .parseHtml()
        .addLinksHandler(new LinksHandler() {
            @Override
            public boolean onClick(View view, String url) {
                Log.i("MyActivity", "Linking to google...");
                return false; // event not handled.  Continue default processing i.e. link to google
            }
        })
        .apply();
    textView.setText(linksLoggingText);
    
    0 讨论(0)
  • 2020-11-22 05:04

    Solution

    I have implemented a small class with the help of which you can handle long clicks on TextView itself and Taps on the links in the TextView.

    Layout

    TextView android:id="@+id/text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:autoLink="all"/>
    

    TextViewClickMovement.java

    import android.content.Context;
    import android.text.Layout;
    import android.text.Spannable;
    import android.text.method.LinkMovementMethod;
    import android.text.style.ClickableSpan;
    import android.util.Patterns;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.widget.TextView;
    
    public class TextViewClickMovement extends LinkMovementMethod {
    
        private final String TAG = TextViewClickMovement.class.getSimpleName();
    
        private final OnTextViewClickMovementListener mListener;
        private final GestureDetector                 mGestureDetector;
        private TextView                              mWidget;
        private Spannable                             mBuffer;
    
        public enum LinkType {
    
            /** Indicates that phone link was clicked */
            PHONE,
    
            /** Identifies that URL was clicked */
            WEB_URL,
    
            /** Identifies that Email Address was clicked */
            EMAIL_ADDRESS,
    
            /** Indicates that none of above mentioned were clicked */
            NONE
        }
    
        /**
         * Interface used to handle Long clicks on the {@link TextView} and taps
         * on the phone, web, mail links inside of {@link TextView}.
         */
        public interface OnTextViewClickMovementListener {
    
            /**
             * This method will be invoked when user press and hold
             * finger on the {@link TextView}
             *
             * @param linkText Text which contains link on which user presses.
             * @param linkType Type of the link can be one of {@link LinkType} enumeration
             */
            void onLinkClicked(final String linkText, final LinkType linkType);
    
            /**
             *
             * @param text Whole text of {@link TextView}
             */
            void onLongClick(final String text);
        }
    
    
        public TextViewClickMovement(final OnTextViewClickMovementListener listener, final Context context) {
            mListener        = listener;
            mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener());
        }
    
        @Override
        public boolean onTouchEvent(final TextView widget, final Spannable buffer, final MotionEvent event) {
    
            mWidget = widget;
            mBuffer = buffer;
            mGestureDetector.onTouchEvent(event);
    
            return false;
        }
    
        /**
         * Detects various gestures and events.
         * Notify users when a particular motion event has occurred.
         */
        class SimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {
            @Override
            public boolean onDown(MotionEvent event) {
                // Notified when a tap occurs.
                return true;
            }
    
            @Override
            public void onLongPress(MotionEvent e) {
                // Notified when a long press occurs.
                final String text = mBuffer.toString();
    
                if (mListener != null) {
                    Log.d(TAG, "----> Long Click Occurs on TextView with ID: " + mWidget.getId() + "\n" +
                                      "Text: " + text + "\n<----");
    
                    mListener.onLongClick(text);
                }
            }
    
            @Override
            public boolean onSingleTapConfirmed(MotionEvent event) {
                // Notified when tap occurs.
                final String linkText = getLinkText(mWidget, mBuffer, event);
    
                LinkType linkType = LinkType.NONE;
    
                if (Patterns.PHONE.matcher(linkText).matches()) {
                    linkType = LinkType.PHONE;
                }
                else if (Patterns.WEB_URL.matcher(linkText).matches()) {
                    linkType = LinkType.WEB_URL;
                }
                else if (Patterns.EMAIL_ADDRESS.matcher(linkText).matches()) {
                    linkType = LinkType.EMAIL_ADDRESS;
                }
    
                if (mListener != null) {
                    Log.d(TAG, "----> Tap Occurs on TextView with ID: " + mWidget.getId() + "\n" +
                                      "Link Text: " + linkText + "\n" +
                                      "Link Type: " + linkType + "\n<----");
    
                    mListener.onLinkClicked(linkText, linkType);
                }
    
                return false;
            }
    
            private String getLinkText(final TextView widget, final Spannable buffer, final MotionEvent event) {
    
                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) {
                    return buffer.subSequence(buffer.getSpanStart(link[0]),
                            buffer.getSpanEnd(link[0])).toString();
                }
    
                return "";
            }
        }
    }
    

    Usage

    TextView tv = (TextView) v.findViewById(R.id.textview);
    tv.setText(Html.fromHtml("<a href='test'>test</a>"));
    textView.setMovementMethod(new TextViewClickMovement(this, context));
    

    Links

    Hope this helps! You can find code here.

    0 讨论(0)
  • 2020-11-22 05:07

    Here is a more generic solution based on @Arun answer

    public abstract class TextViewLinkHandler extends LinkMovementMethod {
    
        public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
            if (event.getAction() != MotionEvent.ACTION_UP)
                return super.onTouchEvent(widget, buffer, event);
    
            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);
    
            URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
            if (link.length != 0) {
                onLinkClick(link[0].getURL());
            }
            return true;
        }
    
        abstract public void onLinkClick(String url);
    }
    

    To use it just implement onLinkClick of TextViewLinkHandler class. For instance:

        textView.setMovementMethod(new TextViewLinkHandler() {
            @Override
            public void onLinkClick(String url) {
                Toast.makeText(textView.getContext(), url, Toast.LENGTH_SHORT).show();
            }
        });
    
    0 讨论(0)
  • 2020-11-22 05:09

    This answer extends Jonathan S's excellent solution:

    You can use the following method to extract links from the text:

    private static ArrayList<String> getLinksFromText(String text) {
            ArrayList links = new ArrayList();
    
            String regex = "\(?\b((http|https)://www[.])[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|]";
            Pattern p = Pattern.compile(regex);
            Matcher m = p.matcher(text);
            while (m.find()) {
                String urlStr = m.group();
                if (urlStr.startsWith("(") && urlStr.endsWith(")")) {
                    urlStr = urlStr.substring(1, urlStr.length() - 1);
                }
                links.add(urlStr);
            }
            return links;
        }
    

    This can be used to remove one of the parameters in the clickify() method:

    public static void clickify(TextView view,
                                    final ClickSpan.OnClickListener listener) {
    
            CharSequence text = view.getText();
            String string = text.toString();
    
    
            ArrayList<String> linksInText = getLinksFromText(string);
            if (linksInText.isEmpty()){
                return;
            }
    
    
            String clickableText = linksInText.get(0);
            ClickSpan span = new ClickSpan(listener,clickableText);
    
            int start = string.indexOf(clickableText);
            int end = start + clickableText.length();
            if (start == -1) return;
    
            if (text instanceof Spannable) {
                ((Spannable) text).setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            } else {
                SpannableString s = SpannableString.valueOf(text);
                s.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                view.setText(s);
            }
    
            MovementMethod m = view.getMovementMethod();
            if ((m == null) || !(m instanceof LinkMovementMethod)) {
                view.setMovementMethod(LinkMovementMethod.getInstance());
            }
        }
    

    A few changes to the ClickSpan:

    public static class ClickSpan extends ClickableSpan {
    
            private String mClickableText;
            private OnClickListener mListener;
    
            public ClickSpan(OnClickListener listener, String clickableText) {
                mListener = listener;
                mClickableText = clickableText;
            }
    
            @Override
            public void onClick(View widget) {
                if (mListener != null) mListener.onClick(mClickableText);
            }
    
            public interface OnClickListener {
                void onClick(String clickableText);
            }
        }
    

    Now you can simply set the text on the TextView and then add a listener to it:

    TextViewUtils.clickify(textWithLink,new TextUtils.ClickSpan.OnClickListener(){
    
    @Override
    public void onClick(String clickableText){
      //action...
    }
    
    });
    
    0 讨论(0)
  • 2020-11-22 05:12

    Another way, borrows a bit from Linkify but allows you to customize your handling.

    Custom Span Class:

    public class ClickSpan extends ClickableSpan {
    
        private OnClickListener mListener;
    
        public ClickSpan(OnClickListener listener) {
            mListener = listener;
        }
    
        @Override
        public void onClick(View widget) {
           if (mListener != null) mListener.onClick();
        }
    
        public interface OnClickListener {
            void onClick();
        }
    }
    

    Helper function:

    public static void clickify(TextView view, final String clickableText, 
        final ClickSpan.OnClickListener listener) {
    
        CharSequence text = view.getText();
        String string = text.toString();
        ClickSpan span = new ClickSpan(listener);
    
        int start = string.indexOf(clickableText);
        int end = start + clickableText.length();
        if (start == -1) return;
    
        if (text instanceof Spannable) {
            ((Spannable)text).setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        } else {
            SpannableString s = SpannableString.valueOf(text);
            s.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            view.setText(s);
        }
    
        MovementMethod m = view.getMovementMethod();
        if ((m == null) || !(m instanceof LinkMovementMethod)) {
            view.setMovementMethod(LinkMovementMethod.getInstance());
        }
    }
    

    Usage:

     clickify(textView, clickText,new ClickSpan.OnClickListener()
         {
            @Override
            public void onClick() {
                // do something
            }
        });
    
    0 讨论(0)
  • 2020-11-22 05:13

    for who looks for more options here is a one

    // Set text within a `TextView`
    TextView textView = (TextView) findViewById(R.id.textView);
    textView.setText("Hey @sarah, where did @jim go? #lost");
    // Style clickable spans based on pattern
    new PatternEditableBuilder().
        addPattern(Pattern.compile("\\@(\\w+)"), Color.BLUE,
           new PatternEditableBuilder.SpannableClickedListener() {
            @Override
            public void onSpanClicked(String text) {
                Toast.makeText(MainActivity.this, "Clicked username: " + text,
                    Toast.LENGTH_SHORT).show();
            }
    }).into(textView);
    

    RESOURCE : CodePath

    0 讨论(0)
提交回复
热议问题