Retrieve the bold strings in a text - android

I am stuck in a small problem. I have text retrieved from the permission window.
Now I found out a way to retrieve the app name . This is how I did it:
private String Namer(String parse){
if(parse.length() > 30) {
parse = parse.substring(parse.indexOf("********") + 6, parse.length());
parse = parse.substring(0, parse.indexOf("*******"));
return parse;
} else {
return parse;
}
}
and this is how i use it:
List<AccessibilityNodeInfo> NodeInfo;
AccessibilityNodeInfo nodeInfo = event.getSource();
for (Map.Entry<OriginalPermissions, String> permissions :
detect.entrySet()) {
NodeInfo = nodeInfo.findAccessibilityNodeInfosByText(permissions.getValue());
Log.d("Information", Namer(Namer(String.valueOf(NodeInfo))));
}
But the problem is that the permission window text can be different. Is there any way in which I could just retrieve the bold text from the permission window?
Thanks

As you see (source code), title of GrantPermissionsActivity is a Spanned text. Instead relying on the surrounding text you can get the value of first span (with bold text) using such method:
public String getFirstSpanValueFromTitle(Spanned text) {
StyleSpan[] spans = text.getSpans(0, text.length(), StyleSpan.class);
if (spans.length > 0) {
int start = text.getSpanStart(spans[0]);
int end = text.getSpanEnd(spans[0]);
return String.valueOf(text.subSequence(start, end));
}
return "";
}
Otherwise, you will need to get the value of the permission_warning_template resource string for each language and hardcode it in you code.
UPDATE:
Example how to get title text from AccessibilityEvent object:
String pkgName = event.getPackageName().toString();
if ("com.google.android.packageinstaller".equals(pkgName)) {
AccessibilityNodeInfo root = event.getSource();
List<AccessibilityNodeInfo> nodes =
root.findAccessibilityNodeInfosByViewId("com.android.packageinstaller:id/permission_message");
if (nodes.size() > 0) {
Log.e(TAG, "First span value: " + getFirstSpanValueFromTitle((Spanned) nodes.get(0).getText()));
}
}

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>"}}

How to split up a SpannableStringBuilder while keeping its formating?

I am working on an android project that involves parsing some HTML (parsed by Jsoup) into a SpannableStringBuilder class.
However, I need this SpannableStringBuilder class to be divided up by each new line character into a List once it is done parsing, while keeping its formatting.
Such that a spanned text of
{"I am a spanned text,\n hear me roar"}
would turn into
{
"I am a spanned text,"
"hear me roar"
}
I am fairly new to developing on Android, and could not find anything in the documentation about spitting spans or even getting a listing of all formatting on a spanned class to build my own. So any help is much appreciated.
I managed to figure this out on my own, after looking into the method that pskink suggested.
My solution to this was
#Override
public List<Spanned> parse() {
List<Spanned> spans = new ArrayList<Spanned>();
Spannable unsegmented = (Spannable) Html.fromHtml(base.html(), null, new ReaderTagHandler());
//Set ColorSpan because it defaults to white text color
unsegmented.setSpan(new ForegroundColorSpan(Color.BLACK), 0, unsegmented.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//get locations of '/n'
Stack<Integer> loc = getNewLineLocations(unsegmented);
loc.push(unsegmented.length());
//divides up a span by each new line character position in loc
while (!loc.isEmpty()) {
Integer end = loc.pop();
Integer start = loc.isEmpty() ? 0 : loc.peek();
spans.add(0,(Spanned) unsegmented.subSequence(start, end));
}
return spans;
}
private Stack<Integer> getNewLineLocations(Spanned unsegmented) {
Stack<Integer> loc = new Stack<>();
String string = unsegmented.toString();
int next = string.indexOf('\n');
while (next > 0) {
//avoid chains of newline characters
if (string.charAt(next - 1) != '\n') {
loc.push(next);
next = string.indexOf('\n', loc.peek() + 1);
} else {
next = string.indexOf('\n', next + 1);
}
if (next >= string.length()) next = -1;
}
return loc;
}

How to change First letter of each word to Uppercase in Textview xml

i need to change the text="font roboto regular" to Font Roboto Regular in xml itself, how to do?
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="18sp"
android:textColor="#android:color/black"
android:fontFamily="roboto-regular"
android:text="font roboto regular"
android:inputType="textCapWords"
android:capitalize="words"/>
If someone looking for kotlin way of doing this, then code becomes very simple and beautiful.
yourTextView.text = yourText.split(' ').joinToString(" ") { it.capitalize() }
You can use this code.
String str = "font roboto regular";
String[] strArray = str.split(" ");
StringBuilder builder = new StringBuilder();
for (String s : strArray) {
String cap = s.substring(0, 1).toUpperCase() + s.substring(1);
builder.append(cap + " ");
}
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText(builder.toString());
Try this...
Method that convert first letter of each word in a string into an uppercase letter.
private String capitalize(String capString){
StringBuffer capBuffer = new StringBuffer();
Matcher capMatcher = Pattern.compile("([a-z])([a-z]*)", Pattern.CASE_INSENSITIVE).matcher(capString);
while (capMatcher.find()){
capMatcher.appendReplacement(capBuffer, capMatcher.group(1).toUpperCase() + capMatcher.group(2).toLowerCase());
}
return capMatcher.appendTail(capBuffer).toString();
}
Usage:
String chars = capitalize("hello dream world");
//textView.setText(chars);
System.out.println("Output: "+chars);
Result:
Output: Hello Dream World
KOTLIN
val strArrayOBJ = "Your String".split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val builder = StringBuilder()
for (s in strArrayOBJ) {
val cap = s.substring(0, 1).toUpperCase() + s.substring(1)
builder.append("$cap ")
}
txt_OBJ.text=builder.toString()
Modification on the accepted answer to clean out any existing capital letters and prevent the trailing space that the accepted answer leaves behind.
public static String capitalize(#NonNull String input) {
String[] words = input.toLowerCase().split(" ");
StringBuilder builder = new StringBuilder();
for (int i = 0; i < words.length; i++) {
String word = words[i];
if (i > 0 && word.length() > 0) {
builder.append(" ");
}
String cap = word.substring(0, 1).toUpperCase() + word.substring(1);
builder.append(cap);
}
return builder.toString();
}
you can use this method to do it programmatically
public String wordFirstCap(String str)
{
String[] words = str.trim().split(" ");
StringBuilder ret = new StringBuilder();
for(int i = 0; i < words.length; i++)
{
if(words[i].trim().length() > 0)
{
Log.e("words[i].trim",""+words[i].trim().charAt(0));
ret.append(Character.toUpperCase(words[i].trim().charAt(0)));
ret.append(words[i].trim().substring(1));
if(i < words.length - 1) {
ret.append(' ');
}
}
}
return ret.toString();
}
refer this if you want to do it in xml.
You can use
private String capitalize(final String line) {
return Character.toUpperCase(line.charAt(0)) + line.substring(1);
}
refer this How to capitalize the first character of each word in a string
android:capitalize is deprecated.
Follow these steps: https://stackoverflow.com/a/31699306/4409113
Tap icon of ‘Settings’ on the Home screen of your Android Lollipop
Device
At the ‘Settings’ screen, scroll down to the PERSONAL section and
tap the ‘Language & input’ section.
At the ‘Language & input’ section, select your keyboard(which is
marked as current keyboard).
Now tap the ‘Preferences’.
Tap to check the ‘Auto – Capitalization’ to enable it.
And then it should work.
If it didn't, i'd rather to do that in Java.
https://stackoverflow.com/a/1149869/2725203
Have a look at ACL WordUtils.
WordUtils.capitalize("your string") == "Your String"
Another approach is to use StringTokenizer class. The below method works for any number of words in a sentence or in the EditText view. I used this to capitalize the full names field in an app.
public String capWordFirstLetter(String fullname)
{
String fname = "";
String s2;
StringTokenizer tokenizer = new StringTokenizer(fullname);
while (tokenizer.hasMoreTokens())
{
s2 = tokenizer.nextToken().toLowerCase();
if (fname.length() == 0)
fname += s2.substring(0, 1).toUpperCase() + s2.substring(1);
else
fname += " "+s2.substring(0, 1).toUpperCase() + s2.substring(1);
}
return fname;
}
in kotlin, string extension
fun String?.capitalizeText() = (this?.toLowerCase(Locale.getDefault())?.split(" ")?.joinToString(" ") { if (it.length <= 1) it else it.capitalize(Locale.getDefault()) }?.trimEnd())?.trim()
Kotlin extension function for capitalising each word
val String?.capitalizeEachWord
get() = (this?.lowercase(Locale.getDefault())?.split(" ")?.joinToString(" ") {
if (it.length <= 1) it else it.replaceFirstChar { firstChar ->
if (firstChar.isLowerCase()) firstChar.titlecase(
Locale.getDefault()
) else firstChar.toString()
}
}?.trimEnd())?.trim()
As the best way for achieving this used to be the capitalize() fun, but now it got depricated in kotlin. So we have an alternate for this. I've the use case where I'm getting a key from api that'll be customized at front end & will be shown apparently. The value is coming as "RECOMMENDED_OFFERS" which should be updated to be shown as "Recommended Offers".
I've created an extension function :
fun String.updateCapitalizedTextByRemovingUnderscore(specialChar: String): String
that takes a string which need to be replaced with white space (" ") & then customise the words as their 1st character would be in caps. So, the function body looks like :
fun String.updateCapitalizedTextByRemovingUnderscore(
specialChar: String = "") : String {
var tabName = this
// removing the special character coming in parameter & if
exist
if (spclChar.isNotEmpty() && this.contains(specialChar)) {
tabName = this.replace(spclChar, " ")
}
return tabName.lowercase().split(' ').joinToString(" ") {
it.replaceFirstChar { if (it.isLowerCase())
it.titlecase(Locale.getDefault()) else it.toString() } }
}
How to call the extension function :
textView.text =
"RECOMMENDED_OFFERS".updateCapitalizedTextByRemovingUnderscore("_")
OR
textView.text = <api_key>.updateCapitalizedTextByRemovingUnderscore("_")
The desired output will be :
Recommended Offers
Hope this will help.Happy coding :) Cheers!!
capitalize each word
public static String toTitleCase(String string) {
// Check if String is null
if (string == null) {
return null;
}
boolean whiteSpace = true;
StringBuilder builder = new StringBuilder(string); // String builder to store string
final int builderLength = builder.length();
// Loop through builder
for (int i = 0; i < builderLength; ++i) {
char c = builder.charAt(i); // Get character at builders position
if (whiteSpace) {
// Check if character is not white space
if (!Character.isWhitespace(c)) {
// Convert to title case and leave whitespace mode.
builder.setCharAt(i, Character.toTitleCase(c));
whiteSpace = false;
}
} else if (Character.isWhitespace(c)) {
whiteSpace = true; // Set character is white space
} else {
builder.setCharAt(i, Character.toLowerCase(c)); // Set character to lowercase
}
}
return builder.toString(); // Return builders text
}
use String to txt.setText(toTitleCase(stringVal))
don't use android:fontFamily to roboto-regular. hyphen not accept. please rename to roboto_regular.
To capitalize each word in a sentence use the below attribute in xml of that paticular textView.
android:inputType="textCapWords"

Combining Spannable with String.format()

Suppose you have the following string:
String s = "The cold hand reaches for the %1$s %2$s Ellesse's";
String old = "old";
String tan = "tan";
String formatted = String.format(s,old,tan); //"The cold hand reaches for the old tan Ellesse's"
Suppose you want to end up with this string, but also have a particular Span set for any word replaced by String.format.
For instance, we also want to do the following:
Spannable spannable = new SpannableString(formatted);
spannable.setSpan(new StrikethroughSpan(), oldStart, oldStart+old.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(new ForegroundColorSpan(Color.BLUE), tanStart, tanStart+tan.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Is there a robust way of getting to know the start indices of old and tan?
Note that just searching for 'old' returns the 'old' in 'cold', so that won't work.
What will work, I guess, is searching for %[0-9]$s beforehand, and calculating the offsets to account for the replacements in String.format. This seems like a headache though, I suspect there might be a method like String.format that is more informative about the specifics of its formatting. Well, is there?
I have created a version of String.format that works with spannables. Download it and use it just like the normal version. In your case you would put the spans around the format specifiers (possibly using strings.xml). In the output, they would be around whatever those specifiers were replaced with.
Using Spannables like that is a headache -- this is probably the most straightforward way around:
String s = "The cold hand reaches for the %1$s %2$s Ellesse's";
String old = "<font color=\"blue\">old</font>";
String tan = "<strike>tan</strike>";
String formatted = String.format(s,old,tan); //The cold hand reaches for the <font color="blue">old</font> <strike>tan</strike> Ellesse's
Spannable spannable = Html.fromHtml(formatted);
Problem: this does not put in a StrikethroughSpan. To make the StrikethroughSpan, we borrow a custom TagHandler from this question.
Spannable spannable = Html.fromHtml(text,null,new MyHtmlTagHandler());
MyTagHandler:
public class MyHtmlTagHandler implements Html.TagHandler {
public void handleTag(boolean opening, String tag, Editable output,
XMLReader xmlReader) {
if (tag.equalsIgnoreCase("strike") || tag.equals("s")) {
processStrike(opening, output);
}
}
private void processStrike(boolean opening, Editable output) {
int len = output.length();
if (opening) {
output.setSpan(new StrikethroughSpan(), len, len, Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, StrikethroughSpan.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != len) {
output.setSpan(new StrikethroughSpan(), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
private Object getLast(Editable text, Class kind) {
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
} else {
for (int i = objs.length; i > 0; i--) {
if (text.getSpanFlags(objs[i - 1]) == Spannable.SPAN_MARK_MARK) {
return objs[i - 1];
}
}
return null;
}
}
}
I made simple Kotlin extension function that should solve String.format spanned issue:
fun Context.getStringSpanned(#StringRes resId: Int, vararg formatArgs: Any): Spanned {
var lastArgIndex = 0
val spannableStringBuilder = SpannableStringBuilder(getString(resId, *formatArgs))
for (arg in formatArgs) {
val argString = arg.toString()
lastArgIndex = spannableStringBuilder.indexOf(argString, lastArgIndex)
if (lastArgIndex != -1) {
(arg as? CharSequence)?.let {
spannableStringBuilder.replace(lastArgIndex, lastArgIndex + argString.length, it)
}
lastArgIndex += argString.length
}
}
return spannableStringBuilder
}
I needed to replace %s placeholders in a String by a set of Spannables, and didn't find anything satisfying enough, so I implemented my own formatter as a kotlin String extension. Hopes it helps anyone.
fun String.formatSpannable(vararg spans: CharSequence?): Spannable {
val result = SpannableStringBuilder()
when {
spans.size != this.split("%s").size - 1 ->
Log.e("formatSpannable",
"cannot format '$this' with ${spans.size} arguments")
!this.contains("%s") -> result.append(this)
else -> {
var str = this
var spanIndex = 0
while (str.contains("%s")) {
val preStr = str.substring(0, str.indexOf("%s"))
result.append(preStr)
result.append(spans[spanIndex] ?: "")
str = str.substring(str.indexOf("%s") + 2)
spanIndex++
}
if (str.isNotEmpty()) {
result.append(str)
}
}
}
return result
}
and then usage as follows
"hello %s kotlin %s world".formatSpannable(span0, span1)
I had the same issue pop up, but found a really great solution here:
Android: How to combine Spannable.setSpan with String.format?
Check out the solution offered by #george-steel. He created a custom version of String.format which preserves spans.
Example use:
Spanned toDisplay = SpanFormatter.format(getText(R.string.foo), bar, baz, quux);

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>"}}

Categories

Resources