Reputation: 6630
I want to insert small pictures, like arrow icons for example, into certain positions of contents of a TextView
.
The photo below depicts exactly what I want:
Obviously, the most naive solution is to use multiple TextView
on either side of the small ImageView
objects. But this approach is not scalable.
I am curious to learn if someone has overcome this problem with some simple yet smart trick. (Maybe with help from HTML or an external library)
Any efficient solution is much appreciated.
Upvotes: 21
Views: 10372
Reputation: 35264
There was a similiar question a while back and someone came up with an awesome solution. I've just tweaked this one a little bit so that the image-size is always as tall as the line. So basically your icons will scale with the textSize.
Create a new Java class which extends TextView
public class TextViewWithImages extends TextView {
public TextViewWithImages(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public TextViewWithImages(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TextViewWithImages(Context context) {
super(context);
}
@Override
public void setText(CharSequence text, BufferType type) {
Spannable s = getTextWithImages(getContext(), text, this.getLineHeight());
super.setText(s, BufferType.SPANNABLE);
}
private static final Spannable.Factory spannableFactory = Spannable.Factory.getInstance();
private static boolean addImages(Context context, Spannable spannable, float height) {
Pattern refImg = Pattern.compile("\\Q[img src=\\E([a-zA-Z0-9_]+?)\\Q/]\\E");
boolean hasChanges = false;
Matcher matcher = refImg.matcher(spannable);
while (matcher.find()) {
boolean set = true;
for (ImageSpan span : spannable.getSpans(matcher.start(), matcher.end(), ImageSpan.class)) {
if (spannable.getSpanStart(span) >= matcher.start()
&& spannable.getSpanEnd(span) <= matcher.end()
) {
spannable.removeSpan(span);
} else {
set = false;
break;
}
}
String resName = spannable.subSequence(matcher.start(1), matcher.end(1)).toString().trim();
int id = context.getResources().getIdentifier(resName, "drawable", context.getPackageName());
Drawable mDrawable = context.getResources().getDrawable(id);
mDrawable.setBounds(0, 0, (int)height, (int)height);
if (set) {
hasChanges = true;
spannable.setSpan( new ImageSpan(mDrawable),
matcher.start(),
matcher.end(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
return hasChanges;
}
private static Spannable getTextWithImages(Context context, CharSequence text, float height) {
Spannable spannable = spannableFactory.newSpannable(text);
addImages(context, spannable, height);
return spannable;
}
}
Now in your layout-xml just use the TextViewWithImages class
<com.stacko.examples.TextViewWithImages
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="@string/my_string_with_icons" />
As you can see in the addImages(...)
function of the TextViewWithImages
class, a special pattern ([img src=my_icon/]
) within the string is used in order to add the images.
So here's a example:
<string name="my_string_with_icons">The [img src=ic_action_trash/] is used to delete an item while the [img src=ic_action_edit/] is to edit one.</string>
The output:
And as previously said it will scale with your textSize
:
As initially said most of this post is taken from 18446744073709551615 answer here. I think this should be published as a library since it's a common use-case to have images in the text. :<
Upvotes: 14
Reputation: 21461
An option is to use a WebView
instead of a TextView
. This also allows you to display images from your asset folder.
See Android Development: Using Image From Assets In A WebView's HTML for more details.
Upvotes: 1
Reputation: 572
You can create SpannableString and add any object to your string
TextView textView = (TextView) findViewById(R.id.textView);
ImageSpan imageSpan = new ImageSpan(this, R.drawable.ic_launcher);
SpannableString spannableString = new SpannableString(textView.getText());
int start = 3;
int end = 4;
int flag = 0;
spannableString.setSpan(imageSpan, start, end, flag);
textView.setText(spannableString);
Upvotes: 31