web-dev-qa-db-fra.com

gérer le lien textview cliquez dans mon application Android

Je suis en train de rendre une entrée HTML dans un TextView comme ceci:

tv.setText(Html.fromHtml("<a href='test'>test</a>"));

Le code HTML affiché m’est fourni par l’intermédiaire d’une ressource externe. Je ne peux donc pas changer les choses comme je le ferai, mais je peux bien sûr modifier de façon rationnelle le code HTML, pour modifier la valeur href, par exemple.

Ce que je veux, c'est pouvoir gérer un clic sur un lien directement depuis l'application, au lieu que le lien ouvre une fenêtre de navigateur. Est-ce réalisable du tout? J'imagine qu'il serait possible de définir le protocole de la valeur href sur quelque chose du type "myApp: //", puis d'enregistrer quelque chose qui laisserait mon application gérer ce protocole. Si c’est effectivement le meilleur moyen, j'aimerais savoir comment cela se fait, mais j’espère qu’il existe un moyen plus simple de dire: "lorsqu’un lien est cliqué dans cette vue en mode texte, je souhaite créer un événement qui reçoit la valeur href du lien en tant que paramètre d'entrée "

137
David Hedlund

Presque un an plus tard, j’ai résolu mon problème particulier de manière différente. Puisque je voulais que le lien soit géré par ma propre application, il existe une solution un peu plus simple.

Outre le filtre d'intention par défaut, je laisse simplement mon activité cible écouter les intentions ACTION_VIEW, et plus particulièrement celles du schéma com.package.name.

<intent-filter>
    <category Android:name="Android.intent.category.DEFAULT" />
    <action Android:name="Android.intent.action.VIEW" />
    <data Android:scheme="com.package.name" />  
</intent-filter>

Cela signifie que les liens commençant par com.package.name:// seront gérés par mon activité.

Donc, tout ce que j'ai à faire est de construire une URL contenant les informations que je veux transmettre:

com.package.name://action-to-perform/id-that-might-be-needed/

Dans mon activité cible, je peux récupérer cette adresse:

Uri data = getIntent().getData();

Dans mon exemple, je pourrais simplement vérifier data pour les valeurs NULL, car chaque fois que ce n'est pas null, je saurai qu'elle a été invoquée au moyen d'un tel lien. À partir de là, j'extrais les instructions dont j'ai besoin de l'URL pour pouvoir afficher les données appropriées.

177
David Hedlund

Une autre façon, emprunte un peu à Linkify mais vous permet de personnaliser votre traitement.

Classe d'envergure personnalisée:

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

Fonction d'assistance:

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
        }
    });
61
Jonathan S.

s'il y a plusieurs liens dans la vue texte. Par exemple, textview a "https: //" et "tel non", nous pouvons personnaliser la méthode LinkMovement et gérer les clics sur les mots basés sur un modèle. Vous trouverez ci-joint la méthode de déplacement de lien personnalisée.

public class CustomLinkMovementMethod extends LinkMovementMethod
{

private static Context movementContext;

private static CustomLinkMovementMethod linkMovementMethod = new CustomLinkMovementMethod();

public boolean onTouchEvent(Android.widget.TextView widget, Android.text.Spannable buffer, Android.view.MotionEvent event)
{
    int action = event.getAction();

    if (action == MotionEvent.ACTION_UP)
    {
        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)
        {
            String url = link[0].getURL();
            if (url.startsWith("https"))
            {
                Log.d("Link", url);
                Toast.makeText(movementContext, "Link was clicked", Toast.LENGTH_LONG).show();
            } else if (url.startsWith("tel"))
            {
                Log.d("Link", url);
                Toast.makeText(movementContext, "Tel was clicked", Toast.LENGTH_LONG).show();
            } else if (url.startsWith("mailto"))
            {
                Log.d("Link", url);
                Toast.makeText(movementContext, "Mail link was clicked", Toast.LENGTH_LONG).show();
            }
            return true;
        }
    }

    return super.onTouchEvent(widget, buffer, event);
}

public static Android.text.method.MovementMethod getInstance(Context c)
{
    movementContext = c;
    return linkMovementMethod;
}

Cela doit être appelé à partir de textview de la manière suivante:

textViewObject.setMovementMethod(CustomLinkMovementMethod.getInstance(context));
52
Arun

Voici une solution plus générique basée sur @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);
}

Pour l'utiliser, il suffit d'implémenter onLinkClick of TextViewLinkHandler class. Par exemple:

    textView.setMovementMethod(new TextViewLinkHandler() {
        @Override
        public void onLinkClick(String url) {
            Toast.makeText(textView.getContext(), url, Toast.LENGTH_SHORT);
        }
    });
34
ruX

c'est très simple d'ajouter cette ligne à votre code:

tv.setMovementMethod(LinkMovementMethod.getInstance());
10
jonathan

Solution

J'ai implémenté une petite classe à l'aide de laquelle vous pouvez gérer de longs clics sur TextView et des pressions sur les liens dans TextView.

Disposition

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

Liens

J'espère que cela t'aides! Vous pouvez trouver le code ici .

4
Victor Apoyan

Juste pour partager une solution alternative en utilisant une bibliothèque que j'ai créée. Avec Textoo , cela peut être réalisé comme suit:

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

Ou avec une source HTML dynamique:

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);
3
PH88

pour qui cherche plus d'options, voici un

// 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);

RESSOURCE:CodePath

2
Dasser Basyouni
public static void setTextViewFromHtmlWithLinkClickable(TextView textView, String text) {
    Spanned result;
    if (Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.N) {
        result = Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY);
    } else {
        result = Html.fromHtml(text);
    }
    textView.setText(result);
    textView.setMovementMethod(LinkMovementMethod.getInstance());
}
2
Kai Wang

Cette réponse prolonge l'excellente solution de Jonathan S:

Vous pouvez utiliser la méthode suivante pour extraire des liens du texte:

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

Ceci peut être utilisé pour supprimer l'un des paramètres de la méthode clickify():

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

Quelques modifications au 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);
        }
    }

Maintenant, vous pouvez simplement définir le texte sur le TextView puis y ajouter un écouteur:

TextViewUtils.clickify(textWithLink,new TextUtils.ClickSpan.OnClickListener(){

@Override
public void onClick(String clickableText){
  //action...
}

});
1
W.K.S

J'ai changé la couleur du TextView en bleu en utilisant par exemple: 

Android:textColor="#3399FF"

dans le fichier XML. Comment le faire souligné est expliqué ici

Puis utilisez sa propriété onClick pour spécifier une méthode (je suppose que vous pourriez appeler setOnClickListener(this) autrement), par exemple:

myTextView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
    doSomething();
}
});

Avec cette méthode, je peux faire ce que je veux comme d'habitude, comme lancer une intention. Notez que vous devez toujours faire la chose normale myTextView.setMovementMethod(LinkMovementMethod.getInstance());, comme dans la méthode onCreate () de votre activité.

1
Tyler Collier

Le meilleur moyen que j'ai utilisé et cela a toujours fonctionné pour moi

Android:autoLink="web"
1
Rohit Mandiwal