Universal Image Loader - Use same image in multiple views - android

I'm using Universal Image Loader to load images from a backend to display user images in a list; however, if the icon shows up multiple times, the Universal Image Loader doesn't fill out all the views.
[User Image 1] - No image
[User Image 1] - No Image
[User Image 2] - Fine
[User Image 2] - No Image
[User Image 3] - Fine
[User Image 1] - Fine
And then on another screen:
[User Image 1] - Fine
[User Image 1] - No image
I'm using cacheInMemory and cacheOnDisk, which seemed to improve it. As before it was only displaying it in one of the views, instead of most, but I need all of them to work.
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
.cacheInMemory( true )
.cacheOnDisk( true )
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder( this )
.threadPoolSize( 3 )
.defaultDisplayImageOptions( defaultOptions )
.build();
ImageLoader.getInstance().init( config );
I'm not using a ListView for this task, I'm using a ScrollView and inflating it with a custom layout.
private View createSmallActivity( LayoutInflater inflater, final Event activity ) {
final View view;
view = inflater.inflate( R.layout.activity_posted_small, null );
...
// The owner's image.
if( activity.ownerImageUrl != null ) {
Loader.loadImage( getActivity(),
activity.ownerImageUrl,
R.drawable.postedactivitysmall_imageprofileempty,
( ImageView ) view.findViewById( R.id.profileImage ) );
}
return view;
}
// Loader.loadImage
// Setting the targetSize, and masking the image with a resource.
public static void loadImage( Context context, String url, int resource, ImageView view ) {
Drawable d = context.getResources().getDrawable( resource );
int h = d.getIntrinsicHeight();
int w = d.getIntrinsicWidth();
ImageSize targetSize = new ImageSize( w, h );
ImageLoader.getInstance().loadImage( url, targetSize, new MaskImageLoader( context, view, resource ) );
}
Any idea on how I can improve the Universal Image Loader to ensure all the views are correctly filled out?
Thanks for your help!

It's because Universal Image Loader cancel previous requests with same url (used as id). To prevent this behaviour, replace
ImageLoader.getInstance().loadImage( url, targetSize, new MaskImageLoader( context, view, resource ) );
by
ImageLoader.getInstance().displayImage(url,
new NonViewAware(new ImageSize(w, h), ViewScaleType.CROP),
new MaskImageLoader(context, view, resource));

I recommend you use ListView. Maybe you should declare ImageLoader globally. then use it.
ImageLoader imageLoader = ImageLoader.getInstance();
...
imageLoader.displayImage(imageUri, imageView);

Related

How to avoid image flickering in a listview

I have a listivew that display a bunch of images. am using Universal Image Loader to load this images from files to imageviews.
This images have different dimensions and i want all of them to have same width but different height in respect to each image aspect ratio.
To achieve this, i have tried setting the following to my imageview
<ImageView
android:layout_width = "400dp"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:adjustViewBounds="true"/>
The issue with this method is that there is a lot of flickering when one scrolls the listview since imageview height is not known in advance and images have to be scaled first using my width to calculate each image height in respect to it's aspect ratio.
How can i calculate each image height in advance instead of letting imageview handle it?
if i have an image which is 400 X 700, and i want the imageview to be 300px wide, how can i calculate imageview's height using my image dimension and maintain image aspect ratio? this can help avoid flickering wnen one scroll the listview.
The reason for this flicker is that, in listview list items are reused. When re-used, the imageviews in the list item retains the old image reference which is displayed first. Later on once new image is downloaded, it starts to show. this causes the flickering behavior.
To avoid this flickering issue, always clear the old image reference from the imageview when it is getting reused.
In your case, add holder.image.setImageBitmap(null); after holder = (ViewHolder) convertView.getTag();
So, your getView() method will look like:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
...
if (convertView == null) {
LayoutInflater inflater = getLayoutInflater();
convertView = inflater.inflate(viewResourceId, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
holder.image.setImageBitmap(null)
}
...
return convertView;
}
After hours of research, i was able to know the method that i can use to calculate new imageview height while maintaining image aspect ratio.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//Returns null, sizes are in the options variable
BitmapFactory.decodeFile("/sdcard/image.png", options);
int width = options.outWidth;
int height = options.outHeight;
//calculating image aspect ratio
float ratio =(float) height/(float) width;
//calculating my image height since i want it to be 360px wide
int newHeight = Math.round(ratio*360);
//setting the new dimentions
imageview.getLayoutParams().width = 360;
imageview.getLayoutParams().height = newHeight;
//i'm using universal image loader to display image
imaheview.post(new Runnable(){
ImageLoader.getInstance().displayImage(imageuri,imageview,displayoptions);
});
You can do something like this :
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//Returns null, sizes are in the options variable
BitmapFactory.decodeFile("/sdcard/image.png", options);
int width = options.outWidth;
int height = options.outHeight;
//If you want, the MIME type will also be decoded (if possible)
String type = options.outMimeType;
How I solved it was by creating a Bitmap[] array variable to store images, then in adapter's getView(), I used position to check if image in Bitmap[] array is null or has value. If it is has value, then I use the value instead of calling the new DownloadImageTask() construct again.
For example:
YourCustomArrayAdapter.java
public class MyCustomArrayAdapter extends ArrayAdapter {
private static Bitmap[] myListViewImageViewsArray = new Bitmap[listViewItemsArray.length];
private String[] myListViewImageURLsArray = new String[listViewItemsArray.length]{
"image_url_1",
"image_url_2",
...
...
};
#Override
public View getView(int position, View view, ViewGroup parent){
CustomViewHolder vHolder;
if(view == null){
view = inflater.inflate(R.layout.movies_coming_soon_content_template, null, true);
vHolder = new CustomViewHolder();
vHolder.imageView = (AppCompatImageView) view.findViewById(R.id.my_cutom_image);
vHolder.imageUrl = "";
view.setTag(vHolder);
}
else{
vHolder = (CustomViewHolder)view.getTag();
// -- Set imageview src to null or some predefined placeholder (this is not really necessary but it might help just to flush any conflicting data hanging around)
vHolder.imageView.setImageResource(null);
}
// ...
// -- THIS IS THE MAIN PART THAT STOPPED THE FLICKERING FOR ME
if(myListViewImageViewsArray[position] != null){
vHolder.imageView.setImageBitmap(myListViewImageViewsArray[position]);
}else{
new DownloadImageTask(position, vHolder.imageView).execute(vHolder.imageUrl);
}
// -- END OF THE FLICKERING CONTROL
}
}
Then, in your image downloader construct, after downloading the image, make an insertion into the Bitmap[] image array for that position. For example:
YourImageDownloaderClass.java
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
AppCompatImageView imageView;
int position;
public DownloadImageTask(int position, AppCompatImageView imageView){
this.imageView = imageView;
this.position = position;
}
#Override
protected Bitmap doInBackground(String...urls) {
String urlOfImage = urls[0];
Bitmap logo = null;
try{
logo = BitmapFactory.decodeStream((InputStream) new URL(urlOfImage).getContent());
}catch(Exception e){
e.printStackTrace();
}
return logo;
}
#Override
protected void onPostExecute(Bitmap result){
if(result != null) {
YourCustomArrayAdapter.myListViewImageViewsArray [position] = result;
imageView.setImageBitmap(result);
}else{
YourCustomArrayAdapter.myListViewImageViewsArray [position] = null;
imageView.setImageResource(null);
}
}
}
my suggestion is to use grid view to avoid flickering of images it will load at first time if it is same url , it will load from cache
Glide.with(mContext)
.load(item.getImageUrl())
.into(holder.mIVGridPic);

Display Custom Size Image Universal ImageLoader

I am using Universal ImageLoader for downloading large images from server and displaying them in coverflow. Issue I am facing here is that I am unable to set custom size for my image. It just takes whole screen. For example, I am downloading image of size 500 x 1200 - and I want to display it of size 300 x 300. But it takes full display. Please help me with this issue as I am stuck for more than 3 days. Thanks.
Code
public class HomeCoverFlow {
Context mContext;
public CoverFlow coverFlow;
ImageAdapter coverImageAdapter;
ImageLoader imageLoader = ImageLoader.getInstance();
DisplayImageOptions options;
/** Called when the activity is first created. */
public HomeCoverFlow(Context context)
{
mContext = context;
options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.flyer_placeholder)
.showImageForEmptyUri(R.drawable.flyer_placeholder)
.imageScaleType(ImageScaleType.EXACTLY)
.cacheInMemory(true)
.build();
coverFlow = new CoverFlow(context);
coverImageAdapter = new ImageAdapter(mContext);
coverFlow.setAdapter(coverImageAdapter);
coverFlow.setSpacing(-25);
coverFlow.setSelection(2, true);
coverFlow.setAnimationDuration(1000);
coverFlow.setGravity(Gravity.CENTER_VERTICAL);
// HEIGHT
coverImageAdapter.coverflowHeight = 100; //display.getHeight() / 3;
//WIDTH
coverImageAdapter.coverflowWidth = 100;// display.getWidth() / 2;
}
A method in adapter to create item views, Here I use imageloader to download image and display
public View getView(int position, View convertView, ViewGroup parent) {
FeaturedFlyerData data = (FeaturedFlyerData) getItem(position);
if(data.flyerImage == null)
{
setNewImage(position);
}
//see text or image
if(data.displayImage)
{
data.flyerImage.setBackgroundColor(Color.BLACK);
imageLoader.displayImage(data.url, data.flyerImage, options, null);
}
return data.flyerImage;
}
void setNewImage(int position)
{
FeaturedFlyerData data = (FeaturedFlyerData) getItem(position);
data.flyerImage = new ImageView(mContext);
data.flyerImage.layout(0, 10, 200,350);
data.flyerImage.setScaleType(ScaleType.CENTER_INSIDE);
data.flyerImage.setTag(Integer.toString(position));
data.flyerImage.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
imageClicked(Integer.parseInt(v.getTag().toString()));
}
});
}
}
I don't know what else code snippet to provide here. Please comment if you need any further clarifications. Thanks.
I am using following code to display custom size images in coverflow using universal imageloader.
In constructor, set DisplayImageOptions as:
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.flyer_placeholder)
.showImageForEmptyUri(R.drawable.flyer_placeholder)
.imageScaleType(ImageScaleType.EXACTLY)
.cacheInMemory(true)
.build();
//set coverflow full screen, and images size.
coverFlow.setSpacing(-25);
coverFlow.setAnimationDuration(1000);
coverFlow.setGravity(Gravity.CENTER_VERTICAL);
coverFlow.setLayoutParams(new CoverFlow.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// GET SCREEN SIZE
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
//Here I am setting image size using my custom adapter in "dp". Avoid to use pixels as things will mess up for different screen sizes
coverImageAdapter.coverflowWidth = (int) Math.ceil(dm.widthPixels * (dm.densityDpi / 160.0));
coverImageAdapter.coverflowHeight = (int) Math.ceil(dm.heightPixels * (dm.densityDpi / 160.0)) / 2;
In adapter's setNewImage method (called in getView), use Gallery.LayoutParams:
void setNewImage(int position)
{
FeaturedFlyerData data = (FeaturedFlyerData) getItem(position);
data.flyerImage = new ImageView(mContext);
data.flyerImage.setLayoutParams(new Gallery.LayoutParams(coverflowWidth, coverflowHeight));
data.flyerImage.setScaleType(ScaleType.CENTER_INSIDE);
}

How to load image (from sdcard) faster in thumbnail size?

I want to make a grid view to show thumbnail of photos in folder in sdcard.
Image resolution is 3264x2448. I use Notras Universal Image Loader lib with config:
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub)
.showImageForEmptyUri(R.drawable.stub)
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
.showImageOnFail(R.drawable.ic_launcher).cacheInMemory()
.cacheOnDisc().bitmapConfig(Bitmap.Config.RGB_565).build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
context).defaultDisplayImageOptions(options).build();
And getView() in my custom Adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView imageView;
if (convertView == null) {
imageView = (ImageView) mInflater.inflate(R.layout.grid_item,
parent, false);
} else {
imageView = (ImageView) convertView;
}
mImageLoader.displayImage(mListData.get(position), imageView, options);
return imageView;
}
But it load image too slow. So please help me to load thumbnail image faster. I don't want to display high quality image, I just want to display fast.
Thanks in advance.
UIL version: 1.8.4
Android version tested on: 2.3.3
Try to use .discCacheExtraOptions(800, 800, CompressFormat.PNG, 0) in configuration. You can vary value "800" depending maximum dimension of device.
You can use this to get the thumbnail:
Bitmap bitmap = MediaStore.Images.Thumbnails.getThumbnail(
getContentResolver(), selectedImageUri,
MediaStore.Images.Thumbnails.MINI_KIND,
(BitmapFactory.Options) null );
There are two types of thumbnails available:
MINI_KIND: 512 x 384 thumbnail
MICRO_KIND: 96 x 96 thumbnail
OR use queryMiniThumbnail with almost same parameters to get the path of the thumbnail.
Cursor cursor = MediaStore.Images.Thumbnails.queryMiniThumbnail(
getContentResolver(), selectedImageUri,
MediaStore.Images.Thumbnails.MINI_KIND,
null );
if( cursor != null && cursor.getCount() > 0 ) {
cursor.moveToFirst();//**EDIT**
String uri = cursor.getString( cursor.getColumnIndex( MediaStore.Images.Thumbnails.DATA ) );
}
you can use this uri in imageloader to view thumbnails
Reference and detailed description

Gallery is loading all the images but gallery should be load one image at a time

i have created a gallery for display images haveing pinchzoom with goto next and previous by touch . i were use to visit https://github.com/kilaka/ImageViewZoom library . i saw Gallery first upload all the images and then it is displaying images which is good activity. but gallery can upload max 4MB images , if images size go to greater than 4MB then it gives us Bitmap outofmemory error. i have used to bitmap.recycle() but it is recycle all the images.
do here any way, so my gallery upload images one by one from sdcard and when i will go to next or previous images by touch , my currently images memory should be delete . and next images should be uploaded in gallery memory . so my gallery can upload a number of images .
this is my code , this code is link from visit https://github.com/kilaka/ImageViewZoom :-
gallery = new GalleryTouch(this);
gallery = (GalleryTouch) findViewById(R.id.image);
gallery.setSpacing(20);
arrayAdapter = new ArrayAdapter<ImageView>(this, -1) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
bookM= position; return getItem(position);
}
};
if(!(it.isEmpty() )) {
Collections.sort(it);
for(int i=0; i<it.size();i++) {
ImageViewTouch imageView = new ImageViewTouch(imageTouch.this);
imageView.setLayoutParams(new Gallery.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
final Options options = new Options();
options.outHeight = (int) scaleHeight;
options.outWidth = (int) scaleWidth;
options.inScaled = true;
options.inPurgeable = true;
options.inSampleSize = 2;
String photoURL = it.get(i);
bitmap = BitmapFactory.decodeFile(photoURL,options);
imageView.setImageBitmap(bitmap);
arrayAdapter.add(imageView);
}
gallery.setAdapter(arrayAdapter);
}
Note:- in this code, "it" is a variable of List it; and list having a list of path of image from sd card like as sdcard/images/one.jpg , sdcard/images/two.jpg ........ etc.
please expert help me ,
thank you for your important time.

Android: showing default image in gallery's ImageView while actual image is downloaded

I believe this is pretty trivial but I can't get it to work.
I want to display a default image in gallery elements (ImageViews) while their actual image is being fetched from the net.
Right now, nothing is shown for an ImageView which its image has yet to arrive. Once it arrives it is immediately shown.
What I tried is right after the instantiation of the ImageView to call its setImageResource function like so:
final ImageView i = new ImageView(mContext);
i.setImageResource(R.drawable.loading);
But it doesn't seem to work. Below is the full getView() function.
Any help is appreciated.
Thanks.
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView i = new ImageView(mContext);
i.setImageResource(R.drawable.loading);
// if the drawbale is in the buffer - fetch it from there
Drawable bufferedImage = DataManager.getInstance().getImagesBuffer()[position];
if (bufferedImage != null){
i.setImageDrawable(bufferedImage);
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
}
// if drawable is not in buffer - fetch it from the net via AsyncImageLoader
else
{
String imageUrl = DataManager.getInstance().getImageBufferInstance().getImageUrl(position);
Drawable downloadedImage = AsyncImageLoader.getInstance().loadDrawable(imageUrl, new ImageCallback() {
public void imageLoaded(Drawable imageDrawable, String imageUrl) {
if (imageDrawable == null)
{
imageDrawable = getResources().getDrawable(R.drawable.icon);
}
i.setImageDrawable(imageDrawable);
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
}
});
i.setImageDrawable(downloadedImage);
}
i.setLayoutParams(new CoverFlow.LayoutParams(Utils.getInstance().getScreenWidth() / 2,
Utils.getInstance().getScreenHeight() / 2));
i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
return i;
}
Fix your indentation... If I'm reading that correctly, you're only setting the temporary drawable icon in the imageLoaded callback of your (not shown) AsyncImageLoader, which I assume means it's then only being set after the image downloads and is then immediately overwritten with the downloaded image. Try moving the placeholder-setting code into your else block outside the callback.

Categories

Resources