Spannablestring with Image Example

SpannableString with Image example

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);
}

Android - ImageSpan - How to center align the image at the end of the text

You can try my CenteredImageSpan.
You can customize in draw method by calculating transY -= (paint.getFontMetricsInt().descent / 2 - 8);. (Good luck. :) )

public class CenteredImageSpan extends ImageSpan {
private WeakReference<Drawable> mDrawableRef;

// Extra variables used to redefine the Font Metrics when an ImageSpan is added
private int initialDescent = 0;
private int extraSpace = 0;

public CenteredImageSpan(Context context, final int drawableRes) {
super(context, drawableRes);
}

public CenteredImageSpan(Drawable drawableRes, int verticalAlignment) {
super(drawableRes, verticalAlignment);
}

@Override
public int getSize(Paint paint, CharSequence text,
int start, int end,
Paint.FontMetricsInt fm) {
Drawable d = getCachedDrawable();
Rect rect = d.getBounds();

// if (fm != null) {
// Paint.FontMetricsInt pfm = paint.getFontMetricsInt();
// // keep it the same as paint's fm
// fm.ascent = pfm.ascent;
// fm.descent = pfm.descent;
// fm.top = pfm.top;
// fm.bottom = pfm.bottom;
// }

if (fm != null) {
// Centers the text with the ImageSpan
if (rect.bottom - (fm.descent - fm.ascent) >= 0) {
// Stores the initial descent and computes the margin available
initialDescent = fm.descent;
extraSpace = rect.bottom - (fm.descent - fm.ascent);
}

fm.descent = extraSpace / 2 + initialDescent;
fm.bottom = fm.descent;

fm.ascent = -rect.bottom + fm.descent;
fm.top = fm.ascent;
}

return rect.right;
}

@Override
public void draw(@NonNull Canvas canvas, CharSequence text,
int start, int end, float x,
int top, int y, int bottom, @NonNull Paint paint) {
Drawable b = getCachedDrawable();
canvas.save();

// int drawableHeight = b.getIntrinsicHeight();
// int fontAscent = paint.getFontMetricsInt().ascent;
// int fontDescent = paint.getFontMetricsInt().descent;
// int transY = bottom - b.getBounds().bottom + // align bottom to bottom
// (drawableHeight - fontDescent + fontAscent) / 2; // align center to center

int transY = bottom - b.getBounds().bottom;
// this is the key
transY -= (paint.getFontMetricsInt().descent / 2 - 8);

// int bCenter = b.getIntrinsicHeight() / 2;
// int fontTop = paint.getFontMetricsInt().top;
// int fontBottom = paint.getFontMetricsInt().bottom;
// int transY = (bottom - b.getBounds().bottom) -
// (((fontBottom - fontTop) / 2) - bCenter);


canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}


// Redefined locally because it is a private member from DynamicDrawableSpan
private Drawable getCachedDrawable() {
WeakReference<Drawable> wr = mDrawableRef;
Drawable d = null;

if (wr != null)
d = wr.get();

if (d == null) {
d = getDrawable();
mDrawableRef = new WeakReference<>(d);
}

return d;
}
}

EDIT

I implemented above code like this:

Drawable myIcon = getResources().getDrawable(R.drawable.btn_feedback_yellow);
int width = (int) Functions.convertDpToPixel(75, getActivity());
int height = (int) Functions.convertDpToPixel(23, getActivity());
myIcon.setBounds(0, 0, width, height);
CenteredImageSpan btnFeedback = new CenteredImageSpan(myIcon, ImageSpan.ALIGN_BASELINE);
ssBuilder.setSpan(
btnFeedback, // Span to add
getString(R.string.text_header_answer).length() - 1, // Start of the span (inclusive)
getString(R.string.text_header_answer).length(), // End of the span (exclusive)
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);// Do not extend the span when text add later

Android Custom text View To hold images and text

Here is the solution I implemented.

Spanned spanned = null;
String messageCustomized = "<img src ='"+ WebConstant.IMAGE_BASE_URL +
part +"'/>";//WebConstant.IMAGE_BASE_URL + part;
Spanned span = Html.fromHtml(messageCustomized, new
URLImageParser(sentMessagesViewHolder.tvMessage, context), null);
if (spanned!=null) {
spanned = (Spanned) TextUtils.concat(spanned, span);
}else spanned= span;
if (spanned!=null) {
txtView.setText(spanned);
}

Image Getter

public class URLImageParser implements ImageGetter {
Context context;
View container;
private int imageSize = 20;
private int imageSizeDisplaySize = 20;
URLDrawable urlDrawable = null;

public URLImageParser(View container, Context context) {
this.context = context;
this.container = container;
imageSize = Utility.convertDpTopPixels(context, 20);
imageSizeDisplaySize = Utility.convertDpTopPixels(context, 35);

}

@Override
public Drawable getDrawable(final String url) {

String[] arr = url.split("/");
final String fileName = arr[arr.length - 1];
urlDrawable = new URLDrawable();
Drawable drawable = null;
if (Build.VERSION.SDK_INT >= 21)
drawable =
context.getDrawable(R.drawable.profile_main_placeholder);
else
drawable = context.getResources().getDrawable(R.drawable.profile_main_placeholder);

drawable.setBounds(0, 0, 0 + imageSize, 0 + imageSize);
urlDrawable.drawable = drawable;

Bitmap bitmap = null;
bitmap = ImageUtility.getImageFromSDCard(fileName);
if (bitmap != null) { // the bitmap is available

bitmap = RoundedImageView.getCroppedBitmap(bitmap, imageSize, imageSize, imageSize);
drawable = new BitmapDrawable(context.getResources(), bitmap);//ImageUtility.bitmapToDrawable(context,resource);
drawable.setBounds(0, 0, 0 + imageSize, 0 + imageSize); //set the correct bound according to the result from HTTP call
URLImageParser.this.urlDrawable.drawable = drawable;

} else
Glide.with(context)
.load(url)
.asBitmap()
.transform(new CircleTransform(context))
.override(imageSizeDisplaySize, imageSizeDisplaySize)
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
// you can do something with loaded bitmap here
// .....
Drawable drawable = new BitmapDrawable(context.getResources(), resource);//ImageUtility.bitmapToDrawable(context,resource);
drawable.setBounds(0, 0, 0 + imageSize, 0 + imageSize); //set the correct bound according to the result from HTTP call
URLImageParser.this.urlDrawable.drawable = drawable;
URLImageParser.this.container.invalidate();
ImageUtility.saveImageToSDCard(resource, fileName);

}
});
return urlDrawable.drawable; //return reference to URLDrawable where We will change with actual image from the src tag
//}
}
}

Custom Bitmap Drawable

public class URLDrawable extends BitmapDrawable {
// the drawable that you need to set, you could set the initial drawing
// with the loading image if you need to
protected Drawable drawable;

@Override
public void draw(Canvas canvas) {
// override the draw to facilitate refresh function later
if(drawable != null) {
drawable.draw(canvas);
}
}
}

Combining an Image with Text inside a Button

You can use ImageSpan to add a drawable in between text. Below is an example .

  TextView textView=(TextView)findViewById(R.id.b1);
Spannable span = new SpannableString("G OL");
Drawable android = getResources().getDrawable(R.mipmap.ic_launcher);
android.setBounds(0, 0, 50,50);
ImageSpan image = new ImageSpan(android, ImageSpan.ALIGN_BASELINE);
span.setSpan(image, 1, 2, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setText(span);

Below is the output . You can modify the bounds.
Sample Image

Image doesn't display in SpannableString with Image

create a SpannableStringBuilder to append multiple imagespan and text

SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(edittext.getText());
builder.append(getFilterWithIcon("100", R.drawable.ic_feature_bed_s));

and finally set

editText.setText(builder);

Note though SpannableStringBuilder is an immutable class it can't be changed.

Android : Adding an imageview right at the end of a textview via ImageSpan and make the image clickable

Here is a more complete answer to your question. I use a drawable resource, but you can create a drawable from your bitmap. See the following code for a description. This code can be placed into the onCreate() of your main activity (or elsewhere.)

Sample Image

// Get a string that can be spanned with an ImageSpan and a ClickableSpan.
SpannableString ss = new SpannableString("Some text ");
// This will be the image. I use a resource here, but there are constructors that can
// accept a Drawable. See BitmapDrawable at https://developer.android.com/reference/android/graphics/drawable/BitmapDrawable#BitmapDrawable(android.content.res.Resources,%20android.graphics.Bitmap)

ImageSpan imgSpan = new ImageSpan(this, R.drawable.ic_baseline_airplanemode_active_24, ImageSpan.ALIGN_CENTER);
ClickableSpan cs = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
Toast.makeText(MainActivity.this, "Clicked!", Toast.LENGTH_SHORT).show();
}
};

// The image will overlay the last blank in the text.
ss.setSpan(imgSpan, ss.length() - 1, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// The clickable span will overlay the image from the ImageSpan.
ss.setSpan(cs, ss.length() - 1, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// Just a TextView.
TextView myView = findViewById(R.id.myView);
myView.setText(ss);
// Prevent the ImageSpan from showing a highlight on click.
myView.setHighlightColor(getResources().getColor(android.R.color.transparent));
// Make sure the TextView can be clicked.
myView.setMovementMethod(LinkMovementMethod.getInstance());


Related Topics



Leave a reply



Submit