android: retain image in display once loaded from url? - android

i am retriving around 20 images url from an xml , and appearing them in gridview.
thing going nice except one.
when i scroll down new images start loading , but when i again scrolls up. the previously loaded images again stars loading .
how can i resolve this bug. the previously loaded image should be retained in the layout , why it is not retaining itself .
I am using the adapter extends the BaseAdapter to view the images

Gridview like any other views that inherit AdapterView only uses the amount of views that you see on the screen. So when you scroll your views going to be reused. The Adapter's responsibility is to reassign the content to the views.
So, you need to cache your bitmaps. There is a very nice tutorial about this on the Android developer side.
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
(I uses grid view in the example)

Every time you scroll, getView() method of the adapter is called.
So every time one view come in to the screen is built in the getView() method.
You build the new view in that method, I suppose you download the image when that method is called, then one solution is caching the images. If you download few images and they are small size, a simply Map with key url and value bitmap solves the problem, if not, use database.
Every time getView() is called, check first if image is in cache, if image is in cache use it to build the view, if not download it.

use LoaderImageView from this blog http://blog.blundell-apps.com/imageview-with-loading-spinner/ & add following function
public void setImageDrawable(final MyGridViewItem obj) {
mDrawable = null;
if(obj.mDrawable != null){
mDrawable = obj.mDrawable
imageLoadedHandler.sendEmptyMessage(COMPLETE);
return;
}
mSpinner.setVisibility(View.VISIBLE);
mImage.setVisibility(View.GONE);
new Thread(){
public void run() {
try {
mDrawable = getDrawableFromUrl(imageUrl);
obj.mDrawable = mDrawable;
imageLoadedHandler.sendEmptyMessage(COMPLETE);
} catch (MalformedURLException e) {
imageLoadedHandler.sendEmptyMessage(FAILED);
} catch (IOException e) {
imageLoadedHandler.sendEmptyMessage(FAILED);
}
};
}.start();
}

Related

Let ImageView point to new ImageView object Android

I'm setting up an image cache and I have the following
ImageView nimage = (ImageView)v.findViewById(R.id.imView1);
ImageView value = imageCache.get(ei.imageURL);
if (value != null)
nimage = value; // this is the code I want to change
else {
new ImageLoadTask(ei.imageURL, nimage).execute();
imageCache.put(ei.imageURL ,nimage);
}
What do I replace the commented value with. I'm not sure why I can't let one imageView equal another imageView.
Your cache isn't caching the correct things.
You don't want to cache the ImageView itself- an instance of a View is tied to the Context in which it was created, and thus is typically not a great object to cache.
What it sounds like you actually want to cache is the image (the Bitmap object) that you download from the URL that you are providing to your ImageLoadTask. If you cache this, then you can simply call nimage.setImageBitmap() to use your cached bitmap instead of downloading it.

Using Google's ImageWorker to load images in ListView results in java.lang.RuntimeException

Recently, I started using Google ImageWorker class for loading bitmaps on a background thread. This class handles everything, including putting the bitmaps in the ImageView. Google talks about this class (and it's helper classes for Image manipulation and caching) here: http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
The ImageView(s) in my case are parts of list items in a ListView. Here's the interesting part of getView on my ArrayAdapter:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView == null ? inflater.inflate(R.layout.nodelist_item, null) : convertView;
NodeHolder holder = getHolder(view);
Node node = mList.get(position);
holder.txtTitle.setText(node.Title);
//Setting the rest of the different fields ...
//And finally loading the image
if(node.hasArt())
worker.loadImage(node.ImageID, holder.imgIcon);
}
The entire getView method can be seen here: http://pastebin.com/CJbtVfij
The worker object is a very simple implementation of the ImageWorker:
public class NodeArtWorker extends ImageWorker {
protected int width;
protected int height;
public NodeArtWorker(Context context) {
super(context);
addImageCache(context);
//Code for getting the approximate width and height of the ImageView, in order to scale to save memory.
}
#Override
protected Bitmap processBitmap(Object data) {
Bitmap b = getBitmap(data);
if(b == null) return null;
return Utils.resizeBitmap(b, width, height);
}
protected Bitmap getBitmap(Object data) {
//Downloads the bitmap from a server, and returns it.
}
}
This works very well, and the performance is much better now than before. However, if I change the ImageID on some of the items in the list, and call adapter.notifyDataSetChanged() to rebuild the view (and thereby start loading new bitmaps), I get a RuntimeException:
0 java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap#41adf448
1 at android.graphics.Canvas.throwIfRecycled(Canvas.java:1058)
2 at android.graphics.Canvas.drawBitmap(Canvas.java:1159)
3 at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:440)
4 at android.widget.ImageView.onDraw(ImageView.java:1025)
5 at android.view.View.draw(View.java:14126)
The full stacktrace can be seen in the following pastebin entry, but is probably uninteresting, as it is the typical Android view hierarchy redraw stacktrace: http://pastebin.com/DsWcidqw
I get that the Bitmaps are being recycled, but I don't understand exactly where and what can be done about it. As my code comes directly from Google, and is, as far as I understand, the recommended way of doing this, I am very confused.
On Android, Bitmaps can be recycled to be later re-used (much faster than re-creating a Bitmap).
The Bitmap#recycle()method will flag the Bitmap as recycled.
So if you try to set such a recycled bitmap to an ImageView or draw it on a Canvas, you will end up with this exception:
java.lang.RuntimeException: Canvas: trying to use a recycled bitmap
The official demo that you linked on your question is dealing with recycled Bitmaps.
It uses a dedicated method hasValidBitmap() and checks the Bitmap#isRecycled() value:
private synchronized boolean hasValidBitmap() {
Bitmap bitmap = getBitmap();
return bitmap != null && !bitmap.isRecycled();
}
So you need to do the same. When you're searching on your cache for a Bitmap, before applying it to an ImageView, check if it's recycled or not. If it's not you can directly set it, otherwise, you need to update the Bitmap.
To create a new Bitmap from a recycled Bitmap, you can use the Bitmap::createBitmap(Bitmap) method.
You can find more details on this page.
i think this is not the ImageWorker class bug, this is
com.mobeta.android.dslv.DragSortListView.dispatchDraw(DragSortListView.java:797)
issue. so in order to see my answer is correct or not just remove that library and see if it works or not. after that i think the best solutions are:
1- not use that library.
2- report issue on github.
3- solve it by your own and debugge it.
4- or you can set null for image holder bitmap
holder.imgIcon.setBitmapDrawble(null);
and then call worker.loadImage.

Hiding multiple Views before saving - Android

I am overlapping couple of view's dynamically in my app over a bitmap. I would like to delete those view's before saving the bitmap to gallery. Below in the function which adds view's on my bitmap
public void add()
{
relLayout.addView(newRect);
relLayout.addView(newSpeech);
relLayout.addView(editImgv);
relLayout.addView(resizeImgv);
}
There is a button on pressing it the above add() function gets called and all those views get added over my bitmap again.
Before saving the bitmap I would like to delete all editImgv and resizeImgv which were added
over my bitmap.
Any ideas on how to do it?
Thanks in advance :)
I have resolved it by using Vector arrays - I placed all the 'resizeImgv' and 'editImgv' image views which I am adding dynamically in a vector array of type 'ImageView' and when I am about to save I am just setting their visibility to 'GONE' one by one. It was a simple solution in the end :)
//Global
Vector<ImageView> imgv = new Vector<ImageView>();
....
//Adding views to my vector array
public void setImageViewArray(ImageView imgview){
imgv.add(imgview);
}
.....
//when I am about to save
for(int i = 0; i < imgv.size(); i++ ){
if(imgv.get(i).getVisibility() == View.VISIBLE){
imgv.get(i).setVisibility(View.GONE);
}
}

How can I improve the performance (first load time, scroll smoothness) of a GridView rendering many large images?

My application allows users to take photos with their devices camera app, which are saved to an app-specific folder.
Then, users can choose to send any image they choose to one of their contacts, using their choice of their devices email app.
Sadly the "choose images to send" dialog takes a long time to load initially, and is very "choppy" (frequent noticable pauses) when scrolling. I suspect that this is because the images are very large — pictures taken with my camera are around 2.3MB each. The initial screen (15 images displayed) thus needs to pull around 34.5MB off disk before it can render.
GridView layout.xml
<!-- ... -->
<GridView
android:id="#+id/gvSelectImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:numColumns="3"
android:gravity="center" />
<!-- ... -->
Adapter getView()
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
File pic = pics[position];
View checkedImageView =
LayoutInflater.from(context).inflate(R.layout.checked_image,
parent, false);
final ImageView imageView =
(ImageView) checkedImageView.findViewById(R.id.checkableImage);
/* LOAD IMAGE TO DISPLAY */
//imageView.setImageURI(pic.toURI())); // "exceeds VM budget"
Bitmap image;
try {
int imageWidth = 100;
image = getBitmapFromFile(pic, imageWidth); // TODO: slooow...
} catch (IOException e) {
throw new RuntimeException(e);
}
imageView.setImageBitmap(image);
/* SET CHECKBOX STATE */
final CheckBox checkBoxView =
(CheckBox) checkedImageView.findViewById(R.id.checkBox);
checkBoxView.setChecked(isChecked[position]);
/* ATTACH HANDLERS */
checkBoxView.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
CheckableLocalImageAdapter.this.isChecked[position] =
checkBoxView.isChecked();
}
});
imageView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
checkBoxView.setChecked(!checkBoxView.isChecked());
}
});
return checkedImageView;
}
getBitmapFromFile() is just adapted from the answer given at https://stackoverflow.com/a/823966/633626. I note that the code there calls decodeStream() twice and might be doubling my I/O, but I think the dialog will still be too slow and too jerky even if I halve the amount of time to takes to render.
My testing phone is a standard Samsung Galaxy S II if that's relevant. I suspect older devices will be even slower / choppier.
What's the best way to improve performance here? I'm guessing a combination of an AsyncTask to load each image (so that the dialog can load before all images are rendered) and some sort of caching (so that scrolling doesn't require rerendering images and isn't jerky), but I'm lost as to how to fit those pieces in with the rest of my puzzle.
You'll want to work with thumbnails instead. Loading the whole image from the SD card will always be slow. Only load the full version of the one(s) they really want to send.
This isn't to say that caching and async loading are a bad idea. I'd change the code to work with thumbnails first, recheck performance, and then go the async route. You can show a little spinner over the currently loading thumbnail to indicate that there's more to come.
Here's a built-in class to help you retrieve them.
Try loading your Images with a SampleSize into the GridView:
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 7;
Bitmap bitmap = BitmapFactory.decodeFile(f.getPath(), opts);
More Info about the Options here
And also you can swap the image loading into a Thread or AsyncTask.

Android - Gallery of ImageViews with background images?

I am new to Android development, but familiar with the concept of views, controls, objects, XML layouts, C#, etc.
I'm trying to create a horizontally scrolling "list" of images using as much native functionality as possible. (I'm not adverse to using custom components, but I'm trying to learn and optimize as much as possible before hacking something together.)
I currently have a Gallery with an adapter tied to it. The adapter is creating ImageViews as seen in many basic tutorials. On each pass of the adapter, I'm setting the background image for the ImageView. My hope was that I'd be able to position the foreground image to lay on top of the background image at a specific X/Y position. Unfortunately, I haven't made it past the point of getting the background image to behave the way I'd like it to.
Is this even possible with a simple Gallery and an ImageView? Or, do I need to build a custom control of some sort (possibly using nested layouts?) and use that control on each iteration of the adapter?
Any help will be greatly appreciated.
Here's what I'm seeing...
http://philaphan.com/public/stackoverflow/gallery1.png
...and what I'd like to see...
http://philaphan.com/public/stackoverflow/gallery2.png
Here's my code:
public class MyAdapter extends BaseAdapter
{
private Context mContext;
public MyAdapter(Context c)
{
mContext = c;
}
public int getCount()
{
return App.myList.size();
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
String imagePath = App.myList.get(position).thumbnail;
ImageView i = new ImageView(mContext);
i.setLayoutParams(new Gallery.LayoutParams(150, 150));
i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
i.setBackgroundResource(R.drawable.image_bk5);
//i.setBackgroundColor(Color.BLACK);
File f = new File(imagePath);
if (!f.exists())
{
i.setImageResource(R.drawable.image_missing);
}
else
{
Bitmap bmp = BitmapFactory.decodeFile(imagePath);
i.setImageBitmap(bmp);
}
return i;
}
}
I think you can use:
i.setPadding(50, 50, 50, 50); //setpadding(left,top,right,bottom)
Try to use this..
replace i.setScaleType(ImageView.ScaleType.CENTER_INSIDE); with i.setScaleType(ImageView.ScaleType.FIT_XY);
or check your background image . I think your background image width creating problem.
I was able to work around this by using a HorizontalScrollView with a LinearLayout nested within it. I then programmatically add two ImageViews -- one for the background and one for the foreground -- and I position the foreground at whatever X/Y that I choose.
I'd still be interested in knowing if this can be done within a Gallery control, if anyone sees this at a later date.
As a general rule, if you want to add a background image to any kind of inflated gallery or ArrayAdapter or BaseAdapter, then - Make sure that the background image is defined in the parent layout, or parent view, for example the main layout, using the attribute "android:background="#drawable/backupimage".
Do not set the background in the XML representing the custom-row/object-view of the repeating instances.
This way the background image will be displayed only once, appearing static behind all inflated objects, and not duplicated again and again on every single row/object.

Categories

Resources