SpannableString with Image example - android

I am looking for an example of how to build and display Android SpannableString with ImageSpan. Something like inline display of smileys.
Thanks a lot.

Found the following and it seems to do the job:
public class TestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView textView = (TextView) findViewById(R.id.textview);
SpannableString ss = new SpannableString("abc");
Drawable d = ContextCompat.getDrawable(this, R.drawable.icon32);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
ss.setSpan(span, 0, 3, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setText(ss);
}

SpannableString + ImageSpan don't work in Android API 21 & 22 (I tested in Android Studio 1.2.1.1 in emulator), but if you do this:
TextView textView = (TextView) findViewById(R.id.textview);
textView.setTransformationMethod(null);
...
textView.setText(ss);
SpannableString + ImageSpan will work.
I was inspired by this post: https://stackoverflow.com/a/26959656/3706042

If anyone is still interested, I've created the a Java method to allow adding recursively a listed drawables to a text (set at the end to a textView) based on a "string to replace".
public void appendImages(#NonNull TextView textView,
#NonNull String text,
#NonNull String toReplace,
Drawable... drawables){
if(drawables != null && drawables.length > 0){
//list of matching positions, if any
List<Integer> positions = new ArrayList<>();
int index = text.indexOf(toReplace);
while (index >= 0) {
//add position
positions.add(index);
index = text.indexOf(toReplace, index + toReplace.length());
}
if(positions.size() > 0 && drawables.length >= positions.size()){
textView.setTransformationMethod(null);
SpannableString ss = new SpannableString(text);
int drawablesIndex = 0;
for(int position : positions){
Drawable drawable = drawables[drawablesIndex++];
//mandatory for Drawables
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
ss.setSpan(new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE), position, position+toReplace.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
textView.setText(ss);
}
else Timber.w("The amount of matches to replace is %s and the number of drawables to apply is %s", positions.size(), drawables.length);
}
else Timber.w("The drawables array is null or empty.");
}
Usage:
appendImages(myTextView, "This is a ^ simple ^ test", "^", drawable1, drawable2);

Related

How to create overlapped image span?

I want to create a SpannableString with some emotions and text like this image.
Below is the method that I have used so for but it just attach emotions with text. Please suggest me how can I create such types of view.
private SpannableStringBuilder getLikeCountString(UserFeedData feedData, Context mContext) {
SpannableStringBuilder builder = new SpannableStringBuilder();
int emotionSize = (int) mContext.getResources().getDimension(R.dimen.emotion_size);
if (feedData.getEmotionTypes() != null) {
String[] emotions = feedData.getEmotionTypes().split(",");
for (String emotion : emotions) {
SpannableString emojSpan = new SpannableString(" ");
// Getting image based on emotion id
Drawable icon = ContextCompat.getDrawable(mContext, ContentDetailFragment.getLikeEmotionResource(Integer.valueOf(emotion.trim())));
//icon.setBounds(0, 0, (icon.getIntrinsicWidth() / 2) + 5, (icon.getIntrinsicHeight() / 2) + 5);
icon.setBounds(0, 0, emotionSize, emotionSize);
ImageSpan imageSpan = new ImageSpan(icon, ImageSpan.ALIGN_BOTTOM);
emojSpan.setSpan(imageSpan, 0, emojSpan.length() - 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
builder.append(emojSpan);
}
}
if (feedData.getContentLikes() > 0) {
builder.append(feedData.getContentLikes() + " ");
}
return builder;
}
Instead of using individual ImageSpans for each emoji, use one ImageSpan with LayerDrawable inside, one emoji per layer. You can do all kinds of overlaps with LayerDrawable.

how set margin between spannable textview?

I am trying to create spannable textview and showing it in EditText. So user can type something in EditText and if user pressed enter button of keyboard then i am converting this text in to spannable textview after this user can start typing again and press enter button of keyboard then again second spannable textview will create ans will show it in edittext but.
Where i stuck?
when i create two spannable textview then this two textview slightly overlapping on each other. And i want to set margin between this two textview.
I also tried to set margin between textview using LayoutParam but not success.
Here is image which showing textview overlapping on each other in EditText.
y of spicy is hidden below tasty
Here is my code.
txtDishTags.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH
|| actionId == EditorInfo.IME_ACTION_NEXT
|| actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_GO) {
txtDishTags.dismissDropDown();
if(txtDishTags.getText().toString().trim().length()>=1){
isEntered = true;
String[] separated = tags.split(",");
tags = separated[separated.length-1];
if(tags.trim().length()>=1){
TextView tv = createContactTextView(tags);
BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(tv);
bd.setBounds(-20, 0, bd.getIntrinsicWidth(),
bd.getIntrinsicHeight());
sb.append(tags + ",");
sb1 = new SpannableStringBuilder();
sb1.append(tags + ",");
sb.setSpan(new ImageSpan(bd),
sb.length() - tags.length(), sb.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.setSpan(clickSpan, sb.length() - tags.length(),
sb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
txtDishTags.setText("");
txtDishTags.setText(sb);
int length = sb.length();
txtDishTags.setSelection(length, length);
}
}
}
return false;
}
});
public TextView createContactTextView(String text) {
//llp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 44);
//llp.setMargins(5, 0, 20, 0);
TextView tv = new TextView(this);
tv.setText(text);
tv.setTextSize(30);
Typeface faceBook = Typeface.createFromAsset(getAssets(),
"fonts/eau_sans_book.otf");
tv.setTypeface(faceBook);
tv.setTextColor(getResources().getColor(R.color.backgroundcolor));
tv.setBackgroundResource(R.color.textviewbubble);
//tv.setLayoutParams(llp);
Resources r = getResources();
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
44, r.getDisplayMetrics());
tv.setHeight(px);
return tv;
}
public static Object convertViewToDrawable(View view) {
int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
view.measure(spec, spec);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(),
view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
c.translate(-view.getScrollX(), -view.getScrollY());
view.draw(c);
view.setDrawingCacheEnabled(true);
Bitmap cacheBmp = view.getDrawingCache();
Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
view.destroyDrawingCache();
return new BitmapDrawable(viewBmp);
}
I tried to set margin between textview using following code
llp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 44);
llp.setMargins(5, 0, 20, 0);
tv.setLayoutParams(llp);
I also set LeftPadding for Textview but seems first textview not getting it.Even i set height to textview but seems textview not getting layout parameter at all. Like
int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
44, r.getDisplayMetrics());
tv.setHeight(px);
Please give reference or hint.
Thanks in advance
There are a few problems that I have identified. You need to make the specified changes and everything should work.
Step 1) Update setBounds parameters
In the following line, update the setBounds parameters from -20 to 0 as follows:
BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(tv);
bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());
This is important because you are setting the wrong bounds, which causes tags to overlap.
Step 2) Fix bug in sb.setSpan
If you followed step 1, and you run the code, you will realize that when you attempt to replace text with ImageSpan, you are passing the wrong values (you are not taking into account the ","(comma) character in the end). Update the following line to include -1:
sb.setSpan(new ImageSpan(bd), sb.length() - tags.length() - 1, sb.length() - 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Now you output will appear correct and with comma in the middle.
Step 3) Add spacing between Tags
To answer your original question, how to add spacing, I would recommend that you modify your code to include ", " between different spans. You can also modify it to use just " " space. Define a contentBetweenTags variable and set it to your desired value. Here is how you can do that:
String contentBetweenTags = ", ";
sb.append(tags + contentBetweenTags);
sb1 = new SpannableStringBuilder();
sb1.append(tags + contentBetweenTags);
sb.setSpan(new ImageSpan(bd),
sb.length() - tags.length() - contentBetweenTags.length(),
sb.length() - contentBetweenTags.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Step 4) Picking the right "space" character
Just in case you are not happy with the "margin/spacing" between two tags, you could use one of the many unicode space characters available. They have different widths and you could use any one of them based on your desire / liking.
Here is the final code and sample screenshot using unicode \u2002:
BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(tv);
bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());
String contentBetweenTags = ",\u2002";
sb.append(tags + contentBetweenTags);
sb1 = new SpannableStringBuilder();
sb1.append(tags + contentBetweenTags);
sb.setSpan(new ImageSpan(bd),
sb.length() - tags.length() - contentBetweenTags.length(),
sb.length() - contentBetweenTags.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

is Spannable text customizable? Android

String aux = getInserzionista(offerta.getIdInserzionista());
sotto_titolo.setText("Offerta dal " + aux);
int inizio = 12;
int fine = 11+aux.length();
sotto_titolo.setMovementMethod(LinkMovementMethod.getInstance());
sotto_titolo.setText(sotto_titolo.getText().toString(),BufferType.SPANNABLE);
Spannable mySpannable = (Spannable) sotto_titolo.getText();
ClickableSpan myClickableSpan = new ClickableSpan() {
#Override
public void onClick(View widget) {
}
};
//if i put this, not work
mySpannable.setSpan(new ForegroundColorSpan(Color.RED), inizio, fine, 0);
mySpannable.setSpan(myClickableSpan, inizio, fine + 1,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
But if i put this:
mySpannable.setSpan(new ForegroundColorSpan(Color.RED), 0, 4, 0);
It works, because the text from 0 to 4 is colored!
So, my question is:
How can I change the color of the link (the one colored blue and underlined)?
Thanks
because you set a static value 4 in this line mySpannable.setSpan(new ForegroundColorSpan(Color.RED), 0, 4, 0);. Set text length in place of 4.
Have you tried using updateDrawState()?

display smiley in textview and edittext in android

hello i am developing chat application in which i want to insert smiley
i have not much idea about it how to integrate and display in it
can u give me suggestion for doing the same ?
ImageGetter imageGetter = new ImageGetter() {
public Drawable getDrawable(String source) {
Drawable d = getResources().getDrawable(
R.drawable.happy);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
return d;
}
};
cs = Html.fromHtml(
"<img src='"
+ getResources()
.getDrawable(R.drawable.happy)
+ "'/>", imageGetter, null);
System.out.println("cs is:- " + cs);
edttxtemoji.setText(cs);
i found this code, in this it uses images, is this feasible ?
or there is another solutions ?
please give me better solution for this thanx in advance
Yes there is another way for showing smiley within the TextView or EditText. Build a Spannable text using ImageSpanand then setText the Spannable to TextView or EditText. Here is an post for the same.
To set Smiley in edittext
int value=R.id.ic_launcher;
Drawable Smiley = getResources().getDrawable(value);
Smiley.setBounds(0, 0, 15, 15);
SpannableStringBuilder builder = new SpannableStringBuilder();
String imgValue = "["+value+"]";
builder.append(imgValue);
builder.setSpan(new ImageSpan(Smiley), builder.length()-imgValue.length(), builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
edit_text.getText().insert(txt.getSelectionStart(), builder);
now to fetch smiley in listview or textview
textview.setText(addSmileySpans(context,edit_text.getText()));
public CharSequence addSmileySpans(Context context, CharSequence msg) {
SpannableStringBuilder builder = new SpannableStringBuilder(your_recieved_message);
Pattern pattern = Pattern.compile("\\[([^\\[\\]]+)\\]");
if( pattern != null )
{
Matcher matcher = pattern.matcher( your_recieved_message );
int matchesSoFar = 0;
while( matcher.find() )
{
CharSequence cs =matcher.group().subSequence(1, matcher.group().length()-1);
int value = Integer.parseInt(cs.toString());
System.out.println("pattern is::"+matcher.group().subSequence(1, matcher.group().length()-1));
int start = matcher.start() - (matchesSoFar * 2);
int end = matcher.end() - (matchesSoFar * 2);
Drawable Smiley = context.getResources().getDrawable(value);
Smiley.setBounds(0, 0,15,15);
builder.setSpan(new ImageSpan(Smiley), start + 1, end - 1, 0 );
builder.delete(start, start + 1);
builder.delete(end - 2, end -1);
matchesSoFar++;
}
}
return builder;
}
I think it is little bit late.
public void addSmily() {
CharSequence text = myEditText.getText();
int resource = R.drawable.ic_menu_emoticons ;
myEditText.setText(getSpannableText(text,resource));
}
private Spannable getSpannableText(CharSequence text, int smilyToAppend) {
Spannable spannable = Factory.getInstance().newSpannable(text+" ");
ImageSpan smilySpan = new ImageSpan(getApplicationContext(),smilyToAppend);
spannable.setSpan(smilySpan, spannable.length()-1, spannable.length(), 0);
return spannable;
}

How to insert ImageView at the end of multiline TextView?

Maybe it's a stupid question, but I cant find a way to inser small image at the end of second line of TextView. Image is always appears to the right of whole textview, not the line end.
I want to insert image like this:
TextTextTextTextTextTextTextTextTextText
TextTextTextText. <ImageView>
What I am getting is:
TextTextTextTextTextTextTextTextTextText <ImageView>
TextTextTextText.
I hope there is way to do it.
Src:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TESTESTESTESTESTESTESTESTESTESTESTESTES"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#FFFFFF" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/tv"
android:src="#drawable/icon" />
</RelativeLayout>
create an ImageSpan and add it to your textview. This way, you can put your image where you want in the text
Example -
ImageSpan imagespan = new ImageSpan(appContext, ressource);
text.setSpan(imagespan, i, i+ strLength, 0); //text is an object of TextView
One best way to complete it is as the following...
ImageGetter getter = new ImageGetter(){
public Drawable getDrawable(String source){ // source is the resource name
Drawable d = null;
Integer id = new Integer(0);
id = getApplicationContext().getResources().getIdentifier(source, "drawable", "com.sampleproject");
d = getApplicationContext().getResources().getDrawable(id);
if (d != null)
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
return d;
}
};
String imgString = this.getResources().getString(R.string.text_string) + " <img src=\"img_drawable_image\"/>";
((TextView)findViewById(R.id.textview_id)).setText(
Html.fromHtml(imgString, getter, null));
You can add the image to end of the string without using imageview like below;
fun addImageToEndOfTheString(text: String, drawableResourceId : Int ,context: Context) : SpannableStringBuilder {
val drawable = ContextCompat.getDrawable(context, drawableResourceId)!!
drawable.setBounds(14, 0, 64, 50)
val rocketImageSpan = ImageSpan(drawable, ImageSpan.ALIGN_BASELINE)
val ssBuilder = SpannableStringBuilder(text)
ssBuilder.setSpan(
rocketImageSpan,
text.length - 1,
text.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
return ssBuilder
}
and call it like;
textView.text = addImageToEndOfTheString("Your text is here", R.drawable.ic_your_image, context!!)
SpannableString ss = new SpannableString(title);
Drawable d = getResources().getDrawable(R.drawable.gallery_list);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
ss.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
newsTextView.setText(ss);
TextView textView =new TextView(this);
SpannableStringBuilder ssb = new SpannableStringBuilder( "Here's a smiley how are you " );
Bitmap smiley = BitmapFactory.decodeResource( getResources(), R.drawable.movie_add );
ssb.setSpan( new ImageSpan( smiley ), ssb.length()-1, ssb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE );
textView.setText( ssb, BufferType.SPANNABLE );
If you want to insert Drawable at the end of the text, Drawable is hiding the last character of the text to avoid that add another character at the end of the text and start the drawable at that character.
val myText = "Your text"
val span: Spannable = SpannableString(myText+"-")
val android: Drawable = ContextCompat.getDrawable(this, R.drawable.yourDrawable)!!
android.setBounds(0, 0, 30, 30)
val image = ImageSpan(android, ImageSpan.ALIGN_BOTTOM)
span.setSpan(image, span.indexOf("-"), span.indexOf("-")+1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
Kotlin extension on the TextView class
/**
* Creates a StringSpan using the text from the TextView itself and it also adds an ImageSpan at the
* end of the last line of the TextView.
* This basically allows us to set a drawable at the end of the TextView text.
*/
fun TextView.setImageSpanAtTheEnd(context: Context, #DrawableRes drawableRes: Int) {
text = SpannableString("$text ").apply {
val d = ContextCompat.getDrawable(context, drawableRes) ?: throw RuntimeException("Drawable not found!")
d.setBounds(0, 0, d.intrinsicWidth, d.intrinsicHeight)
setSpan(ImageSpan(d, ImageSpan.ALIGN_BASELINE), text.length, text.length+1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
}
}
Just call it after you set the text on the TextView and that's it.
It might be not the best solution, but you can try using Html.fromHtml to insert image into your line.
ImageGetter getter = new ImageGetter(){
public Drawable getDrawable(String source){
Drawable d = null;
context.getResources().getDrawable(Integer.parseInt(source));
if (d != null)
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
return d;
}
};
String imgString = <source string> + " <img src=\"R.drawable.icon\"/>";
((TextView)findViewById(R.id.tv)).setText(
Html.fromHtml(imgString, getter, null));
Thanks to Libin Thomas I created a solution for RatingBar. I used SvgRatingBar with SVG stars.
ImageSpan extends DynamicDrawableSpan, and DynamicDrawableSpan has only one child - ImageSpan. So, to attach another View to the end of the TextView we should get a drawable made from the View. I added a method getDrawable() to SvgRatingBar:
public class SvgRatingBar extends AppCompatRatingBar {
...
private Drawable drawable;
public Drawable getDrawable() {
return drawable;
}
private void init() {
LayerDrawable drawable = (LayerDrawable) createTile(getProgressDrawable(), false);
setProgressDrawable(drawable);
this.drawable = drawable;
}
Create a layout for the rating (layout_rating_bar.xml):
<?xml version="1.0" encoding="utf-8"?>
<com.example.myapplication.SvgRatingBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="11dp"
android:isIndicator="true"
android:minHeight="13dp"
android:numStars="5"
android:progressDrawable="#drawable/rating_bar"
android:rating="3.5"
android:stepSize="0.01" />
Then write this code:
val inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val ratingBar: SvgRatingBar = inflater.inflate(R.layout.layout_rating_bar, null, false)
as SvgRatingBar
ratingBar.rating = 3.5f
val drawable = ratingBar.drawable
val ss = SpannableString("dsfjdskljsf dsfjsdkljfslk dsfjlksdjflsdkjf ")
drawable.setBounds(0, 0, 170, 30) // Set width and height of the rating bar here.
val span = ImageSpan(drawable, ImageSpan.ALIGN_BASELINE)
ss.setSpan(span, ss.length - 1, ss.length, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
textView.setText(ss)

Categories

Resources