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;
Related
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);
}
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 am creating an app that has a file directory that contains pictures, videos, pdfs, etc. I am currently working on displaying thumbnails for pictures. I am using the RecyclerView and ViewHolder to display list items that each represent a photo item. I then use an AsyncTask to download the Bitmaps one at a time and store them in a Hashmap. Everything works fine except when I scroll down in a large list of photos very quickly. The placeholder image for random items at the bottom of the list are replaced with thumbnails that have already been loaded at the top of the list. When the background thread reaches the image at the bottom, then the correct image replaces the wrong image. After all the thumbnails are loaded then everything works as intended.
Here is the code for the AsyncTask. I think the problem has to do with the position integer I am passing into the constructor. The position variable represents the position in the Adapter. Maybe there is a way to make sure the image is loading the placeholder image I have in onPreExecute()?
/**
* AsyncTask to download the thumbnails in the RecyclerView list.
*/
private class CreateThumbnail extends AsyncTask<Void, Void, android.graphics.Bitmap> {
// ******
// FIELDS
// ******
private ImageView mPreviewInstance;
private File mFile;
private RelativeLayout.LayoutParams lp;
private FileHolder mFileHolder;
private int mPosition;
private UUID mId;
private FolderFile mFolderFile;
// ***********
// Constructor
// ***********
/**
* #param holder - ViewHolder passed for the list item.
* #param position - position in the Adapter.
* #param id - id for list item stored in database.
*/
private CreateThumbnail(FileHolder holder, int position, UUID id) {
mPosition = position;
mFileHolder = holder;
mPreviewInstance = mFileHolder.mImagePreview;
mId = id;
mFolderFile = FolderFileLab.get(getContext()).getFolderFile(mId);
}
// ****************
// OVERRIDE METHODS
// ****************
#Override
protected void onPreExecute() {
}
#Override
protected Bitmap doInBackground(Void... params) {
FolderFileLab lab = FolderFileLab.get(getContext());
if (!lab.getCurrentMap().containsKey(mId)) {
mFile = lab.getPhotoFile(mFolderFile);
// Create Bitmap (Biggest use of memory and reason this background thread exists)
Bitmap bitmap = PictureUtils.getScaledBitmap(
mFile.getPath(), getActivity());
// Scales Bitmap down for thumbnail.
Bitmap scaledBitmap;
if (bitmap.getWidth() >= bitmap.getHeight()) {
scaledBitmap = Bitmap.createBitmap(bitmap, bitmap.getWidth() / 2
- bitmap.getHeight() / 2,
0, bitmap.getHeight(), bitmap.getHeight());
} else {
scaledBitmap = Bitmap.createBitmap(bitmap, 0, bitmap.getHeight() / 2
- bitmap.getWidth() / 2,
bitmap.getWidth(), bitmap.getWidth());
}
// Cache bitmap
HashMap<UUID, Bitmap> map = lab.getCurrentMap();
map.put(mId, scaledBitmap);
lab.updateMap(map);
return scaledBitmap;
} else {
// If Hashmap already contains the id get the Bitmap.
return lab.getCurrentMap().get(mId);
}
}
#Override
protected void onPostExecute(Bitmap bitmap) {
// Checks to see if the bitmap is still displayed in the list. If not nothing happens.
// If it is then it displays the image.
if (mPreviewInstance.getVisibility() == View.VISIBLE && mFileHolder.getPosition()
== mPosition && bitmap != null) {
// Formatting for thumbnail
lp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout
.LayoutParams.WRAP_CONTENT);
lp.setMargins(7, 7, 7, 0);
// Displaying thumbnail on UI thread.
mPreviewInstance.setLayoutParams(lp);
mPreviewInstance.setBackground(null);
mPreviewInstance.setImageBitmap(bitmap);
}
}
}
Here is some of the relevant Adapter code where the AsyncTask is started.
#Override
public void onBindViewHolder(FileHolder holder, int position) {
FolderFile file = mFiles.get(position);
holder.bindFile(file);
if (file.isPhoto()) {
createThumbnail = new CreateThumbnail(holder, position,file.getId());
createThumbnail.execute();
}
}
Figured it out!
I added code to change the photo to the placeholder image after every bind. This is what I changed in my adapter.
#Override
public void onBindViewHolder(FileHolder holder, int position) {
FolderFile file = mFiles.get(position);
holder.bindFile(file);
if (file.isPhoto()) {
Drawable placeholder = getResources().getDrawable(R.mipmap.picture_blu);
holder.mImagePreview.setBackground(placeholder);
holder.mImagePreview.setImageBitmap(null);
createThumbnail = new CreateThumbnail(holder, position, file.getId());
createThumbnail.execute();
}
}
Your views are recycled, so by the time the async task finishes, the imageView has been reused and has a new image assigned to it.
What you can do is assign to the imageView a tag that is the file name of the file you are trying to load into it. You keep track of that same file name in the async task. Then in your AsyncTask, in onPostExecute, you check if the tag the imageView has is the same file name that you just loaded. If it is, you go ahead and set the bitmap to the imageView. If it is not, then the view has been recycled and you simply drop the Bitmap you just created; another async task will be loading the right bitmap.
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));
}
I have specified an area for the imageView in the layout, in which I should display an image by referring to its path. Now, I got the image displayed on its respective imageview but
(1) the image is smaller than the allotted are for the imageView. And I want the imgae
to fit exactly the imageView
(2) the image is rotated 90 degrees anticlockwise. I want it to be
displayed normally without rotation?
Please help me to achieve the aforementioned points
Last Update
Java_Code:
private void displayMeetinPointImageInsideImageView(DataBaseRow dataBaseRow, ImageView imgSpaceIv) {
// TODO Auto-generated method stub
if (dataBaseRow == null) {
Log.w(TAG, "#displayMeetinPointImageInsideImageView(): The Object Of The DataBaseRow Class Is NULL, Maybe "
+ "It Was Not Instantiated.");
return;
} else if (imgSpaceIv == null) {
Log.w(TAG, "#displayMeetinPointImageInsideImageView(): The Variable Of Type ImageView Is NULL, Maybe It Was "
+ "Not Instantiated.");
return;
} else if (dataBaseRow.getImgPath().isEmpty()) {
Log.w(TAG, "#displayMeetinPointImageInsideImageView(): dataBaseRow.getImgPath() Is Empty");
return;
} else {
String imgPathString = dataBaseRow.getImgPath();
Options options = new Options();
options.inSampleSize = 3;
Bitmap bitmap = BitmapFactory.decodeFile(imgPathString);
Bitmap imgbitmap = BitmapFactory.decodeFile(imgPathString, options);
imgSpaceIv.setImageBitmap(imgbitmap);
}
}
Look at your logs:
dataBaseRow.getImgPath()= /storage/emulated/0/DCIM/MPL/5th guy hinging.jpeg
There is space in your filename.