13

I have two String resources as such:

<string name="give_us_feedback">Give us feedback at %1$s if you want to make the app even better!</string>  
<string name="email">[email protected]</string>

I'd like to style the email part to be blue and underlined to indicate that the user can click on it (the whole TextView, not just the email text). I know to use SpannableString to color text, but it doesn't seem to work when I'm combining two strings via getString(int resId, Object... formatArgs), presumably because getString() will perform a cast or a .toString() on the Object being sent. Here's what doesn't work:

TextView emailTV = new TextView(this);
SpannableString email = new SpannableString(getString(R.string.email));
email.setSpan(new UnderlineSpan(), 0, email.length() - 1, 0);
email.setSpan(new ForegroundColorSpan(Color.BLUE), 0, email.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
String feedback = getString(R.string.give_us_feedback, email);
emailTV.setText(feedback);

Any ideas?

4 Answers 4

12

It's a bit tricky. Converting back to charsequence (String feedback = getString(R.string.give_us_feedback, email);) makes disappear the Spannable. Try this way (you want to check for the correct indexes in your string)

String emailString = getString(R.string.email);
String feedback = getString(R.string.give_us_feedback, emailString);
SpannableString email = new SpannableString(feedback);
int startIndex = feedBack.indexOf(emailString);
int endIndex = startIndex + emailString.length();
email.setSpan(new UnderlineSpan(), startIndex, endIndex, 0);
email.setSpan(new ForegroundColorSpan(Color.BLUE), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
emailTV.setText(email);
Sign up to request clarification or add additional context in comments.

Comments

1

Above answers don't work if your string already contains the same text as the argument.

So this is what I do (in Kotlin). I get the start index of the argument by searching for the literal argument text in the string resource. This is with using a regex.

val text = getString(R.string.id)
val textWithArgs = getString(R.string.id, argument)

// Searches for the start index of %1$s
val startIndex = """%1${"\\$"}s""".toRegex().find(text)?.range?.start
val endIndex = startIndex?.plus(argument.length)

val styledText = if (startIndex == null || endIndex == null) {
    textWithArgs
} else {
    SpannableString(textWithArgs).apply {
        setSpan(
            ForegroundColorSpan(
                ContextCompat.getColor(
                    context,
                    R.color.id
                )
            ), startIndex, endIndex, 0
        )
    }
}

Comments

0

I've wrote a method to handle it.

isSearchForward is a parameter to toggle whether to search the string from forward or backward, as this only highlights the first occurance.

private fun highlightKeywords(
    highlightColor: Int,
    message: String,
    keyword: String?,
    isSearchForward: Boolean? = true
): SpannableString {
    val spannableString = SpannableString(message)
    if (!keyword.isNullOrBlank()) {
        val startIndex = if (isSearchForward == true) {
            message.indexOf(keyword)
        } else {
            message.lastIndexOf(keyword)
        }
        val endIndex = startIndex + keyword.length

        spannableString.setSpan(UnderlineSpan(), startIndex, endIndex, 0)

        spannableString.setSpan(
            ForegroundColorSpan(highlightColor),
            startIndex,
            endIndex,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
    }
    return spannableString
}

Comments

0

You can use my answer from here, so your code would be like:

val email:SpannableString 
... - prepare the email variable
val feedback = SpanFormatter.format(getString(R.string.give_us_feedbac),email)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.