android: most efficient way to repeat an image - android

I am doing a project where I want 100 of the same image randomly scattered throughout the screen. In the future, I want an image to disappear when the image is tapped by the user.
I am mainly focusing right now on the most efficient way to display a repeated image. It seems like I could set it up so that the fact that the images are the same makes it more efficient; I'm just not sure how. I do not want to proceed further until I know I have a sound base. I'm using .png files.
I've looked around without a definite answer.
Also, would if be easier to draw my object with two circles (which is what my image is), rather than using a bitmap?
Any clues???

Assuming you're talking about drawing the bitmap on a Canvas object, the method should be pretty straightforward. You load the image into a Bitmap object and keep it as a member of the owning class, and draw it 100 times using canvas.drawBitmap(...) functions.
The other way of doing it is having 100 ImageViews with the same image, but I won't even write the details because this would be truly inefficient!

You can use a listView: http://developer.android.com/guide/topics/ui/layout/listview.html
It shows a list of items, using an adapter to inflate elements. It is pretty simple to use.

Related

Memory management when changing src for ImageView

I am still new to Android, and never had to deal with memory management in my previous experience.
In my android application, I have an activity with a TextView, a ListView, and ImageView. I have listeners for the textview and listview, and the code that changes the contents in all three of those views. The contents is stored in the arraylist. The source for the ImageView is stored in form of a String (filenames), and the code that makes the changes looks like this:
tv1.setText(myText);
imgView.setImageResource(myImage);
This worked perfectly well while I only had a few images to test the logic, but once I added more images, I started to get the OutOfMemory error. If I make the images smaller, I get that error a little later in the process, but I still get it.
At first, I thought that Android does not release the previous source, so I thought using recycle() before the reassignment will help. Instead, I've got another error when I try to change the source:
Cannot draw recycled bitmaps
It looks like I am missing some vital understanding about how the ImageView handles the source images. Does it assign the bitmap reference and then keeps the same reference, but somehow changes content?
Then, after reading this article, I realized I have a different kind of problem altogether, the one that might be solved by using the inBitmap. Yet, the documentation says the bitmaps have to be the same size for that, and mine are not.
I am thinking of converting my drawable to bitmap, then scaling it to some hard-coded dimensions, then using inBitmap. I guess my second question is - does this approach make sense? Are there any downfalls in scaling the images? And any examples would be appreciated, of course.
Thank you!

Most efficient way to display a grid of images

What is the most efficient way to display a grid of random images on android?
I have a list of random album art images, I need to generate a grid out of them and use it as a background in my activity, the images are downloaded asynchronously, scaled down and cached, displaying them in a grid seems to consume a lot of RAM,(yes I'm recycling the bitmaps, and using LRU cache)
Would drawing them to a canvas be a better solution? Are there other efficient ways to do that?
Is the GridView safe, are there any guarantees that it won't run out of memory?
P.S. drawing them to a canvas would require me to redraw when the orientation or activity's size changes.
I don´t think there is a more efficient way, at least I can not think of one right now. What you could do, that depends if grid view is necessary, is using the Image carousel from Romain Guy which is made in Renderscript. I do not have the URL at the moment but it is a google-code project.
Another page where sometime are good stuff for those things is http://www.theultimateandroidlibrary.com/all
The LRUCache is really nice and fast and should do the trick. I also would be interested in a more efficient way.....

Android OutOfMemory exception - Is there another approach for my needs?

My app is loading a large image (a house floorplan), then drawing touch-reactive objects (furniture, lamps etc.) on the image. I have a base image file included with my app but the objects come from coords in my database.
I've successfully deployed the app in multiple iterations, but now I need to use larger base images and BitmapFactory is causing an OutOfMemory exception on many devices (both old and new devices; anything with < 32MB heap seems to crash). I've read the 157 OOM questions on SO, but I'm afraid the link they all seem to point to won't help me since resolution / zooming is critical to the app's function.
I've tried to test the device's available memory before loading, but the results are spotty at best (some devices like the galaxy S3 report plenty of heap but still crash). I've also tried decreasing resolution but the image became unusable when reduced to a safe size based on the above test.
Is there another way to implement this design without ever using bitmaps?
I need to:
Load large base image
Create clickable shapes on top of the base image, maintaining their position / scale relative to the base image
BONUS: in the iOS version of my app, I can do SVG-style text scaling so a long label on a small object will stay inside the object
instead of running across the map(and will be invisible until the
image is zoomed). Replicating this in android would make me a happy
code monkey.
I can post code if needed, but you've all seen it before (almost all of it came from SO).
Thanks in advance for reading, and for any help you can give.
you have a few options:
break your large image into tiles, load these tiles into an array, and move a camera object around and only load tiles that need to be drawn, as the comments suggest.
make your image small and scale it up using 'android:scaletype`
Draw lines and curves on a Canvas object at runtime.
Use OpenGL
The appropriate solution really depends on how you want it to look. Tiling will take more dev effort but will look better, just be careful that you are properly cleaning up any tiles that aren't being drawn...
dynamically scaling will be easier, but you cannot guarantee the image won't be blurry.
Drawing on a Canvas object at runtime could work well-- just use Lines of different width and circles and Rects etc.
Using OpenGL will have the steepest learning curve, and might be overkill. This depends on your purpose.
You might like to look into using a "largeHeap"
http://developer.android.com/reference/android/R.styleable.html#AndroidManifestApplication_largeHeap
Here are some options:
1) Use tiles. Use tiles and dynamically load your data. Honestly, this is the best solution. With this solution you can load arbitrarily large images.
I've successfully used this approach for an endless paint canvas and it works quite well. You really only need to draw what is directly visible to the user. Tiles is a way to cast away pieces you don't need. A pyramid of tiles (where you pre-downsample your images and create more tiles), allows you to do this in a clean and fast way.
2) Use native code. The memory restrictions on native code are not the same as Java code. You can get away with allocating more memory.
3) Use OpenGL. Once again, the memory restriction for OpenGL are not the same as Java code.
4) Convert your original plan to an SVG and use an SVG library like this one.
5) Use "largeHeap". I strongly discourage this, as I think a largeHeap is rarely the solution, there are generally cleaner ways to approach the problem.
if the image is static , you might wish to use this nice library:
https://github.com/ManuelPeinado/ImageLayout
if the library doesn't support auto-downsampling of the image, you should do it by yourself, in order to use the best image for the current device (so that you won't get OOM).
for auto-sizing text , you might have some luch with the next post:
Auto-fit TextView for Android

How to avoid OutOfMemory for TextView containing unlimited ImageSpans?

I am developing a Handwriting note-taking app.
The app works in this way: the user write(draw) a text on the touchscreen, the app convert the handwriting to a Bitmap(getBitmapFromHandwriting()), then use this bitmap to produce a SpannableString and show it to the user in a TextView(In my case, I use an EditText, but to make things simple, let's say it's TextView).
Here is the code snippet in the extended TextView:
class MyTextView extends TextView{
...
BitmapDrawable bmd = new BitmapDrawable(getBitmapFromHandwriting());
int width = bmd.getIntrinsicWidth();
int height = bmd.getIntrinsicHeight();
bmd.setBounds(
(int)(height/4.0f),
(int)(height/4.0f),
(int)(width + height/4.0f),
(int)(height*5.0f/4.0f));
final ImageSpan img = new ImageSpan(bmd);
getText().setSpan(img, position, position+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
…
}
In getBitmapFromHandwriting(), the app create a new bitmap for each text drawn by the user.
Everything works fine before the note grows big(means there are lots of bitmaps in the native memory), an OutOfMemoryError is thrown when createBitmap().
After lots of googling, I learned bitmaps use "native memory", which is very limited.
I also learned gc for bitmap doesn't happen at the same time with the gc of the JVM take place. So people would better do bitmap.recycle() when the bitmap is surely not necessary any more.
From the above knowledge, I think I need to recycle the bitmaps for invisible texts , and load that bitmap only when necessary(i.e., visible)
Before recycling a bitmap, I must make sure it won't used in the future, otherwise, will get a "RuntimeException: Canvas: trying to use a recycled bitmap…"
i.e., I have to clear the corresponding spans attached to the SpannableString. (By getText().removeSpan(), or getText().clearSpans(), but that's another story.)
For doing that, I need to tell which strings are visible and which are invisible. In ViewGroups like GridView, I can call "getFirstVisiblePosition()", but I don't know how to do that for a TextView.
I know it's possible to down sample the bitmap to support more bitmaps, but what I want is a TextView supports UNLIMITED ImageSpans.
If it's GridView or ListView, a lazy loading implementation will do the work. But how to do it in a single TextView?
Dividing the content to multiple pages should theoretically work for some situations, but in my app, user can also "export" the note. The exporting simply do a copy of the current view(How to do the export without creating new bitmap is another problem, won't discuss in this thread.). If the note is divided to multiple pages, user have to "export" for each page, so it's not an option.
My questions are:
1. Is it possible to determine the visible strings in TextView?
2. Is there any better way(patterns) to implement such "TextView containing lots of ImageSpans" without getting OutOfMemoryError?
Any idea is appreciated. Thanks in advance!

Android: Turn off lazy loading of listview

In my Android App I have a listview containing 30 rows, and each row consists of several textviews of which one is spannable and sometimes contains a lot of formatted text and images.
Those images are loaded from the web asynchroneously: A placeholder is displayed until the image has been downloaded is then replaced by the image.
Unfortunately, the rows of the listview are loaded when I scroll over them. This makes the whole thing very slow. Also, those images are loaded from the web again and again, whenever I scroll over the row.
How can I turn it off, that the ListView rows are loaded when I scroll over them? They should be loaded once when I start the activity and never again.
Best regards and thanks in advance,
Jan Oliver
When you do a lazy-loading ListView, is because you want to speed it up your app. Turn it off is not the best solution. So, what you can do is implementing a basic cache system in order to avoid downloading and setting the ImageView again and again.
The easiest way to do so is implementing a HashMap with URLs as keys and Bitmaps as values. Something like this:
Map cache = new HashMap();
// then, on your lazy loader
Bitmap image = cache.get(urlOfTheImage);
if( image == null ){
// download and decode the image as normal,
// then assign the decoded bitmap to
// the 'image' variable
cache.put(image);
}
imageView.setImageBitmap(image);
If those images will be the same always, meaning that each time you open the app the same images will be downloaded, then you better save those images in the filesystem and use them from there.
On the other hand, if the images tend to change, you could implement some interesting stuff: use SoftReferences. There's an explanation in this video. This can also be used if you are loading images from the filesystem.
Edit
With regards to your comment, I highly recommend you watching the video I posted. It's one hour long, but it really worths the effort. When using an adapter, checking if the convertView is null is just a simple way to improve performance, though there are some other techniques that will improve your app even more. Also, if you had have problems while using that trick, is because you are probably implementing it the wrong way. Remember: even if you don't re-inflate the views, you do have to set the value of each one of the children views, otherwise you will experience some problems.
If you can, start with an Image Array full of the "placeholder images", then download the images in to an Array firing an AsyncTask during on Create. During row view building just refer to the array. That way if it has the new image it will load it, if not it will get the placeholder.
If you have a lot of data its gonna get real slow and be a crappy expirience for the user.
Create a list of objects that represent each row. Create a loader as a background thread that updates the objects as it loads the data. Your list view will draw data from the objects.
(Not a good idea if you have hundreds of rows and a huge amount of data in each row - in that case, you should only load data within a few rows of the currently active row and have some sort of MRU cache).

Categories

Resources