Different font size of strings in the same TextView - android

I have a textView inside with a number (variable) and a string, how can I give the number one size larger than the string?
the code:
TextView size = (TextView)convertView.findViewById(R.id.privarea_list_size);
if (ls.numProducts != null) {
size.setText(ls.numProducts + " " + mContext.getString(R.string.products));
}
I want ls.numproducts has a size different from the rest of the text. How to do?

Use a Spannable String
String s= "Hello Everyone";
SpannableString ss1= new SpannableString(s);
ss1.setSpan(new RelativeSizeSpan(2f), 0,5, 0); // set size
ss1.setSpan(new ForegroundColorSpan(Color.RED), 0, 5, 0);// set color
TextView tv= (TextView) findViewById(R.id.textview);
tv.setText(ss1);
Snap shot
You can split string using space and add span to the string you require.
String s= "Hello Everyone";
String[] each = s.split(" ");
Now apply span to the string and add the same to textview.

Just in case you're wondering how you can set multiple different sizes in the same textview, but using an absolute size and not a relative one, you can achieve that using AbsoluteSizeSpan instead of a RelativeSizeSpan.
Just get the dimension in pixels of the desired text size
int textSize1 = getResources().getDimensionPixelSize(R.dimen.text_size_1);
int textSize2 = getResources().getDimensionPixelSize(R.dimen.text_size_2);
and then create a new AbsoluteSpan based on the text
String text1 = "Hi";
String text2 = "there";
SpannableString span1 = new SpannableString(text1);
span1.setSpan(new AbsoluteSizeSpan(textSize1), 0, text1.length(), SPAN_INCLUSIVE_INCLUSIVE);
SpannableString span2 = new SpannableString(text2);
span2.setSpan(new AbsoluteSizeSpan(textSize2), 0, text2.length(), SPAN_INCLUSIVE_INCLUSIVE);
// let's put both spans together with a separator and all
CharSequence finalText = TextUtils.concat(span1, " ", span2);

You can get this done using html string and setting the html to Textview using
txtView.setText(Html.fromHtml("Your html string here"));
For example :
txtView.setText(Html.fromHtml("<html><body><font size=5 color=red>Hello </font> World </body><html>"));`

Method 1
public static void increaseFontSizeForPath(Spannable spannable, String path, float increaseTime) {
int startIndexOfPath = spannable.toString().indexOf(path);
spannable.setSpan(new RelativeSizeSpan(increaseTime), startIndexOfPath,
startIndexOfPath + path.length(), 0);
}
using
Utils.increaseFontSizeForPath(spannable, "big", 3); // make "big" text bigger 3 time than normal text
Method 2
public static void setFontSizeForPath(Spannable spannable, String path, int fontSizeInPixel) {
int startIndexOfPath = spannable.toString().indexOf(path);
spannable.setSpan(new AbsoluteSizeSpan(fontSizeInPixel), startIndexOfPath,
startIndexOfPath + path.length(), 0);
}
using
Utils.setFontSizeForPath(spannable, "big", (int) textView.getTextSize() + 20); // make "big" text bigger 20px than normal text

private SpannableStringBuilder SpannableStringBuilder(final String text, final char afterChar, final float reduceBy) {
RelativeSizeSpan smallSizeText = new RelativeSizeSpan(reduceBy);
SpannableStringBuilder ssBuilder = new SpannableStringBuilder(text);
ssBuilder.setSpan(
smallSizeText,
text.indexOf(afterChar),
text.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
return ssBuilder;
}
------------------------
TextView textView =view.findViewById(R.id.textview);
String s= "123456.24";
textView.setText(SpannableStringBuilder(s, '.', 0.7f));
---------------- Result ---------------
Result : 12345.24

The best way to do that is Html without substring your text and fully dynamique
For example :
public static String getTextSize(String text,int size) {
return "<span style=\"size:"+size+"\" >"+text+"</span>";
}
and you can use color attribut etc...
if the other hand :
size.setText(Html.fromHtml(getTextSize(ls.numProducts,100) + " " + mContext.getString(R.string.products));

Try spannableStringbuilder. Using this we can create string with multiple font sizes.

I have written my own function which takes 2 strings and 1 int (text size)
The full text and the part of the text you want to change the size of it.
It returns a SpannableStringBuilder which you can use it in text view.
public static SpannableStringBuilder setSectionOfTextSize(String text, String textToChangeSize, int size){
SpannableStringBuilder builder=new SpannableStringBuilder();
if(textToChangeSize.length() > 0 && !textToChangeSize.trim().equals("")){
//for counting start/end indexes
String testText = text.toLowerCase(Locale.US);
String testTextToBold = textToChangeSize.toLowerCase(Locale.US);
int startingIndex = testText.indexOf(testTextToBold);
int endingIndex = startingIndex + testTextToBold.length();
//for counting start/end indexes
if(startingIndex < 0 || endingIndex <0){
return builder.append(text);
}
else if(startingIndex >= 0 && endingIndex >=0){
builder.append(text);
builder.setSpan(new AbsoluteSizeSpan(size, true), startingIndex, endingIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}else{
return builder.append(text);
}
return builder;
}

Utils method for kotlin lover
fun String.setFontSizeForPath(path: String, fontSizeInPixel: Int, colorCode: String = "#FF0000"): SpannableString {
val spannable = SpannableString(this)
val startIndexOfPath = spannable.toString().indexOf(path)
spannable.setSpan(
AbsoluteSizeSpan(fontSizeInPixel),
startIndexOfPath,
startIndexOfPath + path.length,
0
)
spannable.setSpan(
ForegroundColorSpan(Color.parseColor(colorCode)),
startIndexOfPath,
startIndexOfPath + path.length,
0
)
return spannable
}
Use
text_view.text = "A B C D".setFontSizeForPath("B",30)

private SpannableString getFormattedText(String textToBold, String normalText, boolean boldTextFirst) {
String result = boldTextFirst ? textToBold + normalText : normalText + textToBold;
SpannableString str = new SpannableString(result);
Typeface typeface = ResourcesCompat.getFont(requireContext(), R.font.product_sans_bold);
int style = typeface != null ? typeface.getStyle() : Typeface.BOLD;
int textToBoldStartIndex = str.toString().indexOf(textToBold);
str.setSpan(new StyleSpan(style), textToBoldStartIndex, textToBoldStartIndex + textToBold.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
int normalTextStartIndex = str.toString().indexOf(normalText);
str.setSpan(new AbsoluteSizeSpan(13, true), normalTextStartIndex, normalTextStartIndex + normalText.length(), 0);
return str;
}

In case you want to avoid too much confusion for your translators, I've come up with a way to have just a placeholder in the strings, which will be handled in code.
So, supposed you have this in the strings:
<string name="test">
<![CDATA[
We found %1$s items]]>
</string>
And you want the placeholder text to have a different size and color, you can use this:
val textToPutAsPlaceHolder = "123"
val formattedStr = getString(R.string.test, "$textToPutAsPlaceHolder<bc/>")
val placeHolderTextSize = resources.getDimensionPixelSize(R.dimen.some_text_size)
val placeHolderTextColor = ContextCompat.getColor(this, R.color.design_default_color_primary_dark)
val textToShow = HtmlCompat.fromHtml(formattedStr, HtmlCompat.FROM_HTML_MODE_LEGACY, null, object : Html.TagHandler {
var start = 0
override fun handleTag(opening: Boolean, tag: String, output: Editable, xmlReader: XMLReader) {
when (tag) {
"bc" -> if (!opening) start = output.length - textToPutAsPlaceHolder.length
"html" -> if (!opening) {
output.setSpan(AbsoluteSizeSpan(placeHolderTextSize), start, start + textToPutAsPlaceHolder.length, 0)
output.setSpan(ForegroundColorSpan(placeHolderTextColor), start, start + textToPutAsPlaceHolder.length, 0)
}
}
}
})
textView.text = textToShow
And the result:

Kotlin version on #Anandharaj R answer.
class SpannableStringBuilderHelper {
companion object {
fun reduceTextSizeAfterChar(text: String, afterChar: Char, reduceBy: Float): SpannableStringBuilder? {
val smallSizeText = RelativeSizeSpan(reduceBy)
val ssBuilder = SpannableStringBuilder(text)
ssBuilder.setSpan(
smallSizeText,
text.indexOf(afterChar),
text.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
return ssBuilder
}
}
}
Usage:
SpannableStringBuilderHelper.reduceTextSizeAfterChar("70/100", '/', 0.7f)

in kotlin do it as below by using html
HtmlCompat.fromHtml("<html><body><h1>This is Large Heading :-</h1><br>This is normal size<body></html>",HtmlCompat.FROM_HTML_MODE_LEGACY)

Related

How to remove current typeface for some lines only [duplicate]

I don't know how to make a specific text on TextView become BOLD.
its like this
txtResult.setText(id+" "+name);
I want the output to be like this:
1111 neil
id and name are variables that I have retrieved the value from database, and I want to make the id to bold, but only the id so the name will not affected, I have no idea how to do this.
Just build your String in HTML and set it:
String sourceString = "<b>" + id + "</b> " + name;
mytextview.setText(Html.fromHtml(sourceString));
While you can use Html.fromHtml() you can use a more native approach which is SpannableStringBuilder , this post may be helful.
SpannableStringBuilder str = new SpannableStringBuilder("Your awesome text");
str.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), INT_START, INT_END, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView tv=new TextView(context);
tv.setText(str);
First: You don't need to worry about using the slow performance code from the Raghav Sood's answer.
Second: You don't need to write an extension function provided by w3bshark's answer when using Kotlin.
Finnaly: All you need to do is to use the Kotlin android-ktx library from Google (refer here to find more information and how to include it on your project):
// Suppose id = 1111 and name = neil (just what you want).
val s = SpannableStringBuilder()
.bold { append(id) }
.append(name)
txtResult.setText(s)
Produces: 1111 neil
UPDATE:
Because I think it can help someone else as well as to demonstrate how far you can go here are more use cases.
When you need to display a text with some parts in blue and italic:
val myCustomizedString = SpannableStringBuilder()
.color(blueColor, { append("A blue text ") })
.append("showing that ")
.italic{ append("it is painless") }
When you need to display a text in both bold and italic:
bold { italic { append("Bold and italic") } }
In short, bold, append, color and italic are extension functions to SpannableStringBuilder. You can see another extension functions in the official documentation, from where you can think for other possibilities.
I thought that the chosen answer didn't provide a satisfactory result. I have written my own function which takes 2 strings; The full text and the part of the text you want to make bold.
It returns a SpannableStringBuilder with the 'textToBold' from 'text' bolded.
I find the ability to make a substring bold without wrapping it in tags useful.
/**
* Makes a substring of a string bold.
* #param text Full text
* #param textToBold Text you want to make bold
* #return String with bold substring
*/
public static SpannableStringBuilder makeSectionOfTextBold(String text, String textToBold){
SpannableStringBuilder builder=new SpannableStringBuilder();
if(textToBold.length() > 0 && !textToBold.trim().equals("")){
//for counting start/end indexes
String testText = text.toLowerCase(Locale.US);
String testTextToBold = textToBold.toLowerCase(Locale.US);
int startingIndex = testText.indexOf(testTextToBold);
int endingIndex = startingIndex + testTextToBold.length();
//for counting start/end indexes
if(startingIndex < 0 || endingIndex <0){
return builder.append(text);
}
else if(startingIndex >= 0 && endingIndex >=0){
builder.append(text);
builder.setSpan(new StyleSpan(Typeface.BOLD), startingIndex, endingIndex, 0);
}
}else{
return builder.append(text);
}
return builder;
}
As wtsang02 said, using HTML is an expensive overhead. Just use the native solution. If you don't have to modify the string, just use SpannableString, not SpannableStringBuilder.
String boldText = "id";
String normalText = "name";
SpannableString str = new SpannableString(boldText + normalText);
str.setSpan(new StyleSpan(Typeface.BOLD), 0, boldText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(str);
In case you want to use the string from XML, you can do something like this:
strings.xml (the "CDATA" part is important, otherwise it won't work)
<string name="test">
<![CDATA[
<b>bold!</b> normal
]]>
</string>
layout file
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">
<TextView
android:id="#+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
code
textView.text = HtmlCompat.fromHtml(getString(R.string.test), HtmlCompat.FROM_HTML_MODE_LEGACY)
Its simple just close the specified text like this for example <b>"your text here:"</b>
<string name="headquarters">"<b>"Headquarters:"</b>" Mooresville, North Carolina, U.S.</string>
result:
Headquarters: Mooresville, North Carolina, U.S.
If you are using Kotlin, it becomes even easier to do by using core-ktx, as it provides a domain-specific-language (DSL) for doing this:
val string: SpannedString = buildSpannedString {
bold {
append("foo")
}
append("bar")
}
More options provided by it are:
append("Hello There")
bold {
append("bold")
italic {
append("bold and italic")
underline {
append("then some text with underline")
}
}
}
At last, you can just to:
textView.text = string
Based on #mladj0ni's answer, I got the code below to work. The problem was that if you use String.format, it strips out the html markup, so you have to escape the bracket symbols in strings.xml:
strings.xml:
<string name="welcome_messages">Hello, %1$s! You have <b>%2$d new messages</b>.</string>
code.java:
String unspanned = String.format(Locale.US, "%s%s", getResources().getString(R.string. welcome_messages), 99);
Spanned spanned;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
spanned = Html.fromHtml(unspanned, Html.FROM_HTML_MODE_LEGACY);
} else {
spanned = Html.fromHtml(unspanned);
}
textView.setText(spanned);
It's simpler than SpannableStringBuilder. As for performance, if you're displaying just one string, then the user won't notice the extra millisecond to parse it.
See the documentation here.
Here is better solution if you want to make multiple text to bold. I've improved Eitan's code. thanks Eitan.
public static SpannableStringBuilder makeSectionOfTextBold(String text, String... textToBold) {
SpannableStringBuilder builder = new SpannableStringBuilder(text);
for (String textItem :
textToBold) {
if (textItem.length() > 0 && !textItem.trim().equals("")) {
//for counting start/end indexes
String testText = text.toLowerCase(Locale.US);
String testTextToBold = textItem.toLowerCase(Locale.US);
int startingIndex = testText.indexOf(testTextToBold);
int endingIndex = startingIndex + testTextToBold.length();
if (startingIndex >= 0 && endingIndex >= 0) {
builder.setSpan(new StyleSpan(Typeface.BOLD), startingIndex, endingIndex, 0);
}
}
}
return builder;
}
You can use this code to set part of your text to bold. For whatever is in between the bold html tags, it will make it bold.
String myText = "make this <b>bold</b> and <b>this</b> too";
textView.setText(makeSpannable(myText, "<b>(.+?)</b>", "<b>", "</b>"));
public SpannableStringBuilder makeSpannable(String text, String regex, String startTag, String endTag) {
StringBuffer sb = new StringBuffer();
SpannableStringBuilder spannable = new SpannableStringBuilder();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
sb.setLength(0);
String group = matcher.group();
String spanText = group.substring(startTag.length(), group.length() - endTag.length());
matcher.appendReplacement(sb, spanText);
spannable.append(sb.toString());
int start = spannable.length() - spanText.length();
spannable.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), start, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
sb.setLength(0);
matcher.appendTail(sb);
spannable.append(sb.toString());
return spannable;
}
wtsang02 answer is the best way to go about it, since, Html.fromHtml("") is now deprecated. Here I'm just going to enhance it a little bit for whoever is having problem in dynamically making the first word bold, no matter whats the size of the sentence.
First lets create a method to get the first word:
private String getFirstWord(String input){
for(int i = 0; i < input.length(); i++){
if(input.charAt(i) == ' '){
return input.substring(0, i);
}
}
return input;
}
Now let's say you have a long string like this:
String sentence = "friendsAwesomeName#gmail.com want's to be your friend!"
And you want your sentence to be like yourAwesomeName#gmail.com want's to be your friend!
All you have to do is- get the firstWord and get the lenght of it to make the firstWord bold, something like this:
String myFirstWord = getFirstWord(sentence);
int start = 0; // bold will start at index 0
int end = myFirstWord.length(); // and will finish at whatever the length of your first word
Now just follow wtsang02 's steps, like this:
SpannableStringBuilder fancySentence = new SpannableStringBuilder(sentence);
fancySentence.setSpan(new android.text.style.StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(fancySentence);
And that's it! Now you should be able to bold a word with any size from long/short sentence.
I came here to provide a more up-to-date solution, because I wasn't satisfied with the existing answers.
I needed something that would work for translated texts and does not have the performance hit of using Html.fromHtml().
If you're using Kotlin, here is an extension function which will easily set multiple parts of your text to bold. This works just like Markdown, and could be extended to support other Markdown tags, if need be.
val yourString = "**This** is your **string**.".makePartialTextsBold()
val anotherString = getString(R.string.something).makePartialTextsBold()
/**
* This function requires that the parts of the string that need
* to be bolded are wrapped in ** and ** tags
*/
fun String.makePartialTextsBold(): SpannableStringBuilder {
var copy = this
return SpannableStringBuilder().apply {
var setSpan = true
var next: String
do {
setSpan = !setSpan
next = if (length == 0) copy.substringBefore("**", "") else copy.substringBefore("**")
val start = length
append(next)
if (setSpan) {
setSpan(StyleSpan(Typeface.BOLD), start, length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
copy = copy.removePrefix(next).removePrefix("**")
} while (copy.isNotEmpty())
}
}
Here is my complete solution for dynamic String values with case check.
/**
* Makes a portion of String formatted in BOLD.
*
* #param completeString String from which a portion needs to be extracted and formatted.<br> eg. I am BOLD.
* #param targetStringToFormat Target String value to format. <br>eg. BOLD
* #param matchCase Match by target character case or not. If true, BOLD != bold
* #return A string with a portion formatted in BOLD. <br> I am <b>BOLD</b>.
*/
public static SpannableStringBuilder formatAStringPortionInBold(String completeString, String targetStringToFormat, boolean matchCase) {
//Null complete string return empty
if (TextUtils.isEmpty(completeString)) {
return new SpannableStringBuilder("");
}
SpannableStringBuilder str = new SpannableStringBuilder(completeString);
int start_index = 0;
//if matchCase is true, match exact string
if (matchCase) {
if (TextUtils.isEmpty(targetStringToFormat) || !completeString.contains(targetStringToFormat)) {
return str;
}
start_index = str.toString().indexOf(targetStringToFormat);
} else {
//else find in lower cases
if (TextUtils.isEmpty(targetStringToFormat) || !completeString.toLowerCase().contains(targetStringToFormat.toLowerCase())) {
return str;
}
start_index = str.toString().toLowerCase().indexOf(targetStringToFormat.toLowerCase());
}
int end_index = start_index + targetStringToFormat.length();
str.setSpan(new StyleSpan(BOLD), start_index, end_index, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return str;
}
Eg. completeString = "I am BOLD"
CASE I
if *targetStringToFormat* = "bold" and *matchCase* = true
returns "I am BOLD" (since bold != BOLD)
CASE II
if *targetStringToFormat* = "bold" and *matchCase* = false
returns "I am BOLD"
To Apply:
myTextView.setText(formatAStringPortionInBold("I am BOLD", "bold", false))
Hope that helps!
I used this code to bold specific words...
Spanned string = Html.fromHtml("Normal string <b>BOLD STRING</b>");
textView.setText(string);
Isn't this code the easiest solution?
<string name="string">Please provide your <b>Name</b> properly</string>
Just use this string whatever you want :)
The result will like this:
Please provide your Name properly
public static Spanned getBoldString(String textNotBoldFirst, String textToBold, String textNotBoldLast) {
String resultant = null;
resultant = textNotBoldFirst + " " + "<b>" + textToBold + "</b>" + " " + textNotBoldLast;
return Html.fromHtml(resultant);
}
Try this. It can help definitely
Make first char of string spannable while searching for char in list/recycler like
ravi and ajay
previously highlighting like this but i wanted to be like below
ravi and ajay OR ravi and ajay
for this I searched for word length if it is equal to 1 ,I separated main string into words and calculated word start position then I searched word starting with char.
public static SpannableString colorString(int color, String text, String... wordsToColor) {
SpannableString coloredString = new SpannableString(text);
for (String word : wordsToColor) {
Log.e("tokentoken", "-wrd len-" + word.length());
if (word.length() !=1) {
int startColorIndex = text.toLowerCase().indexOf(word.toLowerCase());
int endColorIndex = startColorIndex + word.length();
try {
coloredString.setSpan(new ForegroundColorSpan(color), startColorIndex, endColorIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.getMessage();
}
} else {
int start = 0;
for (String token : text.split("[\u00A0 \n]")) {
if (token.length() > 0) {
start = text.indexOf(token, start);
// Log.e("tokentoken", "-token-" + token + " --start--" + start);
char x = token.toLowerCase().charAt(0);
char w = word.toLowerCase().charAt(0);
// Log.e("tokentoken", "-w-" + w + " --x--" + x);
if (x == w) {
// int startColorIndex = text.toLowerCase().indexOf(word.toLowerCase());
int endColorIndex = start + word.length();
try {
coloredString.setSpan(new ForegroundColorSpan(color), start, endColorIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.getMessage();
}
}
}
}
}
}
return coloredString;
}
You can add the two strings separately in the builder, one of them is spannedString, the other is a regular one.This way you don`t have to calculate the indexes.
val instructionPress = resources?.getString(R.string.settings_press)
val okText = resources?.getString(R.string.ok)
val spannableString = SpannableString(okText)
val spannableBuilder = SpannableStringBuilder()
spannableBuilder.append(instructionPress)
spannableBuilder.append(spannableString, StyleSpan(Typeface.BOLD), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
instructionText.setText(spannableBuilder,TextView.BufferType.SPANNABLE)
This is the Kotlin extension function I use for this
/**
* Sets the specified Typeface Style on the first instance of the specified substring(s)
* #param one or more [Pair] of [String] and [Typeface] style (e.g. BOLD, ITALIC, etc.)
*/
fun TextView.setSubstringTypeface(vararg textsToStyle: Pair<String, Int>) {
val spannableString = SpannableString(this.text)
for (textToStyle in textsToStyle) {
val startIndex = this.text.toString().indexOf(textToStyle.first)
val endIndex = startIndex + textToStyle.first.length
if (startIndex >= 0) {
spannableString.setSpan(
StyleSpan(textToStyle.second),
startIndex,
endIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
this.setText(spannableString, TextView.BufferType.SPANNABLE)
}
Usage:
text_view.text="something bold"
text_view.setSubstringTypeface(
Pair(
"something bold",
Typeface.BOLD
)
)
.
text_view.text="something bold something italic"
text_view.setSubstringTypeface(
Pair(
"something bold ",
Typeface.BOLD
),
Pair(
"something italic",
Typeface.ITALIC
)
)
if the position of bold text is fixed(ex: if is at start of the textView), then use two different textView with same background. Then you can make the other textView's textStyle as bold.
This will require twice the memory compared to a single textView but speed will increase.
Found a way in case you want to handle localization in multiple languages, it's boring to do but it works, let's suppose we want this:
In English:
There are no payments registered
In Spanish:
No hay pagos registrados
You have to create 3 strings
English:
<string name="start_string">There are no</string>
<string name="middle_string">payments</string>
<string name="end_string">registered.</string>
<string name="string_format" translatable="false">%1$s %2$s %3$s</string>
Spanish:
<string name="start_string">No hay</string>
<string name="middle_string">pagos</string>
<string name="end_string">registrados</string>
Now you can do this:
val startSpanPosition = getString(R.string.start_string).length
val endSpanPosition = startSpanPosition + getString(R.string.middle_string).length
val mySpannableString = SpannableStringBuilder(String.format(getString(R.string.string_format),
getString(R.string.start_string), getString(R.string.middle_string))), getString(R.string.end_string)))
mySpannableString.setSpan(StyleSpan(Typeface.BOLD), spanStartPosition, endSpanPosition, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
Your String resource
<resources>
<string name="your_string_resource_name">This is normal text<![CDATA[<b> but this is bold </b>]]> and <![CDATA[<u> but this is underline text</u>]]></string>
</resources>
your java class
yourtextView.setText(getString(R.string.your_string_resource_name));
I have created a static method for setting part of text Bold for TextView and EditText
public static void boldPartOfText(View mView, String contentData, int startIndex, int endIndex){
if(!contentData.isEmpty() && contentData.length() > endIndex) {
final SpannableStringBuilder sb = new SpannableStringBuilder(contentData);
final StyleSpan bss = new StyleSpan(Typeface.BOLD); // Span to make text bold
final StyleSpan iss = new StyleSpan(Typeface.NORMAL); //Span to make text normal
sb.setSpan(iss, 0, startIndex, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
sb.setSpan(bss, startIndex, endIndex, Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
sb.setSpan(iss,endIndex, contentData.length()-1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
if(mView instanceof TextView)
((TextView) mView).setText(sb);
else if(mView instanceof EditText)
((EditText) mView).setText(sb);
}
}
Another more customized code
/*typeFaceStyle can be passed as
Typeface.NORMAL = 0;
Typeface.BOLD = 1;
Typeface.ITALIC = 2;
Typeface.BOLD_ITALIC = 3;*/
public static void boldPartOfText(View mView, String contentData, int startIndex, int endIndex,int typeFaceStyle){
if(!contentData.isEmpty() && contentData.length() > endIndex) {
final SpannableStringBuilder sb = new SpannableStringBuilder(contentData);
final StyleSpan bss = new StyleSpan(typeFaceStyle); // Span to make text bold
final StyleSpan iss = new StyleSpan(Typeface.NORMAL); //Span to make text italic
sb.setSpan(iss, 0, startIndex, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
sb.setSpan(bss, startIndex, endIndex, Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
sb.setSpan(iss,endIndex,contentData.length()-1,Spanned.SPAN_INCLUSIVE_INCLUSIVE);
if(mView instanceof TextView)
((TextView) mView).setText(sb);
else if(mView instanceof EditText)
((EditText) mView).setText(sb);
}
}
In case someone is using Data Binding. We can define binding adapter like this
#BindingAdapter("html")
fun setHtml(view: TextView, html: String) {
view.setText(HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY))
}
Then we can use it on a TextView
app:html="#{#string/bold_text}"
where bold_text is
<string name="bold_text"><![CDATA[Part of text is <b>bold</b>]]></string>
Simple Example
In you strings.xml
<string name="str_privacy_policy">This is our Privacy Policy.</string>
if you want to make specifically "Privacy Policy" as bold put the string between the bold tags.
Like this
<string name="str_privacy_policy">This is our <b>Privacy Policy.</b></string>
Result would be
This is our Privacy Policy
Here's how I do it using regular expressions and Kotlin
val BOLD_SPAN = StyleSpan(Typeface.BOLD)
fun TextView.boldMatches(regexString: String) {
this.applyStyleSpanToMatches(regexString, BOLD_SPAN)
}
fun TextView.applyStyleSpanToMatches(regexString: String, span: StyleSpan){
this.text = this.text.toString().applyStyleSpanToMatches(regexString, span)
}
fun String.applyStyleSpanToMatches(regexString: String, span: StyleSpan): Spannable {
val result = SpannableString.valueOf(this)
if(regexString.isEmpty()) return result
val pattern = try{
Pattern.compile(regexString)
} catch (e: PatternSyntaxException){
return result
}
val matcher = pattern.matcher(result)
while (matcher.find()) {
val start = matcher.start()
val end = matcher.end()
result.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
return result
}
using the question
it can be applied like this:
txtResult.boldMatches(id)
If you are using Kotlin and string resources, a simple solution is:
Create your string on strings.xml, using <b> </b> to bold the parts you want
<string name="my_message"> This is a very <b>important</b> message! </string>
On Kotlin code you must do like so
textView.setText(R.string.my_message)
And that is it!
Important note!
Using property syntax will not work:
textView.text = resources.getString(R.string.my_message)
Hope it helps!
val phone = "+45xxxxxx"
val phoneText = "<font color=#757B7F><b>${phone}</b></font>"
val wholeString = requireActivity().resources.getString(R.string.loginwith)+" "+phoneText
Just add this in your tag
dangerouslySetInnerHTML={{__html: "<p>Your html text here.<p>"}}

Android - Find a substring and make it bold

What would be the correct way to find str1 as a substring of str2, make it bold (in str2, that is), and then display it as a textview?
Use str2 as SpannableString and use setSpan() method to find substring str1 and make it BOLD.
See documentation.
Try this:
TextView textView = (TextView) findViewById(R.id.textView);
String str1 = "substring";
String str2 = "Find a substring and make it bold";
if (str2.contains(str1)) {
SpannableString spannable = new SpannableString(str2);
spannable.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
str2.indexOf(str1),
str2.indexOf(str1) + str1.length(),
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(spannable);
} else {
textView.setText(str2);
}
OUTPUT:
Hope this will help~
you can use Spannable String for that
if(str2.contain(str1){
SpannableStringBuilder sb = new SpannableStringBuilder(str2);
// Span to make text bold
StyleSpan bss = new StyleSpan(android.graphics.Typeface.BOLD);
// make them also bold
sb.setSpan(bss, str2.indexOf(str1), str1.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(sb);
}else{
textView.setText(str2);
}
String str1 = "hello world, how are you?";
String str2 = "world";
//check if str1 contains str2
if (!str1.contains(str2)) {
return;
}
//find index of str2
int startIndex = str1.indexOf(str2);
int endIndex = startIndex + str2.length();
//create spannable
SpannableString spannable = new SpannableString(str2;
spannable.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), startIndex, endIndex, 0);
//set it to textview
textView.setText(spannable);
Use indexOf to find sub1 and Html.fromHtml to format string
int start = str2.indexOf(str1);
String destStr = str2.substring(start)+"<b>" + str2.substring(start, str1.length()) + "</b> " + str2.substring(start+str1.length());
textview.setText(Html.fromHtml(destStr));
With the help of the people who commented here, I created a function that also ignores case - to use, textView.setText(boldifySubstring(str2, str1)); -
public static Spanned boldifySubstring(String string, String substring) {
//Create spannable string
SpannableString spannable = new SpannableString(string);
//Check if substring exists (ignore case)
if (string.toLowerCase().contains(substring.toLowerCase())) {
//Get start + end indexes (ignore case)
int start = string.toLowerCase().indexOf(substring.toLowerCase());
int end = start + substring.length();
//Make relevant parts bold
spannable.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
return spannable;
}
Here is some Kotlin code which will find all occurrences of a string in a CharSequence and apply specified styles (Spans) to the found occurrences.
"Hello World? Hello World!".applySpansToSubString("world", StyleSpan(Typeface.BOLD))
will return "Hello World? Hello World!"
fun CharSequence.applySpansToSubString(searchString: String, vararg spans: CharacterStyle): CharSequence {
if(searchString.isEmpty()) { return this }
val sb = SpannableString(this)
var startIndex = 0
while(startIndex in 0 until length) {
startIndex = indexOf(searchString, startIndex, true)
if (startIndex >= 0) {
val endIndex = startIndex + searchString.length
for (span in spans) {
sb.setSpan(CharacterStyle.wrap(span), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
startIndex = endIndex
}
}
return sb
}

how to make a specific text on TextView BOLD

I don't know how to make a specific text on TextView become BOLD.
its like this
txtResult.setText(id+" "+name);
I want the output to be like this:
1111 neil
id and name are variables that I have retrieved the value from database, and I want to make the id to bold, but only the id so the name will not affected, I have no idea how to do this.
Just build your String in HTML and set it:
String sourceString = "<b>" + id + "</b> " + name;
mytextview.setText(Html.fromHtml(sourceString));
While you can use Html.fromHtml() you can use a more native approach which is SpannableStringBuilder , this post may be helful.
SpannableStringBuilder str = new SpannableStringBuilder("Your awesome text");
str.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), INT_START, INT_END, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView tv=new TextView(context);
tv.setText(str);
First: You don't need to worry about using the slow performance code from the Raghav Sood's answer.
Second: You don't need to write an extension function provided by w3bshark's answer when using Kotlin.
Finnaly: All you need to do is to use the Kotlin android-ktx library from Google (refer here to find more information and how to include it on your project):
// Suppose id = 1111 and name = neil (just what you want).
val s = SpannableStringBuilder()
.bold { append(id) }
.append(name)
txtResult.setText(s)
Produces: 1111 neil
UPDATE:
Because I think it can help someone else as well as to demonstrate how far you can go here are more use cases.
When you need to display a text with some parts in blue and italic:
val myCustomizedString = SpannableStringBuilder()
.color(blueColor, { append("A blue text ") })
.append("showing that ")
.italic{ append("it is painless") }
When you need to display a text in both bold and italic:
bold { italic { append("Bold and italic") } }
In short, bold, append, color and italic are extension functions to SpannableStringBuilder. You can see another extension functions in the official documentation, from where you can think for other possibilities.
I thought that the chosen answer didn't provide a satisfactory result. I have written my own function which takes 2 strings; The full text and the part of the text you want to make bold.
It returns a SpannableStringBuilder with the 'textToBold' from 'text' bolded.
I find the ability to make a substring bold without wrapping it in tags useful.
/**
* Makes a substring of a string bold.
* #param text Full text
* #param textToBold Text you want to make bold
* #return String with bold substring
*/
public static SpannableStringBuilder makeSectionOfTextBold(String text, String textToBold){
SpannableStringBuilder builder=new SpannableStringBuilder();
if(textToBold.length() > 0 && !textToBold.trim().equals("")){
//for counting start/end indexes
String testText = text.toLowerCase(Locale.US);
String testTextToBold = textToBold.toLowerCase(Locale.US);
int startingIndex = testText.indexOf(testTextToBold);
int endingIndex = startingIndex + testTextToBold.length();
//for counting start/end indexes
if(startingIndex < 0 || endingIndex <0){
return builder.append(text);
}
else if(startingIndex >= 0 && endingIndex >=0){
builder.append(text);
builder.setSpan(new StyleSpan(Typeface.BOLD), startingIndex, endingIndex, 0);
}
}else{
return builder.append(text);
}
return builder;
}
As wtsang02 said, using HTML is an expensive overhead. Just use the native solution. If you don't have to modify the string, just use SpannableString, not SpannableStringBuilder.
String boldText = "id";
String normalText = "name";
SpannableString str = new SpannableString(boldText + normalText);
str.setSpan(new StyleSpan(Typeface.BOLD), 0, boldText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(str);
In case you want to use the string from XML, you can do something like this:
strings.xml (the "CDATA" part is important, otherwise it won't work)
<string name="test">
<![CDATA[
<b>bold!</b> normal
]]>
</string>
layout file
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">
<TextView
android:id="#+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
code
textView.text = HtmlCompat.fromHtml(getString(R.string.test), HtmlCompat.FROM_HTML_MODE_LEGACY)
Its simple just close the specified text like this for example <b>"your text here:"</b>
<string name="headquarters">"<b>"Headquarters:"</b>" Mooresville, North Carolina, U.S.</string>
result:
Headquarters: Mooresville, North Carolina, U.S.
If you are using Kotlin, it becomes even easier to do by using core-ktx, as it provides a domain-specific-language (DSL) for doing this:
val string: SpannedString = buildSpannedString {
bold {
append("foo")
}
append("bar")
}
More options provided by it are:
append("Hello There")
bold {
append("bold")
italic {
append("bold and italic")
underline {
append("then some text with underline")
}
}
}
At last, you can just to:
textView.text = string
Based on #mladj0ni's answer, I got the code below to work. The problem was that if you use String.format, it strips out the html markup, so you have to escape the bracket symbols in strings.xml:
strings.xml:
<string name="welcome_messages">Hello, %1$s! You have <b>%2$d new messages</b>.</string>
code.java:
String unspanned = String.format(Locale.US, "%s%s", getResources().getString(R.string. welcome_messages), 99);
Spanned spanned;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
spanned = Html.fromHtml(unspanned, Html.FROM_HTML_MODE_LEGACY);
} else {
spanned = Html.fromHtml(unspanned);
}
textView.setText(spanned);
It's simpler than SpannableStringBuilder. As for performance, if you're displaying just one string, then the user won't notice the extra millisecond to parse it.
See the documentation here.
Here is better solution if you want to make multiple text to bold. I've improved Eitan's code. thanks Eitan.
public static SpannableStringBuilder makeSectionOfTextBold(String text, String... textToBold) {
SpannableStringBuilder builder = new SpannableStringBuilder(text);
for (String textItem :
textToBold) {
if (textItem.length() > 0 && !textItem.trim().equals("")) {
//for counting start/end indexes
String testText = text.toLowerCase(Locale.US);
String testTextToBold = textItem.toLowerCase(Locale.US);
int startingIndex = testText.indexOf(testTextToBold);
int endingIndex = startingIndex + testTextToBold.length();
if (startingIndex >= 0 && endingIndex >= 0) {
builder.setSpan(new StyleSpan(Typeface.BOLD), startingIndex, endingIndex, 0);
}
}
}
return builder;
}
You can use this code to set part of your text to bold. For whatever is in between the bold html tags, it will make it bold.
String myText = "make this <b>bold</b> and <b>this</b> too";
textView.setText(makeSpannable(myText, "<b>(.+?)</b>", "<b>", "</b>"));
public SpannableStringBuilder makeSpannable(String text, String regex, String startTag, String endTag) {
StringBuffer sb = new StringBuffer();
SpannableStringBuilder spannable = new SpannableStringBuilder();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
sb.setLength(0);
String group = matcher.group();
String spanText = group.substring(startTag.length(), group.length() - endTag.length());
matcher.appendReplacement(sb, spanText);
spannable.append(sb.toString());
int start = spannable.length() - spanText.length();
spannable.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), start, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
sb.setLength(0);
matcher.appendTail(sb);
spannable.append(sb.toString());
return spannable;
}
wtsang02 answer is the best way to go about it, since, Html.fromHtml("") is now deprecated. Here I'm just going to enhance it a little bit for whoever is having problem in dynamically making the first word bold, no matter whats the size of the sentence.
First lets create a method to get the first word:
private String getFirstWord(String input){
for(int i = 0; i < input.length(); i++){
if(input.charAt(i) == ' '){
return input.substring(0, i);
}
}
return input;
}
Now let's say you have a long string like this:
String sentence = "friendsAwesomeName#gmail.com want's to be your friend!"
And you want your sentence to be like yourAwesomeName#gmail.com want's to be your friend!
All you have to do is- get the firstWord and get the lenght of it to make the firstWord bold, something like this:
String myFirstWord = getFirstWord(sentence);
int start = 0; // bold will start at index 0
int end = myFirstWord.length(); // and will finish at whatever the length of your first word
Now just follow wtsang02 's steps, like this:
SpannableStringBuilder fancySentence = new SpannableStringBuilder(sentence);
fancySentence.setSpan(new android.text.style.StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(fancySentence);
And that's it! Now you should be able to bold a word with any size from long/short sentence.
Isn't this code the easiest solution?
<string name="string">Please provide your <b>Name</b> properly</string>
Just use this string whatever you want :)
The result will like this:
Please provide your Name properly
I came here to provide a more up-to-date solution, because I wasn't satisfied with the existing answers.
I needed something that would work for translated texts and does not have the performance hit of using Html.fromHtml().
If you're using Kotlin, here is an extension function which will easily set multiple parts of your text to bold. This works just like Markdown, and could be extended to support other Markdown tags, if need be.
val yourString = "**This** is your **string**.".makePartialTextsBold()
val anotherString = getString(R.string.something).makePartialTextsBold()
/**
* This function requires that the parts of the string that need
* to be bolded are wrapped in ** and ** tags
*/
fun String.makePartialTextsBold(): SpannableStringBuilder {
var copy = this
return SpannableStringBuilder().apply {
var setSpan = true
var next: String
do {
setSpan = !setSpan
next = if (length == 0) copy.substringBefore("**", "") else copy.substringBefore("**")
val start = length
append(next)
if (setSpan) {
setSpan(StyleSpan(Typeface.BOLD), start, length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
copy = copy.removePrefix(next).removePrefix("**")
} while (copy.isNotEmpty())
}
}
Here is my complete solution for dynamic String values with case check.
/**
* Makes a portion of String formatted in BOLD.
*
* #param completeString String from which a portion needs to be extracted and formatted.<br> eg. I am BOLD.
* #param targetStringToFormat Target String value to format. <br>eg. BOLD
* #param matchCase Match by target character case or not. If true, BOLD != bold
* #return A string with a portion formatted in BOLD. <br> I am <b>BOLD</b>.
*/
public static SpannableStringBuilder formatAStringPortionInBold(String completeString, String targetStringToFormat, boolean matchCase) {
//Null complete string return empty
if (TextUtils.isEmpty(completeString)) {
return new SpannableStringBuilder("");
}
SpannableStringBuilder str = new SpannableStringBuilder(completeString);
int start_index = 0;
//if matchCase is true, match exact string
if (matchCase) {
if (TextUtils.isEmpty(targetStringToFormat) || !completeString.contains(targetStringToFormat)) {
return str;
}
start_index = str.toString().indexOf(targetStringToFormat);
} else {
//else find in lower cases
if (TextUtils.isEmpty(targetStringToFormat) || !completeString.toLowerCase().contains(targetStringToFormat.toLowerCase())) {
return str;
}
start_index = str.toString().toLowerCase().indexOf(targetStringToFormat.toLowerCase());
}
int end_index = start_index + targetStringToFormat.length();
str.setSpan(new StyleSpan(BOLD), start_index, end_index, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return str;
}
Eg. completeString = "I am BOLD"
CASE I
if *targetStringToFormat* = "bold" and *matchCase* = true
returns "I am BOLD" (since bold != BOLD)
CASE II
if *targetStringToFormat* = "bold" and *matchCase* = false
returns "I am BOLD"
To Apply:
myTextView.setText(formatAStringPortionInBold("I am BOLD", "bold", false))
Hope that helps!
I used this code to bold specific words...
Spanned string = Html.fromHtml("Normal string <b>BOLD STRING</b>");
textView.setText(string);
public static Spanned getBoldString(String textNotBoldFirst, String textToBold, String textNotBoldLast) {
String resultant = null;
resultant = textNotBoldFirst + " " + "<b>" + textToBold + "</b>" + " " + textNotBoldLast;
return Html.fromHtml(resultant);
}
Try this. It can help definitely
Make first char of string spannable while searching for char in list/recycler like
ravi and ajay
previously highlighting like this but i wanted to be like below
ravi and ajay OR ravi and ajay
for this I searched for word length if it is equal to 1 ,I separated main string into words and calculated word start position then I searched word starting with char.
public static SpannableString colorString(int color, String text, String... wordsToColor) {
SpannableString coloredString = new SpannableString(text);
for (String word : wordsToColor) {
Log.e("tokentoken", "-wrd len-" + word.length());
if (word.length() !=1) {
int startColorIndex = text.toLowerCase().indexOf(word.toLowerCase());
int endColorIndex = startColorIndex + word.length();
try {
coloredString.setSpan(new ForegroundColorSpan(color), startColorIndex, endColorIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.getMessage();
}
} else {
int start = 0;
for (String token : text.split("[\u00A0 \n]")) {
if (token.length() > 0) {
start = text.indexOf(token, start);
// Log.e("tokentoken", "-token-" + token + " --start--" + start);
char x = token.toLowerCase().charAt(0);
char w = word.toLowerCase().charAt(0);
// Log.e("tokentoken", "-w-" + w + " --x--" + x);
if (x == w) {
// int startColorIndex = text.toLowerCase().indexOf(word.toLowerCase());
int endColorIndex = start + word.length();
try {
coloredString.setSpan(new ForegroundColorSpan(color), start, endColorIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.getMessage();
}
}
}
}
}
}
return coloredString;
}
You can add the two strings separately in the builder, one of them is spannedString, the other is a regular one.This way you don`t have to calculate the indexes.
val instructionPress = resources?.getString(R.string.settings_press)
val okText = resources?.getString(R.string.ok)
val spannableString = SpannableString(okText)
val spannableBuilder = SpannableStringBuilder()
spannableBuilder.append(instructionPress)
spannableBuilder.append(spannableString, StyleSpan(Typeface.BOLD), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
instructionText.setText(spannableBuilder,TextView.BufferType.SPANNABLE)
This is the Kotlin extension function I use for this
/**
* Sets the specified Typeface Style on the first instance of the specified substring(s)
* #param one or more [Pair] of [String] and [Typeface] style (e.g. BOLD, ITALIC, etc.)
*/
fun TextView.setSubstringTypeface(vararg textsToStyle: Pair<String, Int>) {
val spannableString = SpannableString(this.text)
for (textToStyle in textsToStyle) {
val startIndex = this.text.toString().indexOf(textToStyle.first)
val endIndex = startIndex + textToStyle.first.length
if (startIndex >= 0) {
spannableString.setSpan(
StyleSpan(textToStyle.second),
startIndex,
endIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
this.setText(spannableString, TextView.BufferType.SPANNABLE)
}
Usage:
text_view.text="something bold"
text_view.setSubstringTypeface(
Pair(
"something bold",
Typeface.BOLD
)
)
.
text_view.text="something bold something italic"
text_view.setSubstringTypeface(
Pair(
"something bold ",
Typeface.BOLD
),
Pair(
"something italic",
Typeface.ITALIC
)
)
if the position of bold text is fixed(ex: if is at start of the textView), then use two different textView with same background. Then you can make the other textView's textStyle as bold.
This will require twice the memory compared to a single textView but speed will increase.
Found a way in case you want to handle localization in multiple languages, it's boring to do but it works, let's suppose we want this:
In English:
There are no payments registered
In Spanish:
No hay pagos registrados
You have to create 3 strings
English:
<string name="start_string">There are no</string>
<string name="middle_string">payments</string>
<string name="end_string">registered.</string>
<string name="string_format" translatable="false">%1$s %2$s %3$s</string>
Spanish:
<string name="start_string">No hay</string>
<string name="middle_string">pagos</string>
<string name="end_string">registrados</string>
Now you can do this:
val startSpanPosition = getString(R.string.start_string).length
val endSpanPosition = startSpanPosition + getString(R.string.middle_string).length
val mySpannableString = SpannableStringBuilder(String.format(getString(R.string.string_format),
getString(R.string.start_string), getString(R.string.middle_string))), getString(R.string.end_string)))
mySpannableString.setSpan(StyleSpan(Typeface.BOLD), spanStartPosition, endSpanPosition, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
Your String resource
<resources>
<string name="your_string_resource_name">This is normal text<![CDATA[<b> but this is bold </b>]]> and <![CDATA[<u> but this is underline text</u>]]></string>
</resources>
your java class
yourtextView.setText(getString(R.string.your_string_resource_name));
I have created a static method for setting part of text Bold for TextView and EditText
public static void boldPartOfText(View mView, String contentData, int startIndex, int endIndex){
if(!contentData.isEmpty() && contentData.length() > endIndex) {
final SpannableStringBuilder sb = new SpannableStringBuilder(contentData);
final StyleSpan bss = new StyleSpan(Typeface.BOLD); // Span to make text bold
final StyleSpan iss = new StyleSpan(Typeface.NORMAL); //Span to make text normal
sb.setSpan(iss, 0, startIndex, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
sb.setSpan(bss, startIndex, endIndex, Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
sb.setSpan(iss,endIndex, contentData.length()-1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
if(mView instanceof TextView)
((TextView) mView).setText(sb);
else if(mView instanceof EditText)
((EditText) mView).setText(sb);
}
}
Another more customized code
/*typeFaceStyle can be passed as
Typeface.NORMAL = 0;
Typeface.BOLD = 1;
Typeface.ITALIC = 2;
Typeface.BOLD_ITALIC = 3;*/
public static void boldPartOfText(View mView, String contentData, int startIndex, int endIndex,int typeFaceStyle){
if(!contentData.isEmpty() && contentData.length() > endIndex) {
final SpannableStringBuilder sb = new SpannableStringBuilder(contentData);
final StyleSpan bss = new StyleSpan(typeFaceStyle); // Span to make text bold
final StyleSpan iss = new StyleSpan(Typeface.NORMAL); //Span to make text italic
sb.setSpan(iss, 0, startIndex, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
sb.setSpan(bss, startIndex, endIndex, Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
sb.setSpan(iss,endIndex,contentData.length()-1,Spanned.SPAN_INCLUSIVE_INCLUSIVE);
if(mView instanceof TextView)
((TextView) mView).setText(sb);
else if(mView instanceof EditText)
((EditText) mView).setText(sb);
}
}
In case someone is using Data Binding. We can define binding adapter like this
#BindingAdapter("html")
fun setHtml(view: TextView, html: String) {
view.setText(HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY))
}
Then we can use it on a TextView
app:html="#{#string/bold_text}"
where bold_text is
<string name="bold_text"><![CDATA[Part of text is <b>bold</b>]]></string>
Simple Example
In you strings.xml
<string name="str_privacy_policy">This is our Privacy Policy.</string>
if you want to make specifically "Privacy Policy" as bold put the string between the bold tags.
Like this
<string name="str_privacy_policy">This is our <b>Privacy Policy.</b></string>
Result would be
This is our Privacy Policy
Here's how I do it using regular expressions and Kotlin
val BOLD_SPAN = StyleSpan(Typeface.BOLD)
fun TextView.boldMatches(regexString: String) {
this.applyStyleSpanToMatches(regexString, BOLD_SPAN)
}
fun TextView.applyStyleSpanToMatches(regexString: String, span: StyleSpan){
this.text = this.text.toString().applyStyleSpanToMatches(regexString, span)
}
fun String.applyStyleSpanToMatches(regexString: String, span: StyleSpan): Spannable {
val result = SpannableString.valueOf(this)
if(regexString.isEmpty()) return result
val pattern = try{
Pattern.compile(regexString)
} catch (e: PatternSyntaxException){
return result
}
val matcher = pattern.matcher(result)
while (matcher.find()) {
val start = matcher.start()
val end = matcher.end()
result.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
return result
}
using the question
it can be applied like this:
txtResult.boldMatches(id)
If you are using Kotlin and string resources, a simple solution is:
Create your string on strings.xml, using <b> </b> to bold the parts you want
<string name="my_message"> This is a very <b>important</b> message! </string>
On Kotlin code you must do like so
textView.setText(R.string.my_message)
And that is it!
Important note!
Using property syntax will not work:
textView.text = resources.getString(R.string.my_message)
Hope it helps!
val phone = "+45xxxxxx"
val phoneText = "<font color=#757B7F><b>${phone}</b></font>"
val wholeString = requireActivity().resources.getString(R.string.loginwith)+" "+phoneText
Just add this in your tag
dangerouslySetInnerHTML={{__html: "<p>Your html text here.<p>"}}

Change text color of one word in a TextView

I am looking for a way to change the color of a text of a single word in a TextView from within an Activity.
For example, with this:
String first = "This word is ";
String next = "red"
TextView t = (TextView) findViewById(R.id.textbox);
t.setText(first + next);
How would I change the color of the next text to red?
Easiest way I know is to just use html.
String first = "This word is ";
String next = "<font color='#EE0000'>red</font>";
t.setText(Html.fromHtml(first + next));
But this will require you to rebuild the TextView when (if?) you want to change the color, which could cause a hassle.
t.setText(first + next, BufferType.SPANNABLE);
Spannable s = (Spannable)t.getText();
int start = first.length();
int end = start + next.length();
s.setSpan(new ForegroundColorSpan(0xFFFF0000), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
you have to use spannable this will also allows you to increase some text's size, make it bold etc.... even put in some image.
Use SpannableStringBuilder like this :
SpannableStringBuilder builder = new SpannableStringBuilder();
SpannableString str1= new SpannableString("Text1");
str1.setSpan(new ForegroundColorSpan(Color.RED), 0, str1.length(), 0);
builder.append(str1);
SpannableString str2= new SpannableString(appMode.toString());
str2.setSpan(new ForegroundColorSpan(Color.GREEN), 0, str2.length(), 0);
builder.append(str2);
TextView tv = (TextView) view.findViewById(android.R.id.text1);
tv.setText( builder, TextView.BufferType.SPANNABLE);
for long string you can use this:
String help = getString(R.string.help);
help = help.replace("some word", "<font color='#EE0000'>some word</font>");
txtDesc.setText(Html.fromHtml(help));
If you want to change the state of all the instances of a specific String inside a TextView text(case insensitive) you can use StringBuilders and SpannableString like this:
StringBuilder textBuilder = new StringBuilder(myTextView.getText().toString());
StringBuilder searchedTextBuilder = new StringBuilder((mySearchedString));
SpannableString spannableString = new SpannableString(myTextView.getText().toString());
int counter = 0;
int index = 0;
for (int i = 0;i < textBuilder.length() - mySearchedString.length() - 1;i++)
{
counter = 0;
if (Character.toLowerCase(textBuilder.charAt(i)) == Character.toLowerCase(searchedTextBuilder.charAt(index)))
{
counter++;
index++;
for (int j = 1,z = i + 1;j < mySearchedString.length() - 1;j++,z++)
{
if (Character.toLowerCase(textBuilder .charAt(z)) == Character.toLowerCase(searchedTextBuilder .charAt(index)))
{
counter++;
index++;
}
else
{
index++;
if (index % mySearchedString.length() == 0)
{
index = 0;
}
break;
}
}
if (counter == mySearchedString.length() - 1) // A match
{
spannableString.setSpan(new ForegroundColorSpan(Color.RED), i,
i + mySearchedString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // Do the change you want(In this case changing the fore ground color to red)
index = 0;
continue;
}
else
{
index = 0;
continue;
}
}
}
myTextView.setText(spannableString);
}
Store the whole TextView text inside a StringBuilder.
Store the searched string inside a StringBuilder.
Store the wholre TextView text inside a SpannableString
Make a simple operation to find all the String instances inside the TextView text and change them when reached.
Set the text value of the TextView to the SpannableString.
I implemented a utility function in Kotlin for my own usecase and maybe useful for someone else.
fun getCusomTextWithSpecificTextWithDiffColor(textToBold: String, fullText: String,
targetColor: Int) =
SpannableStringBuilder(fullText).apply {
setSpan(ForegroundColorSpan(targetColor),
fullText.indexOf(textToBold),
(fullText.indexOf(textToBold) + textToBold.length),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
How I am using it:
context?.let {
infoMessage.text = AppUtils.getCusomTextWithSpecificTextWithDiffColor(
wordAsBold,
completeSentence, ContextCompat.getColor(it, R.color.white))
}
USE:
makeTextBold("Your order is accepted","accepted", textView);
makeTextBold("Your order is canceled","canceled", textView);
Function:
public static void makeTextBold(String sentence, String word, AppCompatTextView textView) {
SpannableStringBuilder builder = new SpannableStringBuilder();
int startIndex = sentence.indexOf(word.toLowerCase().trim());
int endIndex = startIndex + word.toLowerCase().trim().length();
SpannableString spannableString = new SpannableString(sentence);
StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
spannableString.setSpan(boldSpan, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //To make text Bold
spannableString.setSpan(new ForegroundColorSpan(Color.RED), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //To change color of text
builder.append(spannableString);
textView.setText(builder, TextView.BufferType.SPANNABLE);
}
I think this is more readable
for coloring a word in a string
it is also probably more efficient a bit because you write once
String str = YOUR_STRING
Spannable s = new SpannableString(str);
int start = str.indexOf(err_word_origin);
int end = start + err_word_origin.length();
s.setSpan(new ForegroundColorSpan(Color.BLUE), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
YOUR_TEXT_VIEW.setText(s , TextView.BufferType.SPANNABLE);
my solution extension:
fun coloredText(
baseText: String,
coloredText: String,
targetColor: Int
): SpannableStringBuilder {
val transformText = "$baseText $coloredText"
return SpannableStringBuilder(transformText).apply {
setSpan(
ForegroundColorSpan(targetColor),
transformText.indexOf(coloredText),
(transformText.indexOf(coloredText) + coloredText.length),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
Usage
binding.mytextView.title = coloredText(
baseText = getString(R.string.my_title),
coloredText = getString(R.string.my_title_colored_part),
targetColor = ContextCompat.getColor(requireContext(), R.color.blue))
Iv'e found this best answer
https://stackoverflow.com/a/53573169/14250778
just changed one line to support also words that starts with uppercase letter
public void setHighLightedText(TextView tv, String textToHighlight) {
// added "toLowerCase()" to support words that starts with uppercase letter
String tvt = tv.getText().toString().toLowerCase();
int ofe = tvt.indexOf(textToHighlight, 0);
Spannable wordToSpan = new SpannableString(tv.getText());
for (int ofs = 0; ofs < tvt.length() && ofe != -1; ofs = ofe + 1) {
ofe = tvt.indexOf(textToHighlight, ofs);
if (ofe == -1)
break;
else {
// set color here
wordToSpan.setSpan(new BackgroundColorSpan(0xFFFFFF00), ofe, ofe + textToHighlight.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(wordToSpan, TextView.BufferType.SPANNABLE);
}
}
}

How can I change the color of a part of a TextView?

text = text + CepVizyon.getPhoneCode() + "\n\n"
+ getText(R.string.currentversion) + CepVizyon.getLicenseText();
activationText.setText(text);
myTextView.setText(text);
I want to change color for CepVizyon.getPhoneCode()'s string. How can I do this?
Spannable is more flexible:
String text2 = text + CepVizyon.getPhoneCode() + "\n\n"
+ getText(R.string.currentversion) + CepVizyon.getLicenseText();
Spannable spannable = new SpannableString(text2);
spannable.setSpan(new ForegroundColorSpan(Color.WHITE), text.length(), (text + CepVizyon.getPhoneCode()).length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
myTextView.setText(spannable, TextView.BufferType.SPANNABLE);
If you have static text that needs color, you can add it without any code via the strings file:
<string name="already_have_an_account">Already have an account? <font color='#01C6DB'>Login</font></string>
then
<TextView
android:layout_width="wrap_content"
android:layout_height="64dp"
android:text="#string/already_have_an_account"/>
result
I'm not sure which API versions this works on, but it doesn't work for API 19 that I've tested so far, so probably only some of the most recent API versions support this.
As #hairraisin mentioned in the comments, try using fgcolor instead of color for the font color, then it should work for lower API levels, but need more testing to be sure.
myTextView.setText(Html.fromHtml(text + "<font color=white>" + CepVizyon.getPhoneCode() + "</font><br><br>"
+ getText(R.string.currentversion) + CepVizyon.getLicenseText()));
Here solution in Kotlin that uses SpannableString to change color of part of a string.
val phoneCodeColor = ContextCompat.getColor(this, R.color.myColor)
val text = SpannableStringBuilder()
.color(phoneCodeColor) { append("${ CepVizyon.getPhoneCode() }") }
.append("\n\n")
.append(getString(R.string.currentversion))
.append(${ CepVizyon.getLicenseText() })
activationText.text = text
myTextView.text = text
With regards to Maneesh's answer, this will work but you need to add and escape the quotes for the color attribute.
myTextView.setText(Html.fromHtml(text + "<font color=\"#FFFFFF\">" + CepVizyon.getPhoneCode() + "</font><br><br>"
+ getText(R.string.currentversion) + CepVizyon.getLicenseText()));
It is good for me!
Spannable spannable = new SpannableString("ABC In-Network DEF");
String str = spannable.toString();
iStart = str.indexOf("In-Network");
iEnd = iStart + 10;/*10 characters = in-network. */
SpannableString ssText = new SpannableString(spannable);
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View widget) {
//your code at here.
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(true);
ds.setColor(getResources().getColor(R.color.green));
}
};
ssText.setSpan(clickableSpan, iStart, iEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTextView.setText(ssText);
mTextView.setMovementMethod(LinkMovementMethod.getInstance());
mTextView.setHighlightColor(Color.TRANSPARENT);
mTextView.setEnabled(true);
Here's a colorize function based on andyboot's answer:
/**
* Colorize a specific substring in a string for TextView. Use it like this: <pre>
* textView.setText(
* Strings.colorized("The some words are black some are the default.","black", Color.BLACK),
* TextView.BufferType.SPANNABLE
* );
* </pre>
* #param text Text that contains a substring to colorize
* #param word The substring to colorize
* #param argb The color
* #return the Spannable for TextView's consumption
*/
public static Spannable colorized(final String text, final String word, final int argb) {
final Spannable spannable = new SpannableString(text);
int substringStart=0;
int start;
while((start=text.indexOf(word,substringStart))>=0){
spannable.setSpan(
new ForegroundColorSpan(argb),start,start+word.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
substringStart = start+word.length();
}
return spannable;
}
I have made this little function, just pass in your text to color, the start and end indexes of what you want to color of that text and the color itself
Kotlin
private fun colorMyText(inputText:String,startIndex:Int,endIndex:Int,textColor:Int):Spannable{
val outPutColoredText: Spannable = SpannableString(inputText)
outPutColoredText.setSpan(
ForegroundColorSpan(textColor), startIndex, endIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
return outPutColoredText
}
Usage
txt_comment.text = colorMyText("Comentario: ${item.comentario}",0,13,Color.BLACK)
Use character escapes + Html.fromHtml()
How to store the String in the string resource folder
<string name="textFromRes">
<font color="#FF0000">This is colored in red </font> This is not
</string>
How to show in TextView?
String text = this.getResources().getString(R.string.textFromRes);
htmlText.setText(Html.fromHtml(text));
Bonus:
The String in the output looks like this
<string name="textFromRes">
<font color="#FF0000">This is colored in red </font> This is not
<br />
<h1> This is h1 heading </h1>
<br />
<h3> This is h2 subheading</h3>
<br />
<b> This text is bold</b>
<br />
<i> This text is italic</i>
<br />
Android users expect your app to look and behave in a way that is
consistent with the platform. Not only should you follow material
design guidelines for visual and navigation patterns,
but you should also follow quality guidelines for compatibility,
performance, security, and more.
<br />
<br />
The following links provide everything you need to design a high quality Android app.
</string>
With a general-purpose Kotlin extension function, it would look like this:
/**
* Change the color of a part of the text contained in this textView
*
* #param subStringToColorize has to already be set in the textView's text
* #param colorResId
*/
fun TextView.colorize(subStringToColorize: String, #ColorRes colorResId: Int) {
val spannable: Spannable = SpannableString(text)
val startIndex = text.indexOf(subStringToColorize, startIndex = 0, ignoreCase = false)
val endIndex = startIndex + subStringToColorize.length
val color: Int = ContextCompat.getColor(context, colorResId)
if (startIndex != -1) {
spannable.setSpan(ForegroundColorSpan(color),
startIndex,
endIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
setText(spannable, TextView.BufferType.SPANNABLE)
}
}
I didn't like the idea of doing this by code every time i want to color parts of the text which i have been doing a lot in all of my apps (and since in some case text is being set in runtime with different inline-defined colors) so i created my own MarkableTextView.
The idea was to:
Detect XML tags from string
Identify and match tag name
Extract and save attributes and position of text
Remove tag and keep content
Iterate through attributes and apply styles
Here's the process step by step:
First i needed a way to find XML tags in a given string and Regex did the trick..
<([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)(?:\s+([^>]*))?>([^>][^<]*)</\1\s*>
For the above to match an XML tag it has to have the following criteria:
Valid tag name like <a> <a > <a-a> <a ..attrs..> but not < a> <1>
Closing tag that has a matching name like <a></a> but not <a></b>
Any content, since there's no need to style "nothing"
Now for the attributes we're going to use this one..
([a-zA-Z]+)\s*=\s*(['"])\s*([^'"]+?)\s*\2
It has the same concept and generally i didn't need to go far for both since the compiler will take care of the rest if anything goes out of format.
Now we need a class that can hold the extracted data:
public class MarkableSheet {
private String attributes;
private String content;
private int outset;
private int ending;
private int offset;
private int contentLength;
public MarkableSheet(String attributes, String content, int outset, int ending, int offset, int contentLength) {
this.attributes = attributes;
this.content = content;
this.outset = outset;
this.ending = ending;
this.offset = offset;
this.contentLength = contentLength;
}
public String getAttributes() {
return attributes;
}
public String getContent() {
return content;
}
public int getOutset() {
return outset;
}
public int getContentLength() {
return contentLength;
}
public int getEnding() {
return ending;
}
public int getOffset() {
return offset;
}
}
Before anything else, we're going to add this cool iterator that i've been using for long to loop through matches (can't remember the author):
public static Iterable<MatchResult> matches(final Pattern p, final CharSequence input) {
return new Iterable<MatchResult>() {
public Iterator<MatchResult> iterator() {
return new Iterator<MatchResult>() {
// Use a matcher internally.
final Matcher matcher = p.matcher(input);
// Keep a match around that supports any interleaving of hasNext/next calls.
MatchResult pending;
public boolean hasNext() {
// Lazily fill pending, and avoid calling find() multiple times if the
// clients call hasNext() repeatedly before sampling via next().
if (pending == null && matcher.find()) {
pending = matcher.toMatchResult();
}
return pending != null;
}
public MatchResult next() {
// Fill pending if necessary (as when clients call next() without
// checking hasNext()), throw if not possible.
if (!hasNext()) { throw new NoSuchElementException(); }
// Consume pending so next call to hasNext() does a find().
MatchResult next = pending;
pending = null;
return next;
}
/** Required to satisfy the interface, but unsupported. */
public void remove() { throw new UnsupportedOperationException(); }
};
}
};
}
MarkableTextView:
public class MarkableTextView extends AppCompatTextView {
public MarkableTextView(Context context) {
super(context);
}
public MarkableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MarkableTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setText(CharSequence text, BufferType type) {
// Intercept and process text
text = prepareText(text.toString());
super.setText(text, type);
}
public Spannable Markable;
private Spannable prepareText(String text) {
String parcel = text;
Multimap<String, MarkableSheet> markableSheets = ArrayListMultimap.create();
// Used to correct content position after tossing tags
int totalOffset = 0;
// Iterate through text
for (MatchResult match : matches(Markable.Patterns.XML, parcel)) {
// Get tag name
String tag = match.group(1);
// Match with a defined tag name "case-sensitive"
if (!tag.equals(Markable.Tags.MARKABLE)) {
// Break if no match
break;
}
// Extract data
String attributes = match.group(2);
String content = match.group(3);
int outset = match.start(0);
int ending = match.end(0);
int offset = totalOffset; // offset=0 since no preceded changes happened
int contentLength = match.group(3).length();
// Calculate offset for the next element
totalOffset = (ending - outset) - contentLength;
// Add to markable sheets
MarkableSheet sheet =
new MarkableSheet(attributes, content, outset, ending, offset, contentLength);
markableSheets.put(tag, sheet);
// Toss the tag and keep content
Matcher reMatcher = Markable.Patterns.XML.matcher(parcel);
parcel = reMatcher.replaceFirst(content);
}
// Initialize spannable with the modified text
Markable = new SpannableString(parcel);
// Iterate through markable sheets
for (MarkableSheet sheet : markableSheets.values()) {
// Iterate through attributes
for (MatchResult match : matches(Markable.Patterns.ATTRIBUTES, sheet.getAttributes())) {
String attribute = match.group(1);
String value = match.group(3);
// Apply styles
stylate(attribute,
value,
sheet.getOutset(),
sheet.getOffset(),
sheet.getContentLength());
}
}
return Markable;
}
Finally, styling, so here's a very simple styler i made for this answer:
public void stylate(String attribute, String value, int outset, int offset, int length) {
// Correct position
outset -= offset;
length += outset;
if (attribute.equals(Markable.Tags.TEXT_STYLE)) {
if (value.contains(Markable.Tags.BOLD) && value.contains(Markable.Tags.ITALIC)) {
Markable.setSpan(
new StyleSpan(Typeface.BOLD_ITALIC),
outset,
length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else if (value.contains(Markable.Tags.BOLD)) {
Markable.setSpan(
new StyleSpan(Typeface.BOLD),
outset,
length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else if (value.contains(Markable.Tags.ITALIC)) {
Markable.setSpan(
new StyleSpan(Typeface.ITALIC),
outset,
length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (value.contains(Markable.Tags.UNDERLINE)) {
Markable.setSpan(
new UnderlineSpan(),
outset,
length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
if (attribute.equals(Markable.Tags.TEXT_COLOR)) {
if (value.equals(Markable.Tags.ATTENTION)) {
Markable.setSpan(
new ForegroundColorSpan(ContextCompat.getColor(
getContext(),
R.color.colorAttention)),
outset,
length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else if (value.equals(Markable.Tags.INTERACTION)) {
Markable.setSpan(
new ForegroundColorSpan(ContextCompat.getColor(
getContext(),
R.color.colorInteraction)),
outset,
length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
And here's how the Markable class containing the definitions looks like:
public class Markable {
public static class Patterns {
public static final Pattern XML =
Pattern.compile("<([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)(?:\\s+([^>]*))?>([^>][^<]*)</\\1\\s*>");
public static final Pattern ATTRIBUTES =
Pattern.compile("(\\S+)\\s*=\\s*(['\"])\\s*(.+?)\\s*\\2");
}
public static class Tags {
public static final String MARKABLE = "markable";
public static final String TEXT_STYLE = "textStyle";
public static final String BOLD = "bold";
public static final String ITALIC = "italic";
public static final String UNDERLINE = "underline";
public static final String TEXT_COLOR = "textColor";
public static final String ATTENTION = "attention";
public static final String INTERACTION = "interaction";
}
}
All that we need now is to reference a string and basically it should look like this:
<string name="markable_string">
<![CDATA[Hello <markable textStyle=\"underline\" textColor=\"interaction\">world</markable>!]]>
</string>
Make sure to wrap the tags with a CDATA Section and escape " with \.
I made this as a modular solution to process parts of the text in all different ways without the need of stuffing unnecessary code behind.
I did as andy boot said, but i had a clickable span as well, and it didn't work because the order the setSpans were called. So you have to first call the spannable.setSpan(clickableSpanand... then the spannable.setSpan(new ForegroundColorSpan... to get the color in the TextView
Inspired by Alejandro H. Cruz's answer above.
His function only works for a single substring match, I have updated his method to use Regex and should update colors on all matches:
fun TextView.colorizeAll(subStringToColorize: String, #ColorRes colorResId: Int) {
val color: Int = ContextCompat.getColor(context, colorResId)
val spannable: Spannable = SpannableString(text)
val pattern = subStringToColorize.toRegex()
val matches = pattern.findAll(text, 0)
matches.forEach { match ->
val startIndex = match.range.first
val endIndex = match.range.last + match.range.step
spannable.setSpan(ForegroundColorSpan(color),
startIndex,
endIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
setText(spannable, TextView.BufferType.SPANNABLE)
}
}
I created this little helper method that can be called from a TextView:
fun TextView.attributedString(
forText: String,
foregroundColor: Int? = null,
style: StyleSpan? = null
) {
val spannable: Spannable = SpannableString(text)
// check if the text we're highlighting is empty to abort
if (forText.isEmpty()) {
return
}
// compute the start and end indices from the text
val startIdx = text.indexOf(forText)
val endIdx = startIdx + forText.length
// if the indices are out of bounds, abort as well
if (startIdx < 0 || endIdx > text.length) {
return
}
// check if we can apply the foreground color
foregroundColor?.let {
spannable.setSpan(
ForegroundColorSpan(it),
startIdx,
endIdx,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
)
}
// check if we have a stylespan
style?.let {
spannable.setSpan(
style,
startIdx,
endIdx,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
)
}
// apply it
text = spannable
}
To use it:
plateText.text = "Hello world!"
// This will color the part "world" to whatever color you have defined
// And make the word **bold**.
plateText.attributedString(
"world",
getColor(R.color.colorMatchingText, null),
StyleSpan(Typeface.BOLD)
)
Tested on API 29, cheers!
SpannableStringBuilder builder = new SpannableStringBuilder();
String the = "The ";
SpannableString theSpannable= new SpannableString(the);
builder.append(theSpannable);
String author = "author ";
SpannableString authorSpannable= new SpannableString(author);
authorSpannable.setSpan(new RelativeSizeSpan(1.2f), 0,authorSpannable.length(), 0); // set size
authorSpannable.setSpan(new ForegroundColorSpan(Color.BLACK), 0, authorSpannable.length(), 0);
builder.append(authorSpannable);
String has = "has ";
SpannableString hasSpannable= new SpannableString(has);
builder.append(hasSpannable);
String approved = "approved ";
SpannableString approvedSpannable= new SpannableString(approved);
approvedSpannable.setSpan(new RelativeSizeSpan(1.2f), 0,approvedSpannable.length(), 0); // set size
StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
approvedSpannable.setSpan(boldSpan, 0, approvedSpannable.length() + 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
approvedSpannable.setSpan(new ForegroundColorSpan(ContextCompat.getColor(this, R.color.CLR_PRESSED_SAVED)), 0,
approvedSpannable.length(), 0);
builder.append(approvedSpannable);
String white = "your access to this share. Do you want re-access now?";
SpannableString whiteSpannable= new SpannableString(white);
builder.append(whiteSpannable);
_AccessStatusText.setText(builder, TextView.BufferType.SPANNABLE);
its Simple
String text = "We've sent the code to ";
String text2 = text + getEmail() + "\n\n";
Spannable spannable = new SpannableString(text2);
spannable.setSpan(new ForegroundColorSpan(Color.BLUE), text.length(), (text + getEmail()).length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mBinding.textViewStatus.setText(spannable, TextView.BufferType.SPANNABLE);
DO NOT USE Html.fromHtml to avoid fragmented behavior. Spannable or SpannableStringBuilder is the best way to do this with ForegroundColorSpan or BackgroundColorSpan depending on your requirements. Html or HtmlCompat tag even with style color or background-color is not realiable as it does not work on all SDK specially on lower version such as 21, same case for emojis.
Example case:
<span style=\"background-color:red\">⬆</span>
When the Html string above converts to Spanned using HtmlCompat.fromHtml and use it in setText() the style does not work on older SDK version.
One way is to split myTextView to few separate TextViews, one of which would be only for phone code. Then controlling color of this specific TextView is pretty straight-forward.

Categories

Resources