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"
In my textview, I want both anchors and raw links to be clickable. I'm letting TextView.setAutoLinkMask(Linkify.ALL) handle all of the raw links and then retrieving the spans and setting them with SpannableString for all of the anchors.
I would like to be able to 'linkify' the anchors in my custom TextView implementation (extends TextView) in the setText(CharSequence text, BufferType type) call. Everything seems to be working properly except for the setSpan() call (end, start, and span are all correct). At the end of setText(...), I'm assigning the global CharSequence var to the new SpannableString. When setText() is called from my adapter, the spans identified in Object[]spans are not linked.
#Override
public void setText(CharSequence text, BufferType type) {
Spanned html = Html.fromHtml(text.toString().replace("\n", "<br />"));
Object[] spans = html.getSpans(0, html.length(), URLSpan.class);
SpannableString s = new SpannableString(html);
for (int i = 0; i < spans.length; i++) {
URLSpan span = (URLSpan) spans[i];
int end = html.getSpanEnd(span);
int start = html.getSpanStart(span);
int flags = html.getSpanFlags(span);
Log.i(LOG_TAG, "span: " + span.getURL() + "\nstart: " + start + "\nend: " + end);
s.setSpan(span, start, end, flags);
}
mText = s;
}
I found a slight workaround to this issue in the code listed below. In this implementation, I'm setting the spans in the adapter instead of the CustomTextView. This method works fine but doesn't entirely fit my needs because my TextView is used as an ExpandableTextView, meaning that there are 2 sets of texts (trimmed and full) and often times the trimmed version is returned (which is fine in typical scenarios, except that I want to add tags to the full text). This implementation often crashes because the spans that were originally identified were for the fullText and getText() is returning the trimmedText. So I think it'll be necessary for me to be able to do this in my custom TextView's setText(). BUT, the below method does work granted that trimmedText and fullText are the same length.
Spanned html = Html.fromHtml(postText.replace("\n", "<br />"));
Object[] spans = html.getSpans(0, html.length(), URLSpan.class);
h.content.setAutoLinkMask(Linkify.ALL);
h.content.setMovementMethod(LinkMovementMethod.getInstance());
h.content.setText(html);
h.content.setLinkTextColor(Color.rgb(136, 194, 226));
h.content.setVisibility(View.VISIBLE); // Need this otherwise the view disappears....
SpannableString ss = (SpannableString) h.content.getText();
for (int i = 0; i < spans.length; i++) {
URLSpan span = (URLSpan) spans[i];
int end = html.getSpanEnd(span);
int start = html.getSpanStart(span);
int flags = html.getSpanFlags(span);
ss.setSpan(span, start, end, flags);
}
Here's how I solved my problem:
At the point of my problem, I had already parsed through the content and added anchors (HTML links that would launch an activity when clicked) to my tags (# and #). I also wanted to make raw (e.g., www.google.com) clickable, so I tried setting my TextView to setAutoLinkMask(Linkify.ALL) but this broke the anchor links.
So this is what I did:
SpannableStringBuilder ssb =
new SpannableStringBuilder(Html.fromHtml
(this.content.replace("\n", "<br />")));
Pattern urlPattern = android.util.Patterns.WEB_URL;
Linkify.addLinks(ssb, urlPattern, "youractivityhere://");
myTextView.setText(ssb);
Basically I'm using a regex matcher to find any URLs within the String and then add a link using Linkify.addLinks(...).
I was making the problem way more difficult than needed.
I have paragraph long stories that contain spanned strings with mostly regular text and a few bolded text (1 or 2 words) here and there. It's in an Edittext. I want to be able to search through that spanned string in the editText and save each bolded text to a string. I'm not sure how to do that though. Any suggestions?
If you're using SpannableString, you can use the getSpans method:
StyleSpan[] spans = ss.getSpans(0, ss.length(), StyleSpan.class);
List<String> boldedWords = new ArrayList<String>();
for(StyleSpan span : spans) {
if(span.getStyle() & Typeface.BOLD) {
int start = ss.getSpanStart(span);
int end = ss.getSpanEnd(span);
boldedWords.add(ss.subSequence(start, end).toString());
}
}
spanned = Html.fromHtml("<sup>aaa</sup>bbb<sub>ccc</sub><b>ddd</b>");
Will create a Spanned object with with 3 spans aaa, ccc, ddd.
bbb is being ignored since it's not inside an html tag,
spans = spanned.getSpans(0, spanned.length(), Object.class);
will only identify 3 spans.
I need a way to extract all the 4 sections of the code, if possible into some sort of an array that will allow to me to identify the the type of each span.
I need a way to extract all the 4 sections of the code
Use nextSpanTransition() to find the starting point of the next span. The characters between your initial position (first parameter to nextSpanTransition()) and the next span represent an unspanned portion of text.
You can take a look at the source code to the toHtml() method on the Html class to see this in action.
'bbb' is the one which is not inside html tag. Even though i guess it will not be missed. 'ccc' is a subscript, may be it is rendered but not visible to you. Try to increase textview height if you have constrained it.
use this http://developer.android.com/reference/android/text/Html.html#fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler), pass null for ImageGetter and your custom TagHandler
see the example
String source = "<b>bold</b> <i>italic</i> <unk>unknown</unk>";
TagHandler tagHandler = new TagHandler() {
Stack<Integer> starts = new Stack<Integer>();
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if (tag.equals("unk")) {
if (opening) {
starts.add(output.length());
} else {
int start = starts.pop();
int end = output.length();
Object what = new Object();
output.setSpan(what, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
};
Spanned spanned = Html.fromHtml(source, null, tagHandler);
TextUtils.dumpSpans(spanned, new LogPrinter(Log.DEBUG, TAG), "span ");
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 .