I have a list adapter to show content frm my social network, i need to put inside the text the img tags, i've tried using Html.fromhtml and it's formatting the text but instead of shows the img it shows a gray square.
How can i achieve that? I'v read about ImageGetter but i still don't have it very clear.
Thanks
You can draw an Image in TextView using the Html img tag with Html.ImageGetter. But make sure your image is available in resource drawable folder
Here is a sample , The image will be loaded from the resource.
String htmlText = "Hai <img src=\"ic_launcher\"> Hello";
textView.setText(Html.fromHtml(htmlText, new Html.ImageGetter() {
#Override
public Drawable getDrawable(String source) {
int resourceId = getResources().getIdentifier(source, "drawable",getPackageName());
Drawable drawable = getResources().getDrawable(resourceId);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
return drawable;
}
}, null));
String mIsi = (String) bundle.get("isi");
isi.setText(Html.fromHtml(mIsi, new Html.ImageGetter() {
#Override
public Drawable getDrawable(final String source) {
Picasso.get()
.load(source).into(imageView, new Callback() {
#Override
public void onSuccess() {
drawable = imageView.getDrawable();
drawable.setBounds(0, 0,
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
}
#Override
public void onError(Exception e) {
Log.v("test pic","err"+e.getMessage());
}
});
imageView.setVisibility(View.GONE);
return drawable;
}
}, null));
Related
In the string.xml file, I have Html text which has link and images, I have successfully shone the image store in my drawable folder, but showing the link which navigates from that activity to another is where I am having trouble
for setting text in text view
myTextView.setText(Html.fromHtml(getString(R.string.my_local_string), Html.FROM_HTML_MODE_COMPACT), new Html.ImageGetter() {
#Override
public Drawable getDrawable(String source) {
int id;
if (source.equals("myPic.jpg")) {
id = R.drawable.myPic;
}
Drawable d = getResources().getDrawable(id);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
return d;
}
}, null));
in string.xml file
<string name="my_local_string<[CDATA[<imgsrc="myPic.jpg"/<br/<br/MyLink<br/>MyAnotherLink<br/> ]]></string>
I have to detect click on both link
Parse the HTML string into SpannableStringBuilder, find the .. span using URLSpan, remove that span and replace it with your custom ClickableSpan. You can start the new activity in it's onClick. You'll need to do something along the following lines.
SpannableStringBuilder builder = (SpannableStringBuilder) Html.fromHtml(getString(R.string.my_local_string), Html.FROM_HTML_MODE_COMPACT, new Html.ImageGetter() {
#Override
public Drawable getDrawable(String source) {
int id;
if (source.equals("myPic.jpg")) {
id = R.drawable.myPic;
Drawable d = getResources().getDrawable(id);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
return d;
} else {
return null;
}
}
}, null);
URLSpan[] spans = builder.getSpans(0, builder.length(), URLSpan.class);
//Keep track of the links
int i=0;
for (URLSpan span : spans) {
int start = builder.getSpanStart(span);
int end = builder.getSpanEnd(span);
builder.removeSpan(span);
//Assign the value to a final variable so that it can be used inside the onClick
//to determine which link was clicked.
final int finalI = i;
builder.setSpan(new ClickableSpan() {
#Override
public void onClick(View v) {
Log.d("main", "link clicked");
String message;
if (finalI == 0) {
//First link
message = "First link clicked";
} else {
//Second link, and so on
message = "Second link clicked";
}
Toast.makeText(Main2Activity.this, message, Toast.LENGTH_SHORT).show();
} }, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
i++;
}
textView.setText(builder);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.TRANSPARENT);
<string name="my_local_string"><![CDATA[<imgsrc="myPic.jpg"/><br/><br/>MyLink<br/>MyAnotherLink<br/>]]></string>
I have to show text and images exactly as TextView holds. spannable object can be used but the problem is images are being downloaded from server at run time and have to display placeholder till images are downloaded..
So I am thinking of creating Custom TextView which extends a ViewGroup but then there would be a lot of handling. let me know if there is another best option available because I have shortage of time
This can be achieved by using SpannableString and ImageSpan classes. An instance of SpannableString can be created and can be set to TeaxtView.
Instance of SpannableString can contain combination of Text and Image. Here is a quick example I could find:
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 = getResources().getDrawable(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);
}
Have a look at this link as well
I think the easiest way to achieve this is to create a custom TextView that inside uses Html.fromHtml() to add the images and the text. You can then feed it the placeholder and when the images load you simply update with the new image. You wouldn't need to handle almost anything.
The stuff you pass to the TextView could be something of the sort:
CustomTextView(String text, List<...> plceholders)
With the text containing string placeholders for where the images should be fitted, something like "{img} test message {img}" and then a simple search and replace for {img} with the <img> tag should be enough.
You can find plenty of samples online about Html.fromHtml().
Also, N-JOY's Spannable String solution would work.
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);
}
}
}
i've been searching but got no results on adding custom images as emoji in android application.Thank you for any kind of help
Here is my problem :
I've trying to do a custom keyboard in android which includes only custom images as emoji,i manage to show image in keyboard but when i try to send it ,i get [OBJ] in return.
Here is some of my code , i tried many possible ways ;
String s = "<img src='a.png'/>";
// Spanned cs = Html.fromHtml("<img src='" + getResources().getDrawable(R.drawable.a) + "'/>", imageGetter, null);
/* ic.commitText( Html.fromHtml(s, new Html.ImageGetter() {
#Override public Drawable getDrawable(String source) {
Drawable drawFromPath;
int path =
emoji.this.getResources().getIdentifier(source, "drawable",
"com.cmosteknoloji.emoji");
drawFromPath = (Drawable) emoji.this.getResources().getDrawable(path);
drawFromPath.setBounds(0, 0, drawFromPath.getIntrinsicWidth(),
drawFromPath.getIntrinsicHeight());
return drawFromPath;
}
}, null),1);*/
Spanned spanned = Html.fromHtml(s, this, null);
ic.commitText(spanned,1);
#Override
public Drawable getDrawable(String arg0) {
// TODO Auto-generated method stub
int id = 0;
if(arg0.equals("a.png")){
id = R.drawable.a;
}
LevelListDrawable d = new LevelListDrawable();
Drawable empty = getResources().getDrawable(id);
d.addLevel(0, 0, empty);
d.setBounds(0, 0, empty.getIntrinsicWidth(), empty.getIntrinsicHeight());
return d;
}
ImageGetter imageGetter = new ImageGetter() {
#Override
public Drawable getDrawable(String source) {
byte [] encodeByte=Base64.decode("iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",Base64.DEFAULT);
Bitmap bitmap=BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.length);
Drawable d = new BitmapDrawable(bitmap);
Log.e("Chk", "Height : "+d.getIntrinsicHeight());
Log.e("Chk", "Width : " + d.getIntrinsicWidth());
return d;
}
};
How to compare two drawables, I am doing like this but not having any success
public void MyClick(View view)
{
Drawable fDraw = view.getBackground();
Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);
if(fDraw.equals(sDraw))
{
//Not coming
}
}
Update
https://stackoverflow.com/a/36373569/1835650
getConstantState() works not well
There is another way to compare:
mRememberPwd.getDrawable().getConstantState().equals
(getResources().getDrawable(R.drawable.login_checked).getConstantState());
mRemeberPwd is an ImageView in this example. If you're using a TextView, use getBackground().getConstantState instead.
Relying on getConstantState() alone can result in false negatives.
The approach I've taken is to try comparing the ConstantState in the first instance, but fall back on a Bitmap comparison if that check fails.
This should work in all cases (including images which aren't resources) but note that it is memory hungry.
public static boolean areDrawablesIdentical(Drawable drawableA, Drawable drawableB) {
Drawable.ConstantState stateA = drawableA.getConstantState();
Drawable.ConstantState stateB = drawableB.getConstantState();
// If the constant state is identical, they are using the same drawable resource.
// However, the opposite is not necessarily true.
return (stateA != null && stateB != null && stateA.equals(stateB))
|| getBitmap(drawableA).sameAs(getBitmap(drawableB));
}
public static Bitmap getBitmap(Drawable drawable) {
Bitmap result;
if (drawable instanceof BitmapDrawable) {
result = ((BitmapDrawable) drawable).getBitmap();
} else {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
// Some drawables have no intrinsic width - e.g. solid colours.
if (width <= 0) {
width = 1;
}
if (height <= 0) {
height = 1;
}
result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
}
return result;
}
My question was for just comparing two drawables, I tried but could not get any method that directly compare two drawables,however for my solution i changed drawable to bitmap and then comparing two bitmaps and that is working.
Bitmap bitmap = ((BitmapDrawable)fDraw).getBitmap();
Bitmap bitmap2 = ((BitmapDrawable)sDraw).getBitmap();
if(bitmap == bitmap2)
{
//Code blcok
}
for SDK 21+
this works in SDK -21
mRememberPwd.getDrawable().getConstantState().equals
(getResources().getDrawable(R.drawable.login_checked).getConstantState())
for SDK +21 android 5.
set drawable id to imageview with tag
img.setTag(R.drawable.xxx);
and compare like this
if ((Integer) img.getTag() == R.drawable.xxx)
{
....your code
}
this solution is for who want to compare drawable id of imageview with id of drawable.xxx.
perhaps try it in this way:
public void MyClick(View view)
{
Drawable fDraw = view.getBackground();
Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);
if(fDraw.hashCode() == sDraw.hashCode())
{
//Not coming
}
}
or prepare a method which takes two drawable arguments and return boolean. In that method you may convert drawable into bytes and compare,
public boolean compareDrawable(Drawable d1, Drawable d2){
try{
Bitmap bitmap1 = ((BitmapDrawable)d1).getBitmap();
ByteArrayOutputStream stream1 = new ByteArrayOutputStream();
bitmap1.compress(Bitmap.CompressFormat.JPEG, 100, stream1);
stream1.flush();
byte[] bitmapdata1 = stream1.toByteArray();
stream1.close();
Bitmap bitmap2 = ((BitmapDrawable)d2).getBitmap();
ByteArrayOutputStream stream2 = new ByteArrayOutputStream();
bitmap2.compress(Bitmap.CompressFormat.JPEG, 100, stream2);
stream2.flush();
byte[] bitmapdata2 = stream2.toByteArray();
stream2.close();
return bitmapdata1.equals(bitmapdata2);
}
catch (Exception e) {
// TODO: handle exception
}
return false;
}
The solution for Android 5:
if(image.getDrawable().getConstantState().equals(image.getContext().getDrawable(R.drawable.something).getConstantState()))
getDrawable(int) is Now Deprecated. Use getDrawable(context,R.drawable.yourimageid)
To Compare Two Backgrounds
Boolean Condition1=v.getBackground().getConstantState().equals(
ContextCompat.getDrawable(getApplicationContext(),R.drawable.***).getConstantState());
Ok, I think I've found the ultimate solution for this. Because of AppCompat and friends, the drawable provided is sometimes inflated in different forms so it's not enough to do getResources().getBitmap(R.drawable.my_awesome_drawable).
So, in order to get a drawable instance of the same type and form as provided by the view one can do this:
public static Drawable drawableFrom(View view, #DrawableRes int drawableId) {
Context context = view.getContext();
try {
View dummyView = view.getClass().getConstructor(Context.class).newInstance(context);
dummyView.setBackgroundResource(drawableId);
return dummyView.getBackground();
} catch (Exception e) {
return ResourcesCompat.getDrawable(context.getResources(), drawableId, null);
}
}
This is useful when doing tests. However, I would not recommend doing this in production. If you need to, extra caching would be desirable to avoid doing too much reflection.
For Expresso tests you can use this quite nicely:
onView(withDrawable(R.drawable.awesome_drawable))
.check(matches(isDisplayed()));
or
onView(withId(R.id.view_id))
.check(matches(withDrawable(R.drawable.awesome_drawable)));
Before you'll have to declare this helper class:
public class CustomMatchers {
public static Matcher<View> withDrawable(#DrawableRes final int drawableId) {
return new DrawableViewMatcher(drawableId);
}
private static class DrawableViewMatcher extends TypeSafeMatcher<View> {
private final int expectedId;
private String resourceName;
private enum DrawableExtractionPolicy {
IMAGE_VIEW {
#Override
Drawable findDrawable(View view) {
return view instanceof ImageView ? ((ImageView) view).getDrawable() : null;
}
},
TEXT_VIEW_COMPOUND {
#Override
Drawable findDrawable(View view) {
return view instanceof TextView ? findFirstCompoundDrawable((TextView) view) : null;
}
},
BACKGROUND {
#Override
Drawable findDrawable(View view) {
return view.getBackground();
}
};
#Nullable
private static Drawable findFirstCompoundDrawable(TextView view) {
for (Drawable drawable : view.getCompoundDrawables()) {
if (drawable != null) {
return drawable;
}
}
return null;
}
abstract Drawable findDrawable(View view);
}
private DrawableViewMatcher(#DrawableRes int expectedId) {
this.expectedId = expectedId;
}
#Override
protected boolean matchesSafely(View view) {
resourceName = resources(view).getResourceName(expectedId);
return haveSameState(actualDrawable(view), expectedDrawable(view));
}
private boolean haveSameState(Drawable actual, Drawable expected) {
return actual != null && expected != null && areEqual(expected.getConstantState(), actual.getConstantState());
}
private Drawable actualDrawable(View view) {
for (DrawableExtractionPolicy policy : DrawableExtractionPolicy.values()) {
Drawable drawable = policy.findDrawable(view);
if (drawable != null) {
return drawable;
}
}
return null;
}
private boolean areEqual(Object first, Object second) {
return first == null ? second == null : first.equals(second);
}
private Drawable expectedDrawable(View view) {
return drawableFrom(view, expectedId);
}
private static Drawable drawableFrom(View view, #DrawableRes int drawableId) {
Context context = view.getContext();
try {
View dummyView = view.getClass().getConstructor(Context.class).newInstance(context);
dummyView.setBackgroundResource(drawableId);
return dummyView.getBackground();
} catch (Exception e) {
return ResourcesCompat.getDrawable(context.getResources(), drawableId, null);
}
}
#NonNull
private Resources resources(View view) {
return view.getContext().getResources();
}
#Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(expectedId);
if (resourceName != null) {
description.appendValueList("[", "", "]", resourceName);
}
}
}
}
Compare 2 drawable:
drawable1.constantState == drawable2.constantState
|| drawable1.toBitmap().sameAs(drawable2.toBitmap())
If you can not find Drawable.toBitmap(...) here is it Drawable.kt
Use getTag() and setTag() for comparison
I already answered on the similar topic here: Get the ID of a drawable in ImageView.
The approach is based on tagging a view with a specified resource id in the custom LayoutInflater. Whole process is automated by a simple library TagView.
As a result, you can compare two drawables just by their ids:
TagViewUtils.getTag(view, ViewTag.VIEW_BACKGROUND.id) == R.drawable.twt_hover
Expanding on the answer from #vaughandroid the following Matcher works for a Vector Drawable that is tinted. You have to provide the tint that was used for the Drawable.
public static Matcher<View> compareVectorDrawables(final int imageId, final int tintId) {
return new TypeSafeMatcher<View>() {
#Override
protected boolean matchesSafely(View target) {
if (!(target instanceof ImageView)) {
return false;
}
ImageView imageView = (ImageView) target;
if (imageId < 0) {
return imageView.getDrawable() == null;
}
Resources resources = target.getContext().getResources();
Drawable expectedDrawable = resources.getDrawable(imageId, null);
if (expectedDrawable == null) {
return false;
}
Drawable imageDrawable = imageView.getDrawable();
ColorFilter imageColorFilter = imageDrawable.getColorFilter();
expectedDrawable.setColorFilter(imageColorFilter);
expectedDrawable.setTintList(target.getResources()
.getColorStateList(tintId, null));
boolean areSame = areDrawablesIdentical(imageDrawable, expectedDrawable);
return areSame;
}
public boolean areDrawablesIdentical(Drawable drawableA, Drawable drawableB) {
Drawable.ConstantState stateA = drawableA.getConstantState();
Drawable.ConstantState stateB = drawableB.getConstantState();
// If the constant state is identical, they are using the same drawable resource.
// However, the opposite is not necessarily true.
return (stateA != null && stateB != null && stateA.equals(stateB))
|| getBitmap(drawableA).sameAs(getBitmap(drawableB));
}
public Bitmap getBitmap(Drawable drawable) {
Bitmap result;
if (drawable instanceof BitmapDrawable) {
result = ((BitmapDrawable) drawable).getBitmap();
} else {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
// Some drawables have no intrinsic width - e.g. solid colours.
if (width <= 0) {
width = 1;
}
if (height <= 0) {
height = 1;
}
result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
}
return result;
}
#Override
public void describeTo(Description description) {
}
};
}
a usefull method collected from GitHub copilot:
fun compareTwoDrawable(drawable1: Drawable, drawable2: Drawable): Boolean {
val bitmap1 = (drawable1 as BitmapDrawable).bitmap
val bitmap2 = (drawable2 as BitmapDrawable).bitmap
return bitmap1.sameAs(bitmap2)
}
if You want to directly compare two drawable then use following code
Drawable fDraw = getResources().getDrawable(R.drawable.twt_hover);
Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);
if (fDraw.getConstantState().equals(sDraw.getConstantState())) {
//write your code.
} else {
//write your code.
}
When you are using equals() method it is used to compare the contents. you should try == for comparing two objects.
public void MyClick(View view)
{
Drawable fDraw = view.getBackground();
Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);
if( fDraw == sDraw )
{
// Coming
}
}
I want to set a TextView with SpannableString which is from the method below:
Html.fromHtml(String source, Html.ImageGetter imageGetter,
Html.TagHandler tagHandler)
But the ImageGetter here need to override the method below:
public abstract Drawable getDrawable(String source)
Because I need to get the drawable from the internet, I have to do it asynchronously and seems it is not.
How to make it work?
Thanks.
These guys did a great job, this is my solution using Square's Picasso library:
//...
final TextView textView = (TextView) findViewById(R.id.description);
Spanned spanned = Html.fromHtml(getIntent().getStringExtra(EXTRA_DESCRIPTION),
new Html.ImageGetter() {
#Override
public Drawable getDrawable(String source) {
LevelListDrawable d = new LevelListDrawable();
Drawable empty = getResources().getDrawable(R.drawable.abc_btn_check_material);;
d.addLevel(0, 0, empty);
d.setBounds(0, 0, empty.getIntrinsicWidth(), empty.getIntrinsicHeight());
new ImageGetterAsyncTask(DetailActivity.this, source, d).execute(textView);
return d;
}
}, null);
textView.setText(spanned);
//...
class ImageGetterAsyncTask extends AsyncTask<TextView, Void, Bitmap> {
private LevelListDrawable levelListDrawable;
private Context context;
private String source;
private TextView t;
public ImageGetterAsyncTask(Context context, String source, LevelListDrawable levelListDrawable) {
this.context = context;
this.source = source;
this.levelListDrawable = levelListDrawable;
}
#Override
protected Bitmap doInBackground(TextView... params) {
t = params[0];
try {
Log.d(LOG_CAT, "Downloading the image from: " + source);
return Picasso.with(context).load(source).get();
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(final Bitmap bitmap) {
try {
Drawable d = new BitmapDrawable(context.getResources(), bitmap);
Point size = new Point();
((Activity) context).getWindowManager().getDefaultDisplay().getSize(size);
// Lets calculate the ratio according to the screen width in px
int multiplier = size.x / bitmap.getWidth();
Log.d(LOG_CAT, "multiplier: " + multiplier);
levelListDrawable.addLevel(1, 1, d);
// Set bounds width and height according to the bitmap resized size
levelListDrawable.setBounds(0, 0, bitmap.getWidth() * multiplier, bitmap.getHeight() * multiplier);
levelListDrawable.setLevel(1);
t.setText(t.getText()); // invalidate() doesn't work correctly...
} catch (Exception e) { /* Like a null bitmap, etc. */ }
}
}
My 2 cents... Peace!
Now I'm using an AsyncTask to download the images in the ImageGetter:
Spanned spannedContent = Html.fromHtml(htmlString, new ImageGetter() {
#Override
public Drawable getDrawable(String source) {
new ImageDownloadAsyncTask().execute(textView, htmlString, source);
return null;
}
}, null);
And set the text again into the TextView when the image has been downloaded.
Now it works. But It failed when I tried to do the TextView.postInvalidate() to redraw the downloaded images. I have to do setText() again in the AsyncTask.
Does anyone know why?
Here's my code that grabs all images in the html string (it's simplified from the original, so I hope it works):
private HashMap<String, Drawable> mImageCache = new HashMap<String, Drawable>();
private String mDescription = "...your html here...";
private void updateImages(final boolean downloadImages) {
if (mDescription == null) return;
Spanned spanned = Html.fromHtml(mDescription,
new Html.ImageGetter() {
#Override
public Drawable getDrawable(final String source) {
Drawable drawable = mImageCache.get(source);
if (drawable != null) {
return drawable;
} else if (downloadImages) {
new ImageDownloader(new ImageDownloader.ImageDownloadListener() {
#Override
public void onImageDownloadComplete(byte[] bitmapData) {
Drawable drawable = new BitmapDrawable(getResources(),
BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length));
try {
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
} catch (Exception ex) {}
mImageCache.put(source, drawable);
updateImages(false);
}
#Override
public void onImageDownloadFailed(Exception ex) {}
}).execute(source);
}
return null;
}
}, null);
tvDescription.setText(spanned);
}
So basically what happens here is the ImageGetter will make a request for every image in the html description. If that image isn't in the mImageCache array and downloadImages is true, we run an async task to download that image. Once it has completed, we add the drawable into the hashmap, and then make a call to this method again (but with downloadImages as false so we don't risk an infinite loop), where the image will be able to be grabbed with the second attempt.
And with that, you'll need the ImageDownloader class that I used:
public class ImageDownloader extends AsyncTask {
public interface ImageDownloadListener {
public void onImageDownloadComplete(byte[] bitmapData);
public void onImageDownloadFailed(Exception ex);
}
private ImageDownloadListener mListener = null;
public ImageDownloader(ImageDownloadListener listener) {
mListener = listener;
}
protected Object doInBackground(Object... urls) {
String url = (String)urls[0];
ByteArrayOutputStream baos = null;
InputStream mIn = null;
try {
mIn = new java.net.URL(url).openStream();
int bytesRead;
byte[] buffer = new byte[64];
baos = new ByteArrayOutputStream();
while ((bytesRead = mIn.read(buffer)) > 0) {
if (isCancelled()) return null;
baos.write(buffer, 0, bytesRead);
}
return new AsyncTaskResult<byte[]>(baos.toByteArray());
} catch (Exception ex) {
return new AsyncTaskResult<byte[]>(ex);
}
finally {
Quick.close(mIn);
Quick.close(baos);
}
}
protected void onPostExecute(Object objResult) {
AsyncTaskResult<byte[]> result = (AsyncTaskResult<byte[]>)objResult;
if (isCancelled() || result == null) return;
if (result.getError() != null) {
mListener.onImageDownloadFailed(result.getError());
}
else if (mListener != null)
mListener.onImageDownloadComplete(result.getResult());
}
}