I have html text that I need to display in TextView. The html may look like this -
<font color="#AFEEEE"><font style="background-color: rgb(255,140,0);">Text with background and color</font></font>
Html.fromHtml doesn't support any attribute other than color for font tag. But we absolutely must show the background. I could write a custom tag handler but the attributes are not passed in, only the tag is passed in.
What is the best way to achieve this ?
NOTE : Cant use Webview.
I tried the code below. If I set raw on the text, it works, but if i process it further and pass it to Html.fromHtml, it doesnt show the background.
public static final String sText =
"Background on <font style=\"background-color: rgb(255,255,0);\">pa</font>rt text only";
Pattern pattern = Pattern.compile(BACKGROUND_PATTERN);
Matcher matcher = pattern.matcher(sText);
SpannableString raw = new SpannableString(sText);
BackgroundColorSpan[] spans =
raw.getSpans(0, raw.length(), BackgroundColorSpan.class);
for (BackgroundColorSpan span : spans) {
raw.removeSpan(span);
}
while (matcher.find()) {
raw.setSpan(new BackgroundColorSpan(0xFF8B008B),
matcher.start(2), matcher.start(2) + matcher.group(2).length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
sText = raw.toString();
final Spanned convertedHtml =
Html.fromHtml(sText, ig, new myTagHandler());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
String str = "<span style=\"background-color:#f3f402;\">" + TEXT TO HIGHLIGHT + "</span>";
textView.setText(Html.fromHtml(str, Html.FROM_HTML_MODE_LEGACY));
} else {
String str = "<font color='#f3f402'>" + TEXT TO HIGHLIGHT + "</font>";
textView.setText(Html.fromHtml(str));
}
More - https://stackoverflow.com/a/46035856/3625510
Add your own BackgroundColorSpan as you see fit.
Here is some code that sets such a span on all occurrences of a search term within a TextView:
private void searchFor(String text) {
TextView prose=(TextView)findViewById(R.id.prose);
Spannable raw=new SpannableString(prose.getText());
BackgroundColorSpan[] spans=raw.getSpans(0,
raw.length(),
BackgroundColorSpan.class);
for (BackgroundColorSpan span : spans) {
raw.removeSpan(span);
}
int index=TextUtils.indexOf(raw, text);
while (index >= 0) {
raw.setSpan(new BackgroundColorSpan(0xFF8B008B), index, index
+ text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
index=TextUtils.indexOf(raw, text, index + text.length());
}
prose.setText(raw);
}
So, find your beginning and ending points, create a BackgroundSpan with your desired color, and use setSpan() to apply it.
Note that this assumes that only part of your text needs the background color. If the entire TextView needs the color, go with njzk2's suggestion, and just apply the color to the whole TextView.
Related
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 the following requirement:
i> User enters a string in an EditText.
e.g: aaaaaaa bbbbbbbbb ccccccccccccc
ii> User now selects the substring "bbbbbbbbb" and adds a bold style to the substring.
Selection of the substring is done with the following code:
EditText content = (EditText) layout
.findViewById(R.id.txt_content);
int startSelection = content.getSelectionStart();
int endSelection = content.getSelectionEnd();
Now , applying the bold style to the substring , the following code has been added to reflect the style on the EditText:
final SpannableStringBuilder str1 = new SpannableStringBuilder(content.getText().toString());
str1.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), startSelection, endSelection, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
content.setText(str1);
iii> Now the user modifies the original string.
Lets assume the new string to be
e.g: aaaaaaa bbbbbbbbb ccccccccccccc ddddddddd
Now user wants to apply underline style on the substring "ddddddddd"
Now , before underline , the current text is fetched from the EditText using the following code:
content.getText()
The issue is the text fetched using content.getText() doesnot contain the BOLD style which has been applied on the substring "bbbbbbbbb".
Basically the requirement is to apply different styles(bold/italic) on individual characters/words in a sentence by selection. Modification(addition / removal of characters/words) of the sentence is also possible.
How to resolve the issue ?
EditText content = (EditText) layout.findViewById(R.id.txt_content);
int startSelection = content.getSelectionStart();
int endSelection = content.getSelectionEnd();
// important. dont use getText()
Spannable sb = new SpannableString( content.toString() );
/** for bold for example. substract the endselection from startselection to get the length **/
sb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), startSelection, endSelection - startSelection, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //bold
/** do more styles ...**/
sb.setSpan(new UnderlineSpan(), 20, 30, 0);
content.setText(sb);
to show this in TextView
textview.setText(sb);
You can also grab all Assigned Styles with:
StyleSpan[] mSpans = content.getText().getSpans(0, content.length(), StyleSpan.class);
and read the different styles by checking the class.
for (StyleSpan mSpan : mSpans) {
if (mSpan instanceof StyleSpan) {
int start = content.getSpanStart(mSpan);
int end = content.getSpanEnd(mSpan);
int flag = content.getSpanFlags(mSpan);
Log.i("SpannableString Spans", "Found StyleSpan at:\n" +
"Start: " + start +
"\n End: " + end +
"\n Flag(s): " + flag);
}
}
You know if you are not appending text you don't have to use SpannableStringBuilder you can use a Spannable text. Anyways I think your problem is your converting your text to String.
Spannable spannable = new SpannableString(mEditText.getText());
spannable.setSpan(new StyleSpan(Typeface.BOLD),
mEditText.getSelectionStart(),
mEditText.getSelectionEnd(),
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
mEditText.setText(spannable);
But if your code is going to overlap styles you will have to handle it in a different way.
I know how to do this in iOS but not yet in android. How do I construct an attributed string in android where a portion is bold as in
"this is the example with a bold portion"
FWIW, I have never seen AttributedString used, in ~6.5 years of Android development work.
A class implementing Spanned contains markup rules ("spans"). The simplest way to construct one dynamically is by using Html.fromHtml() to parse an HTML string with basic tags like <b>. String resources (e.g., res/values/strings.xml) also support <b>, <i>, and <u> tags.
Or, you can apply spans yourself. In the sample code that follows, I get the CharSequence from a TextView, remove all existing spans, and highlight search terms with a BackgroundColorSpan:
private void searchFor(String text) {
TextView prose=(TextView)findViewById(R.id.prose);
Spannable raw=new SpannableString(prose.getText());
BackgroundColorSpan[] spans=raw.getSpans(0,
raw.length(),
BackgroundColorSpan.class);
for (BackgroundColorSpan span : spans) {
raw.removeSpan(span);
}
int index=TextUtils.indexOf(raw, text);
while (index >= 0) {
raw.setSpan(new BackgroundColorSpan(0xFF8B008B), index, index
+ text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
index=TextUtils.indexOf(raw, text, index + text.length());
}
prose.setText(raw);
}
(from this sample project)
For boldface or italics, you would use StyleSpan instead of BackgroundColorSpan, and so forth.
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 .
I am developing an application in which there will be a search screen
where user can search for specific keywords and that keyword should be
highlighted. I have found Html.fromHtml method.
But I will like to know whether its the proper way of doing it or
not.
Please let me know your views on this.
Or far simpler than dealing with Spannables manually, since you didn't say that you want the background highlighted, just the text:
String styledText = "This is <font color='red'>simple</font>.";
textView.setText(Html.fromHtml(styledText), TextView.BufferType.SPANNABLE);
Using color value from xml resource:
int labelColor = getResources().getColor(R.color.label_color);
String сolorString = String.format("%X", labelColor).substring(2); // !!strip alpha value!!
Html.fromHtml(String.format("<font color=\"#%s\">text</font>", сolorString), TextView.BufferType.SPANNABLE);
This can be achieved using a Spannable String. You will need to import the following
import android.text.SpannableString;
import android.text.style.BackgroundColorSpan;
import android.text.style.StyleSpan;
And then you can change the background of the text using something like the following:
TextView text = (TextView) findViewById(R.id.text_login);
text.setText("");
text.append("Your text here");
Spannable sText = (Spannable) text.getText();
sText.setSpan(new BackgroundColorSpan(Color.RED), 1, 4, 0);
Where this will highlight the charecters at pos 1 - 4 with a red color. Hope this helps!
Alternative solution: Using a WebView instead. Html is easy to work with.
WebView webview = new WebView(this);
String summary = "<html><body>Sorry, <span style=\"background: red;\">Madonna</span> gave no results</body></html>";
webview.loadData(summary, "text/html", "utf-8");
String name = modelOrderList.get(position).getName(); //get name from List
String text = "<font color='#000000'>" + name + "</font>"; //set Black color of name
/* check API version, according to version call method of Html class */
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.N) {
Log.d(TAG, "onBindViewHolder: if");
holder.textViewName.setText(context.getString(R.string._5687982) + " ");
holder.textViewName.append(Html.fromHtml(text));
} else {
Log.d(TAG, "onBindViewHolder: else");
holder.textViewName.setText("123456" + " "); //set text
holder.textViewName.append(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); //append text into textView
}
font is deprecated use span instead Html.fromHtml("<span style=color:red>"+content+"</span>")
To make part of your text underlined and colored
in your strings.xml
<string name="text_with_colored_underline">put the text here and <u><font color="#your_hexa_color">the underlined colored part here<font><u></string>
then in the activity
yourTextView.setText(Html.fromHtml(getString(R.string.text_with_colored_underline)));
and for clickable links:
<string name="text_with_link"><![CDATA[<p>text before linktitle of link.<p>]]></string>
and in your activity:
yourTextView.setText(Html.fromHtml(getString(R.string.text_with_link)));
yourTextView.setMovementMethod(LinkMovementMethod.getInstance());
First Convert your string into HTML then convert it into spannable. do as suggest the following codes.
Spannable spannable = new SpannableString(Html.fromHtml(labelText));
spannable.setSpan(new ForegroundColorSpan(Color.parseColor(color)), spannable.toString().indexOf("•"), spannable.toString().lastIndexOf("•") + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textview.setText(Html.fromHtml("<font color='rgb'>"+text contain+"</font>"));
It will give the color exactly what you have made in html editor , just set the textview and concat it with the textview value. Android does not support span color, change it to font color in editor and you are all set to go.
Adding also Kotlin version with:
getting text from resources (strings.xml)
getting color from resources (colors.xml)
"fetching HEX" moved as extension
fun getMulticolorSpanned(): Spanned {
// Get text from resources
val text: String = getString(R.string.your_text_from_resources)
// Get color from resources and parse it to HEX (RGB) value
val warningHexColor = getHexFromColors(R.color.your_error_color)
// Use above string & color in HTML
val html = "<string>$text<span style=\"color:#$warningHexColor;\">*</span></string>"
// Parse HTML (base on API version)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY)
} else {
Html.fromHtml(html)
}
}
And Kotlin extension (with removing alpha):
fun Context.getHexFromColors(
colorRes: Int
): String {
val labelColor: Int = ContextCompat.getColor(this, colorRes)
return String.format("%X", labelColor).substring(2)
}
Demo