About Kamesh Rao

Kamesh is a Software Development Engineer at Amazon.com working in Advertising and Payments domain. He has extensive experience in web and mobile platform and applications development with hands on experience in Java, C++, Ruby, Scala, Objective-C among others.

Android Custom Hyperlinked TextView

Finding Links in Android is very simple you might have heard of Linkify which is a great class in which there are many static methods for getting a simple job done but the problem is that in Linkify you cannot specify the behavior that you require on clicking the links at the Most what Linkify does is that it simply finds the links based on the “Pattern” you provide and adds a “Scheme” string to complete the URL and created a Android Intent for Launching the Browser. The Linkify behavior cannot be overridden as all the methods in Linkify class are static final.

This was the issue that inspired me to create a Custom TextView Widget the one which collects the links in my scenario I also programmed it to collect the the Strings starting with “@” and “#” but this things can be changed by simply changing the Patterns that you require and giving the proper Regular Expression for them.

The LinkEnableTextView class is like this:

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ClickableSpan;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;


public class LinkEnabledTextView  extends TextView
{
// The String Containing the Text that we have to gather links from private SpannableString linkableText;
// Populating and gathering all the links that are present in the Text
private ArrayList<Hyperlink> listOfLinks; 

// A Listener Class for generally sending the Clicks to the one which requires it
TextLinkClickListener mListener;

// Pattern for gathering @usernames from the Text
Pattern screenNamePattern = Pattern.compile('(@[a-zA-Z0-9_]+)');

// Pattern for gathering #hasttags from the Text
Pattern hashTagsPattern = Pattern.compile('(#[a-zA-Z0-9_-]+)');

// Pattern for gathering http:// links from the Text
Pattern hyperLinksPattern = Pattern.compile('([Hh][tT][tT][pP][sS]?:\\/\\/[^ ,'\'>\\]\\)]*[^\\. ,'\'>\\]\\)])');

public LinkEnabledTextView(Context context, AttributeSet attrs)
{
    super(context, attrs);
    listOfLinks = new ArrayList<Hyperlink>();

}

public void gatherLinksForText(String text)
{
    linkableText = new SpannableString(text);
 //gatherLinks basically collects the Links depending upon the Pattern that we supply
 //and add the links to the ArrayList of the links
  
    gatherLinks(listOfLinks, linkableText, screenNamePattern);
    gatherLinks(listOfLinks, linkableText, hashTagsPattern);
    gatherLinks(listOfLinks, linkableText, hyperLinksPattern);

    for(int i = 0; i< listOfLinks.size(); i++)
    {
        Hyperlink linkSpec = listOfLinks.get(i);
        android.util.Log.v('listOfLinks :: ' + linkSpec.textSpan, 'listOfLinks :: ' + linkSpec.textSpan);
        
        // this process here makes the Clickable Links from the text
         
        linkableText.setSpan(linkSpec.span, linkSpec.start, linkSpec.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    
     // sets the text for the TextView with enabled links
     
    setText(linkableText);
}


 // sets the Listener for later click propagation purpose
 
public void setOnTextLinkClickListener(TextLinkClickListener newListener)
{
    mListener = newListener;
}

  //The Method mainly performs the Regex Comparison for the Pattern and adds them to
  //listOfLinks array list
 

private final void gatherLinks(ArrayList<Hyperlink> links,
                               Spannable s, Pattern pattern)
{
    // Matcher matching the pattern
    Matcher m = pattern.matcher(s);

    while (m.find())
    {
        int start = m.start();
        int end = m.end();

        
    // Hyperlink is basically used like a structure for storing the information about
    // where the link was found.
        
        Hyperlink spec = new Hyperlink();

        spec.textSpan = s.subSequence(start, end);
        spec.span = new InternalURLSpan(spec.textSpan.toString());
        spec.start = start;
        spec.end = end;

        links.add(spec);
    }
}


// This is class which gives us the clicks on the links which we then can use.
 

public class InternalURLSpan extends ClickableSpan
{
    private String clickedSpan;

    public InternalURLSpan (String clickedString)
    {
        clickedSpan = clickedString;
    }

    @Override
    public void onClick(View textView)
    {
        mListener.onTextLinkClick(textView, clickedSpan);
    }
}


// Class for storing the information about the Link Location


class Hyperlink
{
    CharSequence textSpan;
    InternalURLSpan span;
    int start;
    int end;
}

Now, having this you require just another interface for propagating the clicks to the place you require to handle them in my case I implemented the interface in my Activity and simple wrote a Log Command there.

The TextLinkClickListener interface is like this:

import android.view.View;

public interface TextLinkClickListener
{

 
  //  This method is called when the TextLink is clicked from LinkEnabledTextView
  
public void onTextLinkClick(View textView, String clickedString)
}

After doing all this you just require to create an Activity using the Custom LinkEnabledTextView and check the things out yourself. There are a few things that you must do while creating a object of the Custom LinkEnabledTextView those are mentioned and described in the Code of the Activity below:

import android.text.method.MovementMethod;
import com.umundoinc.Tvider.Component.LinkEnabledTextView.LinkEnabledTextView;
import com.umundoinc.Tvider.Component.LinkEnabledTextView.TextLinkClickListener;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.View;

//Here the Activity is implementing the TextLinkClickListener the one we have created
//the Clicks over the Links are forwarded over here from the LinkEnabledTextView
 
public class TextViewActivity  extends Activity  implements TextLinkClickListener 
{
private LinkEnabledTextView check;
protected void onCreate(Bundle savedInstance)
{
    super.onCreate(savedInstance);

    String text  =  "This is a #test of regular expressions with http://example.com/ links as used in @twitter for performing various operations based on the links this handles multiple links like http://this_is_fun.com/ and #Awesomess and @Cool";

    check = new LinkEnabledTextView(this, null);
    check.setOnTextLinkClickListener(this);
    check.gatherLinksForText(text);
    check.setTextColor(Color.WHITE);
    check.setLinkTextColor(Color.GREEN);

    MovementMethod m = check.getMovementMethod();
    if ((m == null) || !(m instanceof LinkMovementMethod)) {
        if (check.getLinksClickable()) {
            check.setMovementMethod(LinkMovementMethod.getInstance());
        }
    }

    setContentView(check);
}

public void onTextLinkClick(View textView, String clickedString)
{
    android.util.Log.v('Hyperlink clicked is :: ' + clickedString, 'Hyperlink clicked is :: ' + clickedString);
}

Here, is the Screenshot describing the output in my Listener method I programmed it to display a Toast but anything can be achieved in same method.

Now that’s pretty much all that you’ll need that makes the Custom LinkEnabledTextView working, try it out and share your views and reviews over it.

Reference: Android Custom Hyperlinked TextView from our JCG partner Y Kamesh Rao at the OrangeApple blog.

Related Whitepaper:

Rapid Android Development: Build Rich, Sensor-Based Applications with Processing

Create mobile apps for Android phones and tablets faster and more easily than you ever imagined

Use 'Processing', the free, award-winning, graphics-savvy language and development environment, to work with the touchscreens, hardware sensors, cameras, network transceivers, and other devices and software in the latest Android phones and tablets.

Get it Now!  

4 Responses to "Android Custom Hyperlinked TextView"

  1. Gyanendra Singh says:

    Thanks a lot man! You save my weekend. Cheers.

  2. Steve M says:

    Good article. setLinkTextColor changes the color of all the links in the TextView. Is there a way to change the color of the clicked link alone while being touched? (just like a button pressed state). That feedback is an essential user experience.

  3. Emanuel says:

    Thanks mate, this was exactly what I was looking for.
    Just made a few tweaks to it that might be useful like sending the type of link that was clicked.

    This is my code:

    import java.util.ArrayList;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    import android.content.Context;
    import android.text.Spannable;
    import android.text.SpannableString;
    import android.text.style.ClickableSpan;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.TextView;

    public class LinkEnabledTextView extends TextView {
    // The String Containing the Text that we have to gather links from private SpannableString linkableText;
    // Populating and gathering all the links that are present in the Text
    private ArrayList listOfLinks;
    private SpannableString linkableText;

    // The type of tag that was clicked
    public enum Type {
    USER, HASTAG, URL
    }

    // A Listener Class for generally sending the Clicks to the one which requires it
    TextLinkClickListener mListener;

    // Pattern for gathering @usernames from the Text
    Pattern userNamePattern = Pattern.compile(“(@[a-zA-Z0-9_]+)”);

    // Pattern for gathering #hasttags from the Text
    Pattern hashTagsPattern = Pattern.compile(“(#[a-zA-Z0-9_-]+)”);

    // Pattern for gathering http:// links from the Text
    Pattern hyperLinksPattern = Pattern.compile(“([Hh][tT][tT][pP][sS]?:\\/\\/[^ ,'\'>\\]\\)]*[^\\. ,'\'>\\]\\)])”);

    public LinkEnabledTextView(Context context) {
    super(context);
    init();
    }

    public LinkEnabledTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
    }

    private void init() {
    listOfLinks = new ArrayList();
    }

    public void gatherLinksForText(String text) {
    linkableText = new SpannableString(text);
    // gatherLinks basically collects the Links depending upon the Pattern that we supply
    // and add the links to the ArrayList of the links

    gatherLinks(listOfLinks, linkableText, userNamePattern, Type.USER);
    gatherLinks(listOfLinks, linkableText, hashTagsPattern, Type.HASTAG);
    gatherLinks(listOfLinks, linkableText, hyperLinksPattern, Type.URL);

    for (int i = 0; i < listOfLinks.size(); i++) {
    Hyperlink linkSpec = listOfLinks.get(i);
    //android.util.Log.v("listOfLinks :: " + linkSpec.textSpan, "listOfLinks :: " + linkSpec.textSpan);

    // this process here makes the Clickable Links from the text

    linkableText.setSpan(linkSpec.span, linkSpec.start, linkSpec.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    // sets the text for the TextView with enabled links

    setText(linkableText);
    }

    // sets the Listener for later click propagation purpose

    public void setOnTextLinkClickListener(TextLinkClickListener newListener) {
    mListener = newListener;
    }

    // The Method mainly performs the Regex Comparison for the Pattern and adds them to
    // listOfLinks array list

    private final void gatherLinks(ArrayList links, Spannable s, Pattern pattern, Type type) {
    // Matcher matching the pattern
    Matcher m = pattern.matcher(s);

    while (m.find()) {
    int start = m.start();
    int end = m.end();

    // Hyperlink is basically used like a structure for storing the information about
    // where the link was found.

    Hyperlink spec = new Hyperlink();

    spec.textSpan = s.subSequence(start, end);
    spec.span = new InternalURLSpan(spec.textSpan.toString(), type);
    spec.start = start;
    spec.end = end;

    links.add(spec);
    }
    }

    // This is class which gives us the clicks on the links which we then can use.

    public class InternalURLSpan extends ClickableSpan {
    private String clickedSpan;
    private Type type;

    public InternalURLSpan(String clickedString, Type type) {
    clickedSpan = clickedString;
    this.type = type;
    }

    @Override
    public void onClick(View textView) {
    mListener.onTextLinkClick(textView, clickedSpan, type);
    }
    }

    // Class for storing the information about the Link Location

    class Hyperlink {
    CharSequence textSpan;
    InternalURLSpan span;
    int start;
    int end;
    }

    public interface TextLinkClickListener {

    // This method is called when the TextLink is clicked from LinkEnabledTextView

    public void onTextLinkClick(View textView, String clickedString, Type type);
    }

    }

  4. This Post is Actually Contributed by Y. Ramesh Rao

    (http://stackoverflow.com/users/92039/y-ramesh-rao)

    Just wanted to get the credits right.

Leave a Reply


× 4 = twenty



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books