I have an app for my own use to communicate with arduino. There are custom shape buttons, so i drawn them in PNG as single background, added Buttons
with android:background="@android:color/transparent"
to make them transparent on top almost the size of custom buttons.
In this image you can see: 1. Background - black background with dark blue buttons drawn as single png. 2. Orange png - same size as background, drawing it with match parrent
option. 3. Invisible button - with transparent option, blue square, there are 7 of them.
To eliminate misplacing of indication images i drawn them same size of background in PNG transparent background. I added them to res/drawable
and in layout XML as ImageView
with android:visibility="gone"
, the Layout is FrameLayout
. To indicate that button was pressed i make set VISIBLE
of orange custom button, and when arduino turns on output, sends back confirmation i make i gone and set visible another PNG. It costs a lot of memory. The images dimensions are 1478x2623. At once in worst case i have to show only 8 images + background permanently. And in onCreate method i load all 17 images.
To avoid OOM - out of memory error, i have to resize images and it gives me bad looking...
If visibility of image in XML is android:visibility="gone"
, does it load the image in onCreate? If so, i should create, scale it, and draw only when needed. How to achieve that?
EDIT
Made some changes.
- Images reduced to 1920x1080
- 8 ImageView left on layout and resources are set in method, not in XML
These changes made it faster and no more OOM error. At the moment i am very busy so it will take some time to test methods suggested in answers. I will try ASAP.
Why are your images so big? This resolution (1478x2623) is huge and keeping 8 in memory will indeed lead to OOM exceptions.
If you really must, you can do either of the following:
- Cheap and nasty solution: Use large memory heap. Since this app is for your personal use, this might be the fastest option for you and will work with one line of change in your manifest but I wouldn't recommend it for general apps unless there's a real reason behind why it needs more memory. (To achieve this, see: How to increase heap size of an android application?)
- Use UniversalImageLoadr (https://github.com/nostra13/Android-Universal-Image-Loader) and make sure you do not load all your images at once. For instance, only load your image overlay if and only if it needs to be displayed. You'd do this by setting the image source when you need to and settings the image source for the invisible images to null. Additionally, make sure you recycle the bitmaps you aren't actively using them. Most of this is already being handled by the image loader library though.
- (Use this with 2 in my opinion): You really don't need the resolution you've specified. It's way too large. You should look at the Android guidelines (http://developer.android.com/training/displaying-bitmaps/index.html) or alternatively, use image loader and specify smaller dimensions and load scaled images.
To answer your exact question:
If visibility of image in XML is android:visibility="gone", does it load the image in onCreate? If so, i should create, scale it, and draw only when needed. How to achieve that?
Yes, it will still load the image since it will need to display it at a moment's notice and yes, you should decode bitmaps only when you need it. See above point 2 and 3 as to how to achieve this.
Just a suggestion
Why OOM is happening?
Application is trying to use image with resolution 1478x2623. Which is high resolution image. So naturally when more images are loaded application is going to out of memory sooner.
How to avoid this?
Trying to keep lesser number of images in memory. May be can use LRU cache with defined size. But this is not the solution for his problem because app needs to show more images in same screen.
Now how to solve this?
Can try to take advantage of 9-patch image concept. Where repeating pattern can be represented in lesser pixel. Advantage of this approach is there will not be loss of quality in image because of stretching.
Try it out or give a thought.
Have you tried layer-list
? I think it is designed for just these situations that means putting drawable on top of each other. so what should you do? first reduce your image size then for each button create layer-list
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<bitmap
android:id="@+id/IdofNotPressed"
android:gravity="fill"
android:src="@drawable/notpressed" />
</item>
<item>
<bitmap
android:id="@+id/IdofPressed"
android:gravity="fill"
android:src="@drawable/pressed" />
</item>
</layer-list>
after that set those drawables as a background of your buttons. then get their references like below:
final LayerDrawable background = (LayerDrawable) button.getBackground();
background.getDrawable(0).setAlpha(255); // this is the lowest drawable visibale when no press occured
background.getDrawable(1).setAlpha(0); // this is the top drawable which is shown as press state (orange)it is not visible by default
now when you want to show the press state all you have to do is
background.getDrawable(0).setAlpha(0); // this is the lowest drawable
background.getDrawable(1).setAlpha(255); // this is the top drawable which is shown as press state (orange) now it is visible