I am displaying an image in a RecyclerView whose source is is a bitmap taken from an MMS message. The problem is that the image is not displaying. Absolutely nothing is displayed. Here is my onBindView:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get(position).getContact() ;
final MMSMessage message = mDataset.get(position);
holder.txtHeader.setText(name);
DateTime dateTime = new DateTime(message.getDate());
holder.txtDate.setText(dateTime.toString(Globals.generalSQLFormatterDT));
holder.txtText.setText(message.getBody());
holder.txtText.setVisibility(View.VISIBLE);
Bitmap bitmap = message.getBitmap();
if (bitmap != null) {
//bitmap is not null and I can see an image using Android Studio
bitmap =Bitmap.createScaledBitmap(bitmap, 120, 120, false);
holder.imgMMS.setImageBitmap(bitmap);
} else {
holder.imgMMS.setVisibility(View.GONE);
}
}
The xml for the ImageView:
<ImageView
android:layout_below="#+id/thirdLine"
android:id="#+id/imageMMS"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginRight="6dip"
android:contentDescription="TODO"
/>
I looked here and tried to scale down the image to an arbitrary small size. I don't think it's an out of memory error - I tried putting in the launcher icon as a test. What am I doing wrong?
if (bitmap != null) {
//bitmap is not null and I can see an image using Android Studio
bitmap =Bitmap.createScaledBitmap(bitmap, 120, 120, false);
holder.imgMMS.setImageBitmap(bitmap);
holder.imgMMS.setVisibility(View.GONE);
} else {
holder.imgMMS.setVisibility(View.GONE);
}
You are setting visibility to GONE. My guess is that the RecyclerView is recycling the views, and when it does the view is GONE since you are not setting it to Visible. Try adding holder.imgMMS.setVisibility(View.VISIBLE); for when bitmap is not null, like so:
if (bitmap != null) {
//bitmap is not null and I can see an image using Android Studio
bitmap =Bitmap.createScaledBitmap(bitmap, 120, 120, false);
holder.imgMMS.setImageBitmap(bitmap);
holder.imgMMS.setVisibility(View.VISIBLE);
} else {
holder.imgMMS.setVisibility(View.GONE);
}
Related
I'm looking to populate an imageview depending on which source contains the data. The holder.imgImage could have either a bitmap source or a drawable path but I only want one to be displayed depending on which image is present. I have tried if (image !=null) but doesnt seeem to work.
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.myTextView1.setText(categoryList.get(position).getRecipe_name());
holder.myTextView2.setText(categoryList.get(position).getCategory_name());
String image2 = categoryList.get(position).getImage2();
Bitmap myBitmap = BitmapFactory.decodeFile(image2);
holder.imgImage.setImageBitmap(myBitmap);
holder.imgImage.setImageResource(categoryList.get(position).getImage());
}
maybe check if created Bitmap isn't null?
Bitmap myBitmap = BitmapFactory.decodeFile(image2);
if (myBitmap != null)
holder.imgImage.setImageBitmap(myBitmap);
else
holder.imgImage.setImageResource(categoryList.get(position).getImage());
maybe there is a case when getImage2() returns null or empty string?
String image2 = categoryList.get(position).getImage2();
Bitmap myBitmap = (image2 != null && image2.length()) > 0 ?
BitmapFactory.decodeFile(image2) : null;
The problem is as it is stated in question title. In fact I want to load images which I have their url in my records into RecyclerView and at the same time persist downloaded image to database. I am using realm.io and Glide and my RecyclerViewAdapter is as below:
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final ProductModel obj = getData().get(position);
holder.data = obj;
holder.title.setText(obj.getTitle());
if (obj.getImage() == null) {
Glide
.with(context)
.load(obj.getImageUrl())
.fitCenter()
.placeholder(R.drawable.bronteel_logo)
.into(new GlideDrawableImageViewTarget(holder.icon) {
#Override
protected void setResource(GlideDrawable resource) {
// this.getView().setImageDrawable(resource); is about to be called
super.setResource(resource);
// here you can be sure it's already set
((ProductsFragment) mFragment).saveImage(obj, resource);
}
});
} else {
byte[] data = obj.getImage();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length, options);
holder.icon.setImageBitmap(bmp);
}
}
class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView title;
public ImageView icon;
public ProductModel data;
public MyViewHolder(View view) {
super(view);
title = (TextView) view.findViewById(R.id.textView);
icon = (ImageView) view.findViewById(R.id.imageView);
view.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (data.getImage() != null)
activity.startActivity(new Intent(activity, ProductActivity.class).putExtra("id", data.getId()));
}
}
And here's how I save images:
public void saveImage(final ProductModel data, Drawable drw) {
new AsyncImagePersister(data).execute(drw);
}
private class AsyncImagePersister extends AsyncTask<Drawable, Void, byte[]> {
private final ProductModel data;
AsyncImagePersister(ProductModel data) {
this.data = data;
}
#Override
protected byte[] doInBackground(Drawable... drawables) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Bitmap bmp = drawableToBitmap(drawables[0]);
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
return stream.toByteArray();
}
#Override
protected void onPostExecute(final byte[] bytes) {
super.onPostExecute(bytes);
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
data.setImage(bytes);
}
});
}
public Bitmap drawableToBitmap (Drawable drawable) {
Bitmap bitmap = null;
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if(bitmapDrawable.getBitmap() != null) {
return bitmapDrawable.getBitmap();
}
}
if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}
However, when loading the images for the first time from internet (using Glide) it shows wrong pictures for different places and on the other hand after it fetches all images, the saved images to realm are in their correct place.
So what am I doing wrong? Please help. Thanks.
The misplaced images is due to views are being recycled, So the loaded bitmap does not necessarily belong to the current position, And another thing to consider is that using AsyncTask inside a RecyclerView won't play nice and will cause lags in your UI, And for the final point, saving the byte[] array in your model might end up to a OOM exception!
If you want do some long running task inside your adapter, think of using a Service, IntentService or ThreadHandler, so you will be sending tasks one by one and the'd be queued and executed one by one.
About having offline access to images:
One option could be using Glide.diskCacheStrategy method and use DiskCacheStrategy.ALL so the original image size will be cached and you can use later in offline mode
Second option is to use Picasso instead of Glide!
so that you can use a custom RequestHandler and download the image and save it somewhere so you can access it later, consider memory management is all on your side and you should handle it!
here's a hint for your second option:
create class which extends from RequestHandler:
CustomReqHandler : RequestHandler() {
Then you should override two methods: canHandleRequest(), load()
in canHandleRequest() you should determine whether you want to handle current request or not, so define a custom scheme for these requests and check if this is one of them like:
val scheme:String = data.uri.scheme
the 2nd method is load() which is executed on a background thread and returns a Result object, download the image, save it somewhere, and return Result object!
You don't actually have to save the loaded images in your database when you're using Glide for this purpose. Glide caches the images loaded once automatically and efficiently. The caching is a complex system and if you want to read more about the caching with Glide, you might have a look here.
Now, about the images loaded in wrong place - this should not happen. I found no serious bug in your onBindViewHolder but hence as I suggest you not to save the images locally you might consider loading the images simply with Glide like this.
Glide
.with(context)
.load(obj.getImageUrl())
.fitCenter()
.placeholder(R.drawable.bronteel_logo)
.into(holder.icon);
Just you need to make sure if the obj.getImageUrl() is returning proper url.
I have been struggling with setting an image (that i fetch using the uri) into an ImageView.
What i am doing ?
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<Attachment> imageViewReference;
public BitmapWorkerTask(Attachment imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<Attachment>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
return ImageResizer.decodeSampledBitmapFromFile(imageViewReference.get().getPath(), 200, 100);
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final Attachment imageView = imageViewReference.get();
if (imageView != null) {
profilePic.setImageBitmap(bitmap);
}
else {
Log.v("BLAH[Inner]", (imageViewReference ==null) +""+(bitmap == null));
}
}
else {
Log.v("BLAH", (imageViewReference ==null) +""+(bitmap == null));
}
}
}
I have confirmed that the uri is correct , and the absolute path to the image is also correct(which is set in the path property of the Attachment object).Plus the bitmap is not null.
But the imageView is still not showing the image.
UPDATE
The image is not being shown on the first time but works everytime after that.Weird thing is that nothing shows up on the logcat as well.
Caution :
Bad variable name used(refactoring went wrong)
Default image(set in the xml) does show however.Take a look
I used hierarchy viewer to check into the layout.
I hope you have added permissions in manifest:
<uses-permission android:name="android.permission.INTERNET" />
Sometimes we miss small things.
You are using setImageBitmap which works always but you can use the following snippet code.
BitmapDrawable ob = new BitmapDrawable(getResources(), bitmap)
imageView.setBackgroundDrawable(ob);
Change
profilePic.setImageBitmap(bitmap)
to
imageView.setImageBitmap(bitmap)
From the information you provide, it seems that either the file has problem, or it is the ImageResizer.decodeSampledBitmapFromFile not working, returned an empty or transparent Bitmap.
Can you add this code in onPostExecute and post the result?
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Log.v("BLAH", "width : " + width);
Log.v("BLAH", "height : " + height);
if(width > 0 && height > 0) {
Log.v("BLAH", "pixel : " + bitmap.getPixel(width/2,height/2));
}
Currently I have an app that allows a user to look at a request and add notes or images to the request. Everything looks good, but I don't want to have to recreate the activity to show the updated information. I have the note portion working fine, but the image portion doesn't update until the configuration changes on orientation change.
Is is the post execute that uses the displayThumbnails function to recreate the imageviews.
#Override
protected void onPostExecute(Void result) {
if (errorMessage.equals("")) {
updateImageProgress.dismiss();
isupdateImageProgressShowing = false;
displayThumbnails(updatedThumbPaths);
This is the actual function call to recreate the imageviews.
private void displayThumbnails(String[] path) {
thumbnails.removeAllViews();
thumbnails.invalidate();
if (imageCount > 0) {
for (int i = 0; i < imageCount; i++) {
Bitmap bitmap = BitmapFactory.decodeFile(path[i]);
Bitmap scaled = Bitmap.createScaledBitmap(bitmap, 150, 150,
false);
bitmap.recycle();
ImageView imgPhoto = new ImageView(this.getActivity());
imgPhoto.setImageBitmap(scaled);
imgPhoto.setId(i);
imgPhoto.setPadding(5, 5, 5, 5);
imgPhoto.setClickable(true);
if(updatedThumbPaths == null){
imgPhoto.setOnClickListener(photoPopup);
}else{
imgPhoto.setOnClickListener(updatePhotoPopup);
}
thumbnails.addView(imgPhoto);
}
}
}
So does anyone have any suggestion on how to redraw the imageviews without having to recreate the entire fragment/activity.
I am using:
https://github.com/jasonpolites/gesture-imageview
on load of the app, it has a placeholder image in a GestureImageView that pinch/zooms appropriately. I have a button that when clicks fires a camera intent, saves the file, and then I wish to set that image to be the source bitmap used in the gestureimageview.
GestureImageView imageView = (GestureImageView) findViewById(R.id.imageViewOne);
ContentResolver cr = getContentResolver();
getContentResolver().notifyChange(imageUriOne, null);
try {
Bitmap mybitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, imageUriOne);
imageView.setImageBitmap(mybitmap);
}
For a normal imageview, that works. But for the GestureImageView, the image stays as the original once returned from the camera intent, and if touched disappears.
To check it's not the bitmap that's the problem, I tried
int idTwo=getResources().getIdentifier("com.jazz.test1:drawable/second_photo", null, null);
imageView.setImageResource(idTwo);
I.e. set the imageview to an existing resource, but this has the same problem.
If I call that setImageResource code before the intent, it does work.
Any ideas how to debug? There are no errors in the logs.
Resolution is here:
https://github.com/jasonpolites/gesture-imageview/issues/21
hadn't noticed it when I initially looked at the github issues.
You have to replace your initMethod function. With this code it will work properly (GestureImageView.java file in com.polites.android package).
protected void initImage() {
if (this.drawable != null) {
this.drawable.setAlpha(alpha);
this.drawable.setFilterBitmap(true);
if (colorFilter != null) {
this.drawable.setColorFilter(colorFilter);
}
// Keppel.Cao
layout = false;
startingScale = -1.0f;
}
if (!layout) {
requestLayout();
// Keppel.Cao
// redraw();
reset();
}
}
Like Dave said. More you can find here Issue 21