Universal Image Loader cache - universal-image-loader

I'm trying to integrate Universal Image Loader into my Android App.
It has a GridView and shows images acquired from internet.
I implemented it using ArrayAdapter which loads images in getView() in a usual way.
It works well in terms of displaying picture correctly.
But I found an unexpected behavior in loading images from memory cache.
When activity is launched, UIL loads image from internet or disc cache if exists.
(Of course, it is expected behavior.)
Scrolling down GridView until first column go out from screen, and scroll back to the top.
In this time, images at first column are loaded from disc cache, instead of memory cache.
Then scrolling down and up again.
In this time, images at first column are loaded from memory cache.
I expect images are loaded from memory cache at second time of displaying, which is step 2 in operation above.
I don't know why disc cache is used in this case.
Here is my codes.
ImageLoaderConfiguration
ImageLoaderConfiguration mImageLoaderConfig =
new ImageLoaderConfiguration.Builder(getApplicationContext())
.defaultDisplayImageOptions(defaultOptions)
.enableLogging()
.build();
DisplayImageOptions
DisplayImageOptions defaultOptions =
new DisplayImageOptions.Builder()
.cacheInMemory()
.cacheOnDisc()
.showImageForEmptyUri(R.drawable.empty_photo)
.showStubImage(R.drawable.empty_photo)
.displayer(new FadeInBitmapDisplayer(500))
.build();
getView() in ArrayAdapter
if (convertView == null) {
convertView = (FrameLayout) LayoutInflater.from(getContext())
.inflate(mLayoutId, null);
convertView.setLayoutParams(mImageViewLayoutParams);
} else { // Otherwise re-use the converted view
convertView.findViewById(R.id.videoIconInThumbnail).setVisibility(View.GONE);
}
// Check the height matches our calculated column width
if (convertView.getLayoutParams().height != mItemHeight) {
convertView.setLayoutParams(mImageViewLayoutParams);
}
ImageView image = (ImageView) convertView.findViewById(R.id.photoThumbnail);
ImageLoader.getInstance().displayImage(thumbnailUrl, image,
new SimpleImageLoadingListener() {
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
Log.v(TAG, imageUri + " is loaded.");
}
});
return convertView;
layout XML for an element in GridView
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/photoThumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" >
</ImageView>
<ImageView
android:id="#+id/videoIconInThumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="#drawable/ic_play"
android:visibility="gone" >
</ImageView>
</FrameLayout>
Version of UIL is 1.8.4.
Tested Android version is 4.1.2.
Added log output of UIL when an image is loaded for three times with a operation described above.
// Fist time of displaying
I/ImageLoader( 7404): Start display image task [http://xxx/yyy.JPG_1080x1776]
I/ImageLoader( 7404): Load image from disc cache [http://xxx/yyy.JPG_1080x1776]
I/ImageLoader( 7404): Subsample original image (x192) to x192 (scale = 1) [http://xxx/yyy.JPG_1080x1776]
I/ImageLoader( 7404): Cache image in memory [http://xxx/yyy.JPG_1080x1776]
I/ImageLoader( 7404): Display image in ImageView [http://xxx/yyy.JPG_1080x1776]
// Second time of displaying
I/ImageLoader( 7404): ImageLoader is paused. Waiting... [http://xxx/yyy.JPG_358x357]
I/ImageLoader( 7404): Start display image task [http://xxx/yyy.JPG_358x357]
I/ImageLoader( 7404): Load image from disc cache [http://xxx/yyy.JPG_358x357]
I/ImageLoader( 7404): Subsample original image (x192) to x192 (scale = 1) [http://xxx/yyy.JPG_358x357]
I/ImageLoader( 7404): Cache image in memory [http://xxx/yyy.JPG_358x357]
I/ImageLoader( 7404): Display image in ImageView [http://xxx/yyy.JPG_358x357]
// Third time of displaying
I/ImageLoader( 7404): Load image from memory cache [http://xxx/yyy.JPG_358x357]
Thank you.

This is because of UIL's logic.
1) First time (ImageLoader.displayImage(...)) size of ImageView is unknown because it haven't drawn yet on screen. So UIL considers size of ImageView as full screen size, decodes image to Bitmap of this size (1080x1776, considering aspect ratio) and caches this Bitmap in memory.
2) Second time real size of drawn ImageView is known (which is smaller than full screen size) and UIL search cached Bitmap of appropriate size but cache contains only previous large Bitmap which is too large for our needs. So UIL decodes image again into smaller Bitmap and cache it in memory too.
3) Following displays uses already cached Bitmap of needed size.
So this is just a feature of UIL. I recommend you to use denyCacheImageMultipleSizesInMemory() in configuration to save memory.

NOSTRA's answer is right,to avoid this(load from disk cache when the second time request the image),you can let UIL know the ImageView's width/height before it deocode image, just add "android:maxWidth android:maxHeight" to the ImageView

Related

How to fit image to center while using TouchImageView?

I have a gallery application that needs to zoom and pan. Instead of using default imageview , I found a sample touchmageview on internet and copied it. I called my images with TouchImageView and now i can zoom and pan the images that I called from my drawable folder. Then I added gallery to my project. Now the problem is that I can not fit the images to the screen.When I use scaletype.center then it fits the image to the screen but now zoom and pan features are not working. This is my gallerylistener ;
gallery.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(Display.this, "Your selected position = " + position, Toast.LENGTH_SHORT).show();
// show the selected Image
img.setScaleType(ScaleType.CENTER_INSIDE);
img.setImageResource(Imgid[position]);
}
});
As you see , if I add img.setScaleType(ScaleType.CENTER_INSIDE); then the TouchImageView class doesn't work and I am not able to zoom and pinch and pan etc.. It only display the image. When I delete this code , then I can zoom but then when I change the picture to another one , the new one has the previous image's size. But I want to fit them to the center. Is there any way to reset the scaletype before calling the new image ?
By the way I have only one imageview in xml.
There is a similar question on this link but the solution did not work for me.
how to get setScaleType(ImageView.ScaleType.FIT_CENTER) effect using setScaleType(ImageView.ScaleType.MATRIX)

Windows Store load URL image and get size

I need to load 8 images into Image or BitmapImage elements in my C# Windows Store (Windows RT) app, and need to find their dimensions (even before they are being displayed). I can download the images, but can't really figure out how to get the size (size is always zero). The reason size is zero is that the bitmap image has not loaded, but can't figure out how to wait till the image is loaded, catch the function that indicates image has loaded.
Here is the key code, where the ImageOpened or ImageFailed never get called:
bm.ImageOpened += bm_ImageOpened;
bm.ImageFailed += bm_ImageFailed;
bm = new BitmapImage(new Uri(arbitraryImageUriThatKeepsChanging, UriKind.Absolute));
image.ImageFailed += image_ImageFailed;
image.ImageOpened += image_ImageOpened;
image.Source = new BitmapImage(new Uri(arbitraryImageUriThatKeepsChanging,
UriKind.Absolute));
/* .. at this point the image still has not been loaded,
so can't find the dimensions or if it failed or not, and the functions to catch
image opened and failed are never called..*/
All I need is to load images from external web site, and find the dimensions, before displaying the images.
You could just use a dummy-Imageelement that is invisible to preload the image.
In your Xaml:
<Image Name="DummyImage" Visibility="Collapsed"/>
Code behind:
BitmapImage btm = new BitmapImage(new Uri("http://anyurl.com/foo.jpg"));
btm.DownloadProgress += btm_DownloadProgress;
DummyImage.Source = btm;
private void btm_DownloadProgress(object sender, DownloadProgressEventArgs e)
{
if (e.Progress == 100)
{
int width = ((BitmapImage)sender).PixelWidth;
}
}
You should be able to use either the DownloadProgress or the ImageLoaded event.

Is there a way of finding out what would my orientation have been?

I'm currently writing a portrait only app, but I have a customer requirement that they'd like to implement a special feature if the phone is turned on its side.
To be clear they don't want the page to change orientation - so keeping the page as portrait works well here - but they do want to be able to detect the sideways change.
Is there anyway of finding this out (e.g. from rootframe or from some other object?) or do I have to access the Accelerometer data and work it out myself?
To be clear on this...
I'm trying to keep the page in portrait at all times.
and if I specify SupportedOrientations="portraitorlandscape" then keeping the page in portrait seems to be hard (correct me if I'm wrong, but it just doesn't seem to want to stay in portrait - the MS SDK is too good at making the page go landscape)
and if I don't specify SupportedOrientations="portraitorlandscape" then I don't get calls to OnOrientationChanged in either the page or the RootFrame
And as the icing on the cake... I need the phone to stay in portrait mode too - I need the SystemTray to stay at the top of the screen (the portrait top).
You can handle the OnOrientationChanged event which will return a PageOrientation enumeration.
Accepting this because of the comments:
#Stuart - You may find the Orientation Helper class in this starter kit useful. It uses the accelerometer, so I guess you'll have to use that, but it might save you time rolling out your own version: http://msdn.microsoft.com/en-us/library/gg442298%28VS.92%29.aspx#Customizing_Behavior
This might help, but for a case when arriving to this particular page, not for the initial page - so only partly answering the question. It trigs the OnOrientationChanged although no change has been done! (Figured out this solution after having tried to find a solution for two days) :
On the particular page, write in . xaml code
Orientation="None"
On the .xaml.cs side, write under
InitializeComponent();
Orientation = this.Orientation;
this.OrientationChanged += new EventHandler<OrientationChangedEventArgs>
(OnOrientationChanged);
and separately
void OnOrientationChanged(object sender, OrientationChangedEventArgs e)
{
if ((e.Orientation & PageOrientation.Landscape) != 0)
{
MyImage.Height = 480; //for example
}
{
MyImage.Width = 480; // for example
}
}
In my case, I placed the image as follows:
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel>
<Image x:Name="MyImage"/>
</StackPanel>
.. followed by other code, still loading during which time picture is shown ...
This decreases the size of the image when in Landscape mode when entering the page!
Got the solution, finally, after having seen Jeff Prosises site
Detect orientation change:
http://alan.beech.me.uk/2011/04/19/detecting-orientation-change-wp7dev/
I had to do a similar thing in one of my apps before where an image that is used as the background doesnt rotate but other items on the page do.
The code looks a bit like this:
protected override void OnOrientationChanged(OrientationChangedEventArgs e)
{
// Keep the image in the same position as in portrait
// But still allows other controls to rotate when orientation changes.
switch (e.Orientation)
{
case PageOrientation.LandscapeRight:
ForegroundImage.RenderTransform = new CompositeTransform { Rotation = 90 };
ForegroundImage.RenderTransformOrigin = new Point(0.5, 0.5);
ForegroundImage.Margin = new Thickness(158.592, -158.792, 158.592, -160.558);
break;
case PageOrientation.LandscapeLeft:
ForegroundImage.RenderTransform = new CompositeTransform { Rotation = 270 };
ForegroundImage.RenderTransformOrigin = new Point(0.5, 0.5);
ForegroundImage.Margin = new Thickness(158.592, -158.792, 158.592, -160.558);
break;
default: // case PageOrientation.PortraitUp:
ForegroundImage.RenderTransform = null;
ForegroundImage.RenderTransformOrigin = new Point(0, 0);
ForegroundImage.Margin = new Thickness();
break;
}
base.OnOrientationChanged(e);
}
Unfortunately there's no real work around for the system tray or app bar. For the system tray you could hide this though and then only show it (for a period of time) when the user taps or swipes near that part of the screen.

Image gets clipped while changing orientation using Qt

HI all,
I wnt to develop an ImageViewer using qt. I m trying to resize big images by scaling them. My problem is , when i change the screen orientation some part of the image gets clipped and also if i open the image in landscape mode, by default the size of image remains small even when i change back to portrait mode. What am i Doin wrong?
Please help me out. Heres the code dat i hv written
ImageViewer::ImageViewer()
{
setAttribute(Qt::WA_DeleteOnClose);
QAction *back = new QAction(this);
back->setText(QString("Back"));
connect(back,SIGNAL(triggered()),this,SLOT(close()));
back->setSoftKeyRole(QAction::PositiveSoftKey);
addAction(back);
imageLabel = new QLabel();
imageLabel->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
imageLabel->setAlignment(/*Qt::AlignLeft|*/Qt::AlignCenter);
QWidget *widget = new QWidget;
layout=new QStackedLayout();
layout->addWidget(imageLabel);
widget->setLayout(layout);
setCentralWidget(widget);
}
void ImageViewer::showImage(QString filePath)
{
QImageReader reader;
reader.setFileName(filePath);
QSize imageSize = reader.size();
imageSize.scale(size(), Qt::KeepAspectRatio);
reader.setScaledSize(imageSize);
QImage image = reader.read();
imageLabel->setPixmap(QPixmap::fromImage(image));
imageLabel->adjustSize();
}
You should re-implement QLabel's resizeEvent or install event filter to it and handle QResizeEvent there
The content of showImage method should go to handler of a resize event.
Currently you are using size() of ImageViewer widget (which seems to be derived from QMainWindow), it's better to use imageLabel.size(); or the best QResizeEvent::size() as this will prevent a problem if you will change UI layout in future.

Trying to cache uiImages in dictionary, doesn't seem to affect loading time

I'm using a standard caching method to cache some UIImages loaded from the Documents directory. The images are being displayed in a UITableView. They're pretty large – the images themselves are up to 600x600 and are displayed in imageviews that are 240x180 (on a retina display, so the res discrepancy is not large).
Loading the images in realtime causes some lag when a new cell is about to come onscreen. So I've implemented a caching method in the object that handles the image:
- (UIImage *)imageWithStyle:(NSString *)styleName {
NSLog(#"looking for image %#", self.imageFileName);
/* if we have a cached image in dictionary, return it */
if (imageCache == nil) imageCache = [[NSMutableDictionary alloc] init];
UIImage *returnImage = [imageCache objectForKey:styleName];
if (returnImage != nil) {
NSLog(#"returning cached image");
return returnImage;
}
/* otherwise, look for image at path */
NSString *path = [self cacheFilePathWithStyle:styleName];
UIImage * originalImage = [[UIImage alloc] initWithContentsOfFile:path];
/* if image doesnt exist at path, start download and return nil */
if (originalImage == nil) {
NSLog(#"image not found. downloading.");
[self downloadImageFromS3];
return nil;
}
/* found image at path */
/* scale image for screen */
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)] && [[UIScreen mainScreen] scale] == 2){
returnImage = [UIImage imageWithCGImage:[[originalImage autorelease] CGImage] scale:2.0 orientation:UIImageOrientationUp];
NSLog(#"scaling image for retina");
} else {
returnImage = [originalImage autorelease];
NSLog(#"image scaled for standard resolution");
}
/* cache image in dictionary */
NSLog(#"caching image");
[imageCache setObject:returnImage forKey:styleName];
return returnImage;
}
Before the tableview appears on screen, I force all of the image handling objects to run this caching method to ensure that the images are present in the dictionary to be retrieved when they need to be displayed. By the NSLog's I can see that things are working as they should.
I'm getting lag-free performance now, but only after the image has appeared once on screen. So, when I initially see the tableview, I scroll down and the NSLogs tell me that the images are being retrieved from the cache, but still I get the same loading lag. After a cell has appeared on screen once, it thereafter loads up with no lag.
Is there something I'm missing here? Is there something more that I have to to to actually cache the image? Loading it and putting it in the dictionary doesn't seem to do the trick.
Thanks!
UPDATE
I've given up on this for now. There are some attempts out there to force the images to load by drawing them in a new context, but I'm not familiar with core graphics programming at this point. I've tried some code that folks have shared, but with no luck.
Instead, I'm going to display low-res versions of the images while the tableview is scrolling and load high-res versions when the tableview stops scrolling as announced through its delegate methods. At least I know this approach will work with any number of images.
From the documentation for -[UIImage initWithContentsOfFile:]:
This method loads the image data into memory and marks it as purgeable. If the data is purged and needs to be reloaded, the image object loads that data again from the specified path.
My guess is that, by loading all images into memory, your app consumes too much memory, causing the UIImage class to release the image data it can reload from files later.
If you use [UIImage imageNamed:], it'll do all this caching business for you. No need to roll your own cache and then wonder if it's working.
An upside and a downside to using that method. Upside: If you use the same image file twice, it won't actually load it twice, saving you that memory. Downside: Caching has a big memory impact, and you want to think pretty hard about doing it, whether you roll your own or not.

Resources