Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Social Media User Tags in Android

5.00/5 (2 votes)
24 Jun 2018CPOL3 min read 9.1K  
Implement user tagging in your Android application

Introduction

I recently added the capability to tag users in an Android application. If you have ever tagged a user in instagram, you get a list of users as you type in the "@" sign and the list is filtered as you type in characters eventually narrowing down the list of users to choose from. This is the basic behavior I was able to replicate in my own app and I want to share it here on CodeProject.

Background

I won't go into the details of the backend web services, instead, I will focus on the Android implementation. Essentially, I want to be able to invoke a list of users as I type in the "@" symbol. I then want to continue to filter the list as I type in each character of the username I'm tagging. I also want to be able to link the tagged username and show a user profile page if the user taps a tag.

Using the Code

Let's start with the activity layout file. We need an EditText control that the user will use to type in some text. I prefer to use a RealtiveLayout. Below the EditText control, we have a ListView control with the visibility set to "gone" and the width and height set to "match_parent". This is key, as we find a user tag will make the ListView visible and it will fill the screen allowing the user to select a user to tag.

XML
<EditText android:id="@+id/myEditText" android:layout_width="match_parent"	
android:layout_height="wrap_content" android:inputType="textMultiLine" android:minLines="5" 
android:maxLines="5" />
	
<ListView android:id="@+id/myUsers" android:layout_width=&"match_parent"	
android:layout_height="match_parent" android:divider="#E6E6E6" android:dividerHeight="2dip" 
android:paddingLeft="20dip" android:paddingRight="20dip" android:visibility="gone" 
android:background="#ffffff"/>

Next, attach a TextWatcher in the addTextChangedListener for the EditText control. In the TextWatcher class, as the user types in the EditText control, the "afterTextChanged" method will fire passing in our editable parameter from our EditText control. We use a regular expression to find lower and upper case alpha numeric string following the "@" character including the "-" and "." characters as well. We do not allow spaces or other special characters. It's important to only execute on matches starting from the cursor's current position. This will allow multiple tags inside the EditText control. Once we find the current tag, that is where you will want to get the substring excluding the "@" and pass that as a parameter to your backend services that will return a list of users. This is also where you will want to set the visibility of your ListView to VISIBLE. In a Relative layout, placing the ListView at the bottom with "match_parent" as the width and height value will place it over the top of all other controls in the layout. At this point, I'm assuming you have worked with ListViews, Adapters, paging via scroll listeners, etc.

Java
this.mEditText.addTextChangedListener(new TextWatcher()
{
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after)
    {
    }
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count)
    {
    }
    @Override
    public void afterTextChanged(Editable editable)
    {
        String text = editable.toString();
        Pattern p = Pattern.compile("[@][a-zA-Z0-9-.]+");
        Matcher m = p.matcher(text);
        int cursorPosition = mEditText.getSelectionStart();
        while(m.find())
        {
            if (cursorPosition >= m.start() && cursorPosition <= m.end())
            {
                final int s = m.start() + 1; // add 1 to ommit the "@" tag
                final int e = m.end();
	    		loadUsersFromBackEnd(text.substring(s, e);
                break;
            }
        }
    }
}); 

Next, let's cover the reverse. You are looking at a post or comment from another user and we want to find the tags and make them clickable inside a TextView. Once again, we will use the Pattern and Matcher classes to find our tags. The ClickableSpan class is used to make a substring of text clickable. As we find pattern matches for user tags, we create an instance of the clickableSpan class, override the onClick method. In this example, we are creating an Intent that will pass the users friendly name, excluding the "@" character, to our user profile activity. Again, I'm not going to cover Intent's and passing values between activities. This code snippet will most likely be inside an Adapter class that is binding data records to a ListView. As the user scrolls the ListView content, user tags will appear like hyper links. Clicking on them will execute the ClickableSpan onClick method.

Java
Pattern p = Pattern.compile("[@][a-zA-Z0-9-.]+");
Matcher m = p.matcher(ss);

while(m.find())
{
    final int s = m.start() + 1; // add 1 to omit the "@" tag
    final int e = m.end();

    ClickableSpan clickableSpan = new ClickableSpan()
    {
        @Override
        public void onClick(View textView)
        {
            Intent intent = new Intent(mContext, com.myawesomeapp.UserActivity.class);
            intent.putExtra(UserActivity.EXTRA_FRIENDLY_NAME, wallPost.getMessage().substring(s, e));
            mContext.startActivity(intent);
        }
    };

    ss.setSpan(clickableSpan, m.start(), m.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}

Points of Interest

The main things to remember are using the Pattern, Matcher and ClickableSpan classes to find user tags, both as the user submits a new post/reply and as users are reading the text another user posted. Overriding the onClick method in the ClickableSpan allows you to specify an action to take when the user taps a user tag. In my case, navigating to an activity that displays a user's profile. Another trick is to store data as encoded HTML on the backend which allows you to support emojis!

History

  • 24th June, 2018: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)