1

My application loads comments from an API which often contain links with the same markup as here on Stack Overflow. (If there is a name for this markup style to help me Google it, please let me know in comments)

//this is the markup I am referring to
[Here's a picture](https://www.web.com/sub/path/to/picture/?st=JHTYA46&am-p;sh=487Bac48)

I tried converting them to links with

private static final String REGEX_LINK_MARKUP = "\\[(.*?)\\]\\((.*?)\\)";
private static final String REGEX_LINK_REPLACEMENT = "<a href=\"$2\">$1</a>";
commentText.setText(comment.getBody().replaceAll(REGEX_LINK_MARKUP, REGEX_LINK_REPLACEMENT)));

and using

android:autoLink="all"

But of course that showed the HTML with the href part clickable so I am currently converting them to links with

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    //the constants are the same patterns from above
    commentText.setText(Html.fromHtml(comment.getBody().replaceAll(REGEX_LINK_MARKUP, REGEX_LINK_REPLACEMENT), Html.FROM_HTML_MODE_LEGACY));
} else {
    commentText.setText(Html.fromHtml(comment.getBody().replaceAll(REGEX_LINK_MARKUP, REGEX_LINK_REPLACEMENT)));
}

I now see the correctly coloured span, but it isn't clickable. The field already has

android:linksClickable="true"

Whether I leave the following to none or all makes no difference (the link is unclickable)

android:autoLink="none"
  • What is the correct way to make this type of markup clickable in a TextView?
  • Is there a way to make TextView links without using HTML?
  • Is there a more efficient regex than my very basic pattern?
Zoe
  • 27,060
  • 21
  • 118
  • 148
Nick Cardoso
  • 20,807
  • 14
  • 73
  • 124
  • You might need to be careful using this type of expression `\[(.*?)\]\((.*?)\)`. It could be that the an escaped delimiter is valid inside the delimiter's. –  Apr 13 '17 at 19:10
  • Good advice, this was a lazy first try, I was basing it on a comment I just came across something like `"fsfsg [dfsdf sdfds](...) :)"` so made it lazy matching – Nick Cardoso Apr 13 '17 at 19:13

2 Answers2

2

This is a suggestion using replace and split:

public static String getFormatted(String rawInput){//rawInput is the link. It does not find links from a bulk of text.
    String[] content;
    content = rawInput.split("\\]\\(");//Split the input
    String ctnt = content[0].replaceAll("\\[", "");//remove remaining link bracket
    String link = content[1].replaceAll("\\)", "");
    String format = "<a href=\"%s\">%s</a>";//The format
    return String.format(Locale.ENGLISH, format, link, ctnt);//Format and return the link in the format of an `a`-tag
}

calling like:

System.out.println(getFormatted("[This is link text](https://example.com)"));//System.out.println is to show it works

Which returns:

<a href="https://example.com">This is link text</a>

To set the link as clickable, you simply do the following(source):

TextView t2 = (TextView) findViewById(R.id.text2);
//Set text if you do that. If you do set the text in Java code, make sure
//you add Html.fromHtml() to allow it. You may need to add <p> tags as well
t2.setMovementMethod(LinkMovementMethod.getInstance());

That allows you to have <a href"... in the textview and making it clickable

is there a way to make TextView links without using HTML?

According to what I have seen, you cannot do that without displaying the full link(example: www.google.com).


EDIT:

TO take any given text and reformat it, do this:

    String pattern = "(\\[.*\\]\\(.*\\))";
    Pattern p = Pattern.compile(pattern);
    String link = "Blah blah [link](https://example.com) blah blah";//This will be replaced with your block of text
    link.replaceAll(pattern, "%s");
    Matcher matcher = p.matcher(link);
    String lnk = null;
    if (matcher.find()){
        lnk = matcher.group(1);
    }

    System.out.println(lnk);//Debug
    String formatted = getFormatted(lnk);
    System.out.println(formatted);//Debug
    link = String.format(Locale.ENGLISH, link, formatted);//This amends the formatted link into the location of the [link](in this format)

It uses the method provided at the top of this answer.

It prints out:

[link](https://example.com)//The link extracted from the block
<a href="https://example.com">link</a>//The formatted link
Community
  • 1
  • 1
Zoe
  • 27,060
  • 21
  • 118
  • 148
  • Thanks, what's LinkMovementMethod? – Nick Cardoso Apr 13 '17 at 19:11
  • According to the source(see the link above that code) that is how you would move forward to allow ` – Zoe Apr 13 '17 at 19:12
  • LinkMovementMethod can though be replaced with what you have to make links clickable(assuming you have a method that works) – Zoe Apr 13 '17 at 19:20
  • @NickCardoso added an update that includes altering blocks of text. It is not optimized for multiple links, but works fine with one. It extracts the link and adds the formatted link in its place – Zoe Apr 13 '17 at 19:48
  • I actually didn't need most of your answer - but you pointed me on the right track - simply adding the `LinkMovementMethod` was enough to get it working! – Nick Cardoso Apr 14 '17 at 11:48
  • Good to hear I could be of help – Zoe Apr 14 '17 at 11:49
1

If you have to take into account escaped delimiters, a general regex would be

\[([^\\\]]*(?:\\[\S\s][^\\\]]*)*)\]\(([^\\)]*(?:\\[\S\s][^\\)]*)*)\)

Expanded

 \[
 (                             # (1 start)
      [^\\\]]* 
      (?: \\ [\S\s] [^\\\]]* )*
 )                             # (1 end)
 \]

 \(
 (                             # (2 start)
      [^\\)]* 
      (?: \\ [\S\s] [^\\)]* )*
 )                             # (2 end)
 \)
  • Is white space valid in the second group (url)? I'd love an explanation about what `?:` does. Because the matchers there look to me like "Match whitespace , then a space, then anything that's not a backslash, as many times as you want" (You can tell I'm no regex master) – Nick Cardoso Apr 13 '17 at 19:27
  • @NickCardoso - It's a kind of _general_ regex. That means group 2 will match _anything_ until if finds a valid _unescaped_ closure `)`. If you care about a valid url, you'd have to modify this group a little. Or, just use a url regex inside group 2, but that might be worse than you think. I would hope the target string has a valid url, in which case you could just use what you had for _that_ one, i.e. `\((.*?)\)` I'm not a url syntax master so I don't know. –  Apr 13 '17 at 19:33