Home > Software engineering >  Make link inside of sting clickable: Java
Make link inside of sting clickable: Java

Time:07-16

I am creating a messaging system in Java using android studio.

People can send messages back and forth. But if they send a link, it just shows up as regular text. I want the part that is the link to show up as a clickable link and the rest just text.

I checked all day on this site and others but no seems to do this in the way I'm trying too. Most of the answers I see are people using a TexView to accomplish their goal. I'm using a string. Can someone please help me figure this out ?

private void showMessages(){

    DatabaseReference userMessageKeyRef = RootRef.child("Messages").child(messageSenderID).child(messageReceiverID);
    userMessageKeyRef.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            for (DataSnapshot snapshot1 : snapshot.getChildren()) {

                 Messages messages = new Messages();
                 String strMessage = snapshot1.child("message").getValue().toString();
                 String strFrom = snapshot1.child("from").getValue().toString();
                 String strType = snapshot1.child("type").getValue().toString();
                 messages.setMessage(strMessage);
                 messages.setFrom(strFrom);
                 messages.setType(strType);
                 messagesList.add(messages);

                // Pattern for recognizing a URL, based off RFC 3986
                final Pattern urlPattern = Pattern.compile(
                        "(?:^|[\\W])((ht|f)tp(s?):\\/\\/|www\\.)"
                                  "(([\\w\\-] \\.){1,}?([\\w\\-.~] \\/?)*"
                                  "[\\p{Alnum}.,%_=?&#\\- ()\\[\\]\\*$~@!:/{};']*)",
                        Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);

                // separate input by spaces ( URLs don't have spaces )
                String [] parts = strMessage.split("\\s ");

                // get every part
                for( String item : parts ) {
                    if(urlPattern.matcher(item).matches()) {
                        //it's a good url
                        System.out.print("<a href=\""   item   "\">"  item   "</a> " );
                    } else {
                        // it isn't a url
                        System.out.print(item   " ");
                    }
                }
                
            }
            messageAdapter = new MessageAdapter(ChatActivity.this,messagesList);
            userMessagesList.setAdapter(messageAdapter);
        }

        @Override
        public void onCancelled(@NonNull DatabaseError error) {
        }
    });
}

CodePudding user response:

There are two common ways to do this. One, like you have done, is to add html to the string. The second is to use the TextView's auto link mask feature.

Using HTML

Once you have identified URLs in your incoming string and added the appropriate html tags to turn them into links, you just need to use HtmlCompat when you go to actually display it in the TextView. You also need to make sure to call either setMovementMethod or you won't be able to click the link. The advantage of using HTML is that you can have the link text be a readable phrase instead of a URL.

String txt = "This is <a href=\"https://www.google.com\">www.google.com</a>";

TextView link = findViewById(R.id.link);
link.setMovementMethod(LinkMovementMethod.getInstance());
link.setText(HtmlCompat.fromHtml(txt,HtmlCompat.FROM_HTML_MODE_LEGACY));

If you choose to go this route, your existing code just needs to be modified a bit to save the HTML string in the messages list passed to the adapter, then add the TextView calls above inside the adapter when you set the text.

String [] parts = strMessage.split("\\s ");

// replace URL parts with html links
for( int i = 0; i < parts.length;   i ) {
    if(urlPattern.matcher(parts[i]).matches()) {
        parts[i] = "<a href=\""   parts[i]   "\">"   parts[i]   "</a>";
    }
}

// re-join parts back into a single string
String htmlMessage = String.join(" ", parts);

// save a list of html strings to pass to your adapter
htmlMessageStrings.add(htmlMessage);

Using Link Mask

This method doesn't require you to edit the string at all. If you use Linkify.ALL it also recognizes things like web links, emails, phone numbers, and physical addresses - not just web links. If you only want it to recognize web links use Linkify.WEB_URLS instead. This requires a lot less code on your part - you no longer have to try to parse the string for links.

String txt = "This is www.google.com"; // no need to modify the string

TextView link = findViewById(R.id.link);
link.setAutoLinkMask(Linkify.ALL); // or Linkify.WEB_URLS
link.setText(txt);

You can also add android:autoLink="all" to the TextView XML definition instead of calling it in-code.

Both methods produce this output

link output

  • Related