gérer le lien textview cliquez sur dans mon application android

je suis en train de rendre HTML input dans un TextView comme ceci:

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

le HTML affiché m'est fourni via une ressource externe, donc je ne peux pas changer les choses comme je le veux, mais je peux, bien sûr, faire quelque modification regex avec le HTML, pour changer la valeur href, disons, à quelque chose d'autre.

Ce que je veux, c'est être capable de gérer un lien, cliquez directement depuis l'application, plutôt que d'avoir le lien, ouvrez une fenêtre de navigateur. Être ce réalisable? Je suppose qu'il serait possible de définir le protocole de la valeur href à quelque chose comme "myApp://", puis enregistrer quelque chose qui laisserait mon application gérer ce protocole. Si c'est effectivement la meilleure façon, j'aimerais savoir comment faire, mais j'espère qu'il y a une façon plus simple de dire, "quand un lien est cliqué dans ce textview, je veux soulever un événement qui reçoit la valeur href du lien comme paramètre d'entrée"

136
demandé sur David Hedlund 2009-11-08 19:42:02

13 réponses

presque un an plus tard, il y a une autre façon dont j'ai résolu mon problème particulier. Comme je voulais que le lien soit géré par ma propre application, il y a une solution un peu plus simple.

outre le filtre d'intention par défaut, je laisse simplement mon activité cible écouter ACTION_VIEW intentions, et spécifiquement, ceux avec le 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:// sera géré par mon activité.

donc tout ce que j'ai à faire est de construire une URL qui contient les informations que je veux transmettre:

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

dans mon activité cible, je peux retrouver cette adresse:

Uri data = getIntent().getData();

dans mon exemple, je pourrais simplement vérifier data pour les valeurs nulles, parce que quand jamais il n'est pas null, je saurai qu'il a été invoqué au moyen d'un tel lien. De là, j'extrait les instructions dont j'ai besoin à partir de l'url pour être en mesure de afficher les données appropriées.

176
répondu David Hedlund 2010-08-10 20:03:52

une Autre façon, emprunte un peu de Linkify mais vous permet de personnaliser votre manipulation.

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

fonction Helper:

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

Utilisation:

 clickify(textView, clickText,new ClickSpan.OnClickListener()
     {
        @Override
        public void onClick() {
            // do something
        }
    });
60
répondu Jonathan S. 2011-04-15 20:18:31

s'il y a plusieurs liens dans la vue texte . Par exemple, textview a "https://" et "tel no" nous pouvons personnaliser la méthode LinkMovement et gérer les clics pour les mots basés sur un motif. Attaché est la méthode de mouvement de lien personnalisé.

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

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

textViewObject.setMovementMethod(CustomLinkMovementMethod.getInstance(context));
52
répondu Arun 2014-06-18 18:02:54

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 de la classe TextViewLinkHandler . Par exemple:

    textView.setMovementMethod(new TextViewLinkHandler() {
        @Override
        public void onLinkClick(String url) {
            Toast.makeText(textView.getContext(), url, Toast.LENGTH_SHORT);
        }
    });
32
répondu ruX 2015-09-07 17:53:00

son très simple ajouter cette ligne à votre code:

tv.setMovementMethod(LinkMovementMethod.getInstance());
10
répondu jonathan 2012-04-28 02:49:46

Solution

j'ai mis en place une petite classe avec l'aide de laquelle vous pouvez gérer les longs clics sur TextView lui-même et les robinets sur les liens dans TextView.

Mise en page

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

Utilisation

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

Liens

Espérons que cette aide! Vous pouvez trouver le code ici .

4
répondu Victor Apoyan 2018-05-28 00:37:59

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

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 le HTML dynamique 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);
2
répondu PH88 2016-01-14 08:06:57

pour qui cherche plus d'options ici est 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);

RESOURCE: CodePath

2
répondu Dasser Basyouni 2017-01-12 05:31:44
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
répondu Kai Wang 2017-07-06 20:38:38

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 utiliser sa propriété onClick pour spécifier une méthode( je suppose que vous pouvez appeler setOnClickListener(this) comme une autre façon), par exemple:

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

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

1
répondu Tyler Collier 2017-05-23 12:10:48

La meilleure façon que j'ai utilisé et ça a toujours marché pour moi

android:autoLink="web"
1
répondu Rohit Mandiwal 2013-12-18 03:16:48

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 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 et y ajouter un auditeur:

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

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

});
1
répondu W.K.S 2015-11-26 08:02:44

exemple: supposons que vous ayez défini un texte dans textview et que vous vouliez fournir un lien sur une expression textuelle particulière: "Cliquez sur #facebook vous mènera à facebook.com "

dans la mise en page xml:

<TextView
            android:id="@+id/testtext"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

En Activité:

String text  =  "Click on #facebook will take you to facebook.com";
tv.setText(text);
Pattern tagMatcher = Pattern.compile("[#]+[A-Za-z0-9-_]+\b");
String newActivityURL = "content://ankit.testactivity/";
Linkify.addLinks(tv, tagMatcher, newActivityURL);

crée aussi un fournisseur d'étiquettes comme:

public class TagProvider extends ContentProvider {

    @Override
    public int delete(Uri arg0, String arg1, String[] arg2) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String getType(Uri arg0) {
        return "vnd.android.cursor.item/vnd.cc.tag";
    }

    @Override
    public Uri insert(Uri arg0, ContentValues arg1) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
                        String arg4) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
        // TODO Auto-generated method stub
        return 0;
    }

}

dans le fichier du Manifeste:

<provider
    android:name="ankit.TagProvider"
    android:authorities="ankit.testactivity" />

<activity android:name=".TestActivity"
    android:label = "@string/app_name">
    <intent-filter >
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="vnd.android.cursor.item/vnd.cc.tag" />
    </intent-filter>
</activity>

maintenant quand vous cliquez sur # facebook, il va invoquer testiactivtiy. Et dans l'activité de test vous pouvez obtenir les données comme:

Uri uri = getIntent().getData();
0
répondu Ankit Adlakha 2016-12-31 07:17:06