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