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 stands with Ukraine
  • 25,310
  • 18
  • 114
  • 149
Nick Cardoso
  • 19,801
  • 12
  • 67
  • 115
  • 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 stands with Ukraine
  • 25,310
  • 18
  • 114
  • 149
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