A ListView in my application has many string elements like name, experience, date of joining, etc. I just want to make name bold. All the string elements will be in a single TextView.
my XML:
<ImageView
android:id="#+id/logo"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="15dp" >
</ImageView>
<TextView
android:id="#+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/logo"
android:padding="5dp"
android:textSize="12dp" >
</TextView>
My code to set the TextView of the ListView item:
holder.text.setText(name + "\n" + expirience + " " + dateOfJoininf);
Let's say you have a TextView called etx. You would then use the following code:
final SpannableStringBuilder sb = new SpannableStringBuilder("HELLOO");
final StyleSpan bss = new StyleSpan(android.graphics.Typeface.BOLD); // Span to make text bold
final StyleSpan iss = new StyleSpan(android.graphics.Typeface.ITALIC); //Span to make text italic
sb.setSpan(bss, 0, 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make first 4 characters Bold
sb.setSpan(iss, 4, 6, Spannable.SPAN_INCLUSIVE_INCLUSIVE); // make last 2 characters Italic
etx.setText(sb);
Based on Imran Rana's answer, here is a generic, reusable method if you need to apply StyleSpans to several TextViews, with support for multiple languages (where indices are variable):
void setTextWithSpan(TextView textView, String text, String spanText, StyleSpan style) {
SpannableStringBuilder sb = new SpannableStringBuilder(text);
int start = text.indexOf(spanText);
int end = start + spanText.length();
sb.setSpan(style, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(sb);
}
Use it in an Activity like so:
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
StyleSpan boldStyle = new StyleSpan(Typeface.BOLD);
setTextWithSpan((TextView) findViewById(R.id.welcome_text),
getString(R.string.welcome_text),
getString(R.string.welcome_text_bold),
boldStyle);
// ...
}
strings.xml
<string name="welcome_text">Welcome to CompanyName</string>
<string name="welcome_text_bold">CompanyName</string>
Result:
Welcome to CompanyName
You can do it using Kotlin and buildSpannedString extension function from core-ktx
holder.textView.text = buildSpannedString {
bold { append("$name\n") }
append("$experience $dateOfJoining")
}
The answers provided here are correct, but can't be called in a loop because the StyleSpan object is a single contiguous span (not a style that can be applied to multiple spans). Calling setSpan multiple times with the same bold StyleSpan would create one bold span and just move it around in the parent span.
In my case (displaying search results), I needed to make all instances of all the search keywords appear bold. This is what I did:
private static SpannableStringBuilder emboldenKeywords(final String text,
final String[] searchKeywords) {
// searching in the lower case text to make sure we catch all cases
final String loweredMasterText = text.toLowerCase(Locale.ENGLISH);
final SpannableStringBuilder span = new SpannableStringBuilder(text);
// for each keyword
for (final String keyword : searchKeywords) {
// lower the keyword to catch both lower and upper case chars
final String loweredKeyword = keyword.toLowerCase(Locale.ENGLISH);
// start at the beginning of the master text
int offset = 0;
int start;
final int len = keyword.length(); // let's calculate this outside the 'while'
while ((start = loweredMasterText.indexOf(loweredKeyword, offset)) >= 0) {
// make it bold
span.setSpan(new StyleSpan(Typeface.BOLD), start, start+len, SPAN_INCLUSIVE_INCLUSIVE);
// move your offset pointer
offset = start + len;
}
}
// put it in your TextView and smoke it!
return span;
}
Keep in mind that the code above isn't smart enough to skip double-bolding if one keyword is a substring of the other. For example, if you search for "Fish fi" inside "Fishes in the fisty Sea" it will make the "fish" bold once and then the "fi" portion. The good thing is that while inefficient and a bit undesirable, it won't have a visual drawback as your displayed result will still look like
Fishes in the fisty Sea
if you don't know exactly the length of the text before the text portion that you want to make Bold, or even you don't know the length of the text to be Bold, you can easily use HTML tags like the following:
yourTextView.setText(Html.fromHtml("text before " + "<font><b>" + "text to be Bold" + "</b></font>" + " text after"));
<string name="My_Name">Given name is <b>Not Right</b>Please try again </string>
use "b" tag in string.xml file.
also for Italic "i" and Underline "u".
Extending frieder's answer to support case and diacritics insensitivity.
public static String stripDiacritics(String s) {
s = Normalizer.normalize(s, Normalizer.Form.NFD);
s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
return s;
}
public static void setTextWithSpan(TextView textView, String text, String spanText, StyleSpan style, boolean caseDiacriticsInsensitive) {
SpannableStringBuilder sb = new SpannableStringBuilder(text);
int start;
if (caseDiacriticsInsensitive) {
start = stripDiacritics(text).toLowerCase(Locale.US).indexOf(stripDiacritics(spanText).toLowerCase(Locale.US));
} else {
start = text.indexOf(spanText);
}
int end = start + spanText.length();
if (start > -1)
sb.setSpan(style, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(sb);
}
If you are using the # srings / your_string annotation, access the strings.xml file and use the <b></b> tag in the part of the text you want.
Example:
<string><b>Bold Text</b><i>italic</i>Normal Text</string>
I recommend to use strings.xml file with CDATA
<string name="mystring"><![CDATA[ <b>Hello</b> <i>World</i> ]]></string>
Then in the java file :
TextView myTextView = (TextView) this.findViewById(R.id.myTextView);
myTextView.setText(Html.fromHtml( getResources().getString(R.string.mystring) ));
To better support translations and remove any dependency on length of the string or particular index, you should use android.text.Annotation in you string defined strings.xml.
In your particular case, you can create a string like below
<string name="bold_name_experience_text"><annotation type="bold">name</annotation> \nexpirience dateOfJoininf</string>
or if you want to substitute these in runtime, you can create a string as follow
<string name="bold_name_experience_text"><annotation type="bold">name</annotation> \n%d %s</string>
You must apply this bold_name_experience_text in your text view label. These annotation class spans get added to your string and then you can iterate on them to apply the bold span.
You can refer to my SO answer which shows the Kotlin code to iterate through these spans and apply the bold span
Remember all the above answers has one of the following flows:
They are using some hard-coded index logic which may crash or give wrong results in some other language
They are using hardcode string in Java code which will result in lots of complicated logic to maintain internalisation
Some used Html.fromHtml which can be acceptable answer depending on the use-case. As Html.fromHtml doesn't always work for all types of HTML attributes for example there is not support of click span. Also depending on OEM you might get different rendered TextView
Related
I have this code
TextView text1 = (TextView) view.findViewById(R.layout.myLayout);
Spanned myBold = (Html.fromHtml("<b>Test<b>", Html.FROM_HTML_MODE_LEGACY));
If I do
text1.setText(myBold);
Then myBold is in bold,which is ok. But when I want to add a string more, like
text1.setText(myBold+"bla");
Then the whole TextView is not bold anymore. Why does the new String "bla" affect this?
Thanks.
Why does the new String "bla" affect this?
Because what you are really doing is:
text1.setText(myBold.toString() + "bla");
A String has no style information. A Spanned object does.
Use TextUtils.concat() instead:
text1.setText(TextUtils.concat(myBold, "bla"));
A better choice would be to use a Bold StyleSpan. In the next sample only the "hello" world will be set to bold by using such technique:
Java:
final SpannableString caption = new SpannableString("hello world");
// Set to bold from index 0 to the length of 'hello'
caption.setSpan(new StyleSpan(Typeface.BOLD), 0, "hello".length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
yourTextView.setText(caption);
Kotlin:
yourTextView.text = SpannableString("hello world").apply {
// Set to bold from index 0 to the length of 'hello'
setSpan(StyleSpan(Typeface.BOLD), 0, "hello".length, Spannable.SPAN_INCLUSIVE_EXCLUSIVE))
}
This would be a more optimal solution rather than using the Html.fromHtml technicque, as it doesn't have to go through the overhead of parsing/interpreting the HTML tags.
In addition, it allows you to combine more styles, sizes, etc, in the same SpannableString.
Lets say I have two textviews, a title, and details, the details is toEndOf the title (same line), and while the title is one line, the details, can be multiple, my question is, how can I configure the details textview that when it starts a new line, instead of aligning it to the previous row of the textview, align it to the title textview, hence creating a paragraph feel.
Please see these screenshot to understand what I'm trying to accomplish (the title is the bold text, details it the rest):
Desired:
What I have so far:
The only solution I can think of is using one textview for both, and format the text with HTML, but what if I need a bigger text size for the title?
Thanks!
You can use SpannableText
This will do the job!
<TextView
..
android:id="#+id/text_view"
/>
...
TextView text=(TextView)findViewById(R.id.text_view);
String head = "Seat (s):";
String body = " The baby name means the meaning of the nam";
setTextWithSpan(text,head+body,head, body,new android.text.style.StyleSpan(android.graphics.Typeface.BOLD));
Custom method
public void setTextWithSpan(TextView textView, String text, String spanTextBold,String secondPartOfText,StyleSpan style) {
SpannableStringBuilder sb = new SpannableStringBuilder(text);
int start = text.indexOf(spanTextBold);
int end = start + spanTextBold.length();
sb.setSpan(style, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
//sb.setSpan(new ForegroundColorSpan(Color.BLUE), start, end ,0); // if you need a color
int startTwo = text.indexOf(secondPartOfText);
int endTwo = startTwo + secondPartOfText.length();
// sb.setSpan(new StyleSpan(Typeface.ITALIC),startTwo,endTwo , 0);
sb.setSpan(new RelativeSizeSpan(0.8f), startTwo, endTwo, 0);
textView.setText(sb);
}
Yes, As you mentioned, I also see One TextView with different strings sizes/colors for texts inside.
To achieve this you need to use SpannableString - Docs link
Also Check this answer for details
Use SpannableText for your issue to separate two textviews.
I woduld like to make all of links in textview clickable.
The example text is:
"All three should link out http://google.com and here link and http://google.com"
If I use MovementMethod with the html text, only second and third link is clickable.
If I use Linkify(or mix both) only first and second link is clickable.
How can I make all of them clickable?
After invesigation I found that Linkify.addLinks() method remove current spans from text and apply new once (based on eg web page url). Because of that my spans from Html.fromHtml() was deleted at the beginning and never applay again.
So I did following:
1. Read thext from htmml Html.fromHtml which gives me Spanned obj with html spans.
2. Save spans from html in array
3. Make linkify.addLinks - this method remove my old spans so I will have to add it back
4. Add old spans
5. Set text to the textview.
Implementation:
private void setLabel(){
label.setText(linkifyHTML(Html.fromHtml("text with links here"));
label.setMovementMethod(LinkMovementMethod.getInstance());
label.setLinkTextColor(getRes().getColor(R.color.link));
}
private Spannable linkifyHTML(CharSequence text) {
Spannable s = new SpannableString(text);
URLSpan[] old = s.getSpans(0, s.length(), URLSpan.class);
LinkSpec oldLinks[] = new LinkSpec[old.length];
for (int i = 0; i < old.length; i++) {
oldLinks[i] = new LinkSpec(old[i], s.getSpanStart(old[i]), s.getSpanEnd(old[i]));
}
Linkify.addLinks(s, Linkify.ALL);
for (LinkSpec span : oldLinks) {
s.setSpan(span.span, span.start, span.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return s;
}
class LinkSpec {
final URLSpan span;
final int start, end;
public LinkSpec(URLSpan urlSpan, int spanStart, int spanEnd) {
span = urlSpan;
start = spanStart;
end = spanEnd;
}
}
You have to use the backslash \ to scape " character so the string will not consider it as the final point of the string. I mean, a string is considered when all the text is inside two "". You have to scape " characters in your url because if not the string will consider that it has to end when he find a new " character, in this case in your url.
"All three should link out http://google.com and here link and http://google.com"
I am using 2 parts in a textview, 1st part is date another is name and email.
They are both referenced in the same textview. I would like to change the color of the date to give it a different visual it from name and email. is it possible to do this without actually adding a whole new textview for name and email?
Here's my code so far:
String nameandemail;
holder.mytext.setText(String.valueOf(dateFormat.format(new Date(msg.getDate())) + " " + nameandemail + ": "));
How do I make it such that I can set the color of date with
holder.mytext.setTextColor(Color.white) and for the nameandemail string something like green?
Thanks!
You can Use spans.
final SpannableStringBuilder sb = new SpannableStringBuilder("your text here");
// Set text color to some RGB value
final ForegroundColorSpan fcs = new ForegroundColorSpan(Color.rgb(158, 158, 158));
// Make text bold
final StyleSpan bss = new StyleSpan(android.graphics.Typeface.BOLD);
// Set the text color for first 6 characters
sb.setSpan(fcs, 0, 6, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
// make them also bold
sb.setSpan(bss, 0, 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(sb);
You can also use html like below
myTextView.setText(Html.fromHtml(text + "<font color=white>" + some_text + "</font><br><br>"
+ some_text));
you could define a String in your strings.xml file
<string name="test2"><font color=\'#FFFFFF\'>%1$s</font> -- <font color=\'#00FF00\'>%2$s</font></string>
and then programmatically
TextView tv = (TextView) findViewById(R.id.test);
tv.setText(Html.fromHtml(getString(R.string.test2, String.valueOf(dateFormat.format(new Date(msg.getDate())), nameandemail)));
My recommendation would be to use Spannable.
Here is a short utils method I wrapped up for you to use. You simply need to pass your TextView, your full text and the single part to be re-colored from the full text.
You can place this method to a Utils class and call it whenever you want, or keep it in a single Activity or Fragment(or wherever else) if you use it in a single class:
public static void colorText(TextView view, final String fullText, final String whiteText) {
if (fullText.length() < whiteText.length()) {
throw new IllegalArgumentException("'fullText' parameter should be longer than 'whiteText' parameter ");
}
int start = fullText.indexOf(whiteText);
if (start == -1) {
return;
}
int end = start + whiteText.length();
SpannableStringBuilder finalSpan = new SpannableStringBuilder(fullText);
// finalSpan.setSpan(new ForegroundColorSpan(ContextCompat.getColor(view.getContext(),R.color.your_own_color_code)), start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
finalSpan.setSpan(new ForegroundColorSpan(Color.WHITE), start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
view.setText(finalSpan);
}
I have a database search query which search in the database for a word entered by the user and return a Cursor.
In my ListActivity, I have a ListView which will hold the items (the Cursor items). The ListView items layout is basically a TextView. I mean, the ListView will be a list of TextView's.
What I want is to highlight the search term wherever it appears in the TextView. I mean by highlighting: different color or different background color or anything makes it different than the rest of the text.
Is this possible? and how?
Update:
cursor = myDbHelper.search(term); //term: a word entered by the user.
cursor.moveToFirst();
String[] columns = {cursor.getColumnName(1)};
int[] columnsLayouts = {R.id.item_title}; //item_title: the TextView holding the one raw
ca = new SimpleCursorAdapter(this.getBaseContext(), R.layout.items_layout, cursor,columns , columnsLayouts);
lv = getListView();
lv.setAdapter(ca);
For #Shailendra: The search() method will return some titles. I want to highlight the words in those titles that matches the term word. I hope this is clear now.
insert HTML code for color around word and set it to your textView .
like
String newString = oldString.replaceAll(textToHighlight, "<font color='red'>"+textToHighlight+"</font>");
textView.setText(Html.fromHtml(newString));
TextView textView = (TextView)findViewById(R.id.mytextview01);
//use a loop to change text color
Spannable WordtoSpan = new SpannableString("partial colored text");
WordtoSpan.setSpan(new ForegroundColorSpan(Color.BLUE), 2, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(WordtoSpan);
The numbers 2 and 4 are start/stop indexes for the coloring of the text, in this example "rti" would be colored.
So you would basically just find the starting index of your searching word in the title:
int startIndex = titleText.indexOf(term);
int stopIndex = startIndex + term.length();
and then replace the numbers 2 and 4 with the indexes and "partial colored text" with your title string.
source: https://stackoverflow.com/a/10279703/2160827
More Easy Way
You can use Spannable class for highlighting/formatting part of Text.
textView.setText("Hello, I am Awesome, Most Awesome"); // set text first
setHighLightedText(textView, "a"); // highlight all `a` in TextView
Here is the method.
/**
* use this method to highlight a text in TextView
*
* #param tv TextView or Edittext or Button (or derived from TextView)
* #param textToHighlight Text to highlight
*/
public void setHighLightedText(TextView tv, String textToHighlight) {
String tvt = tv.getText().toString();
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);
}
}
}
You can check this answer for clickable highlighted text.
I know it's old question but i have created a method to highlight a repeated-word in string\paragraph.
private Spannable highlight(int color, Spannable original, String word) {
String normalized = Normalizer.normalize(original, Normalizer.Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
int start = normalized.indexOf(word);
if (start < 0) {
return original;
} else {
Spannable highlighted = new SpannableString(original);
while (start >= 0) {
int spanStart = Math.min(start, original.length());
int spanEnd = Math.min(start+word.length(), original.length());
highlighted.setSpan(new ForegroundColorSpan(color), spanStart,
spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
start = normalizedText.indexOf(word, spanEnd);
}
return highlighted;
}
}
usage:
textView.setText(highlight(primaryColor, textAll, wordToHighlight));
Based on the previous answers I developed the following function, you can copy/paste it
private void highlightMask(TextView textView, String text, String mask) {
boolean highlightenabled = true;
boolean isHighlighted = false;
if (highlightenabled) {
if (!TextUtils.isEmpty(text) && !TextUtils.isEmpty(mask)) {
String textLC = text.toLowerCase();
mask = mask.toLowerCase();
if (textLC.contains(mask)) {
int ofe = textLC.indexOf(mask, 0);
Spannable wordToSpan = new SpannableString(text);
for (int ofs = 0; ofs < textLC.length() && ofe != -1; ofs = ofe + 1) {
ofe = textLC.indexOf(mask, ofs);
if (ofe == -1) {
break;
} else {
// set color here
wordToSpan.setSpan(new BackgroundColorSpan(0xFFFFFF00), ofe, ofe + mask.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(wordToSpan, TextView.BufferType.SPANNABLE);
isHighlighted = true;
}
}
}
}
}
if (!isHighlighted) {
textView.setText(text);
}
}
I haven't done it but this looks promising:
http://developer.android.com/reference/android/text/SpannableString.html
http://developer.android.com/guide/topics/resources/string-resource.html
public final void setText (CharSequence text)
Since: API Level 1 Sets the string value of the TextView. TextView
does not accept HTML-like formatting, which you can do with text
strings in XML resource files. To style your strings, attach
android.text.style.* objects to a SpannableString, or see the
Available Resource Types documentation for an example of setting
formatted text in the XML resource file.
http://developer.android.com/reference/android/widget/TextView.html
Try this library Android TextHighlighter.
Implementations
TextView.setText() gets a parameter as Spannable not only CharacterSequence. SpannableString has a method setSpan() which allows applying styles.
See list of direct subclass form CharacterStyle https://developer.android.com/reference/android/text/style/CharacterStyle.html
example of giving background color and foreground color for word "Hello" in "Hello, World"
Spannable spannable = new SpannableString("Hello, World");
// setting red foreground color
ForegroundSpan fgSpan = new ForegroundColorSpan(Color.red);
// setting blue background color
BackgroundSpan bgSpan = new BackgroundColorSPan(Color.blue);
// setSpan requires start and end index
// in our case, it's 0 and 5
// You can directly set fgSpan or bgSpan, however,
// to reuse defined CharacterStyle, use CharacterStyle.wrap()
spannable.setSpan(CharacterStyle.wrap(fgSpan), 0, 5, 0);
spannable.setSpan(CharacterStyle.wrap(bgSpan), 0, 5, 0);
// apply spannableString on textview
textView.setText(spannable);
You do so in xml strings if your strings are static
<string name="my_text">This text is <font color='red'>red here</font></string>
I know this thread is old, but just in case anyone is looking to highlight strings in a textview, I have created a library that does exactly this. This is my first answer to a question on stack overflow, as I have just joined, hopefully it's formatted properly and relevant. It uses SpannableString and will locate all occurrences of a string you specify. Additionally, a custom ClickableSpan is built in giving you the option to set up listeners for text clicked if desired.
Linker
Lightweight android library for highlighting Strings inside of a textview (ignoring case), with optional callbacks.
Language: Java
MinSDK: 17
An image of it's functionality and all of the code can be found
here.
JavaDocs
To bring into your android project implement the artifact:
In the Project level build.gradle
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
In the App level build.gradle
dependencies {
implementation 'com.github.Gaineyj0349:Linker:1.2'
}
How to use:
1 - Construct a Linker object with a textview:
Linker linker = new Linker(textView);
2 - Add an array or a list of strings to be highlighted within the textview's text:
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
linker.addStrings(list);
AND/OR
String[] words = new String[]{"One", "Two", "Three"};
linker.addStrings(words);
3 - Add a callback: (this is optional):
linker.setListener(new LinkerListener() {
#Override
public void onLinkClick(String charSequenceClicked) {
// charSequenceClicked is the word that was clicked
Toast.makeText(MainActivity.this, charSequenceClicked, Toast.LENGTH_SHORT).show();
}
});
4 - Call the linker's update method to commit customization and rollout the setup.:
linker.update();
You always have the option to add Strings to the linker object, just make sure you call the update method after to refresh the spans.
linker.addStrings("yoda");
linker.update();
If you need a fresh slate with same linker object just call
linker.clearLinksList()
You can customize the links also:
1 - Customize all the link colors:
linker.setAllLinkColors(Color.BLUE);
2 - Customize link underlines:
linker.setAllLinkUnderline(false);
3 - If you wish to customize a color or underline setting for a certain string (note the string must already be added to the linker):
linker.setLinkColorForCharSequence("world", Color.MAGENTA);
linker.setUnderlineModeForCharSequence("world", true);
4 - If you wish to use different setups for every word then you can also give the linker object a list or array of LinkProfiles:
ArrayList<LinkProfile> profiles = new ArrayList<>();
profiles.add(new LinkProfile("hello world",
Color.GREEN, false));
profiles.add(new LinkProfile("goodbye cruel world",
Color.RED, false));
profiles.add(new LinkProfile("Whoa awesome!",
Color.CYAN, true));
linker.addProfiles(profiles);
Just remember to call .update() after any additions to the linker object.
Note that the library will take care of subtleties like adding two of the same words, or same parts of a word. For example if "helloworld" and "hello" are two of the words added to the linker, "helloworld" will be given preference over "hello" when they are in the same span of characters. The linker will sort according to larger words first and trace all spans as it links them - avoiding the issue of duplication as well as intersecting spans.
Licensed under MIT license .