2

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.

enter image description here

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.

  1. Images reduced to 1920x1080
  2. 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.

Martynas
  • 627
  • 2
  • 8
  • 28
  • @Funkystein i understand that, its `FrameLayout` so i draw then each on top and it does not effect `gone` or `invisible`. – Martynas Oct 31 '14 at 11:54

3 Answers3

4

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:

  1. 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?)
  2. 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.
  3. (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.

Community
  • 1
  • 1
kha
  • 19,123
  • 9
  • 34
  • 67
  • I use `android:largeHeap="true"` to avoid OOM error. Could you guide me for a resolution? As you can see in image area around solid circle should be straight lines. But when resized it comes rounded... I am doing this app for a 10.1" 4.4.2 tablet – Martynas Nov 03 '14 at 08:22
  • If you're resizing images, you should resize all of your images while maintaining the correct aspect ratio. I would also suggest playing around with the scaleType in your ImageView to see if any of them give you the desired behaviour. Remember, you don't *have to* only resize in android, you can also use photoshop or other image editing tools to aid you. – kha Nov 03 '14 at 08:34
  • I am not clear. I resize images wiht PAINT.NET. I am not resizing images in android, i just make them `visible` or `gone`. At the moment i use `fitXY` scaleType, and it fits very good. I will check Universal loader and give you feedback. – Martynas Nov 03 '14 at 08:45
  • Hmm.. your answer gave me some thoughts. I have 15 `ImageView`s in my layout with set `src`. Why am i doing it in XML and not in code like `ImageView.setImageResource(...)`. That's strange why i didn't think about this – Martynas Nov 03 '14 at 09:23
  • Yep. Doing it in code gives you a lot more flexibility especially since you only need one (or at most, a few of them) active at any given point. – kha Nov 03 '14 at 09:36
2

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.

ZAN
  • 86
  • 5
  • It's quite difficult to understand what you are trying to say. Please rephrase it so it's more readable :) – Alfergon Nov 05 '14 at 08:25
  • Thank you ZAN for suggestion, but could you be more accurate? I didn't really get what you mean – Martynas Nov 05 '14 at 10:12
  • @Alfergon, I am assuming you are aware of the concept called 9-patch. 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. – ZAN Nov 05 '14 at 10:29
  • 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. http://tekeye.biz/2012/android-9patch-files – ZAN Nov 05 '14 at 10:29
  • @ZAN No I'm not aware of said concept. If it's useful for the question, please include it in your answer (by editing it) so the user asking the question can have more info. – Alfergon Nov 05 '14 at 11:00
  • @ZAN at the moment i reduced images to 1920x1080. I have a big lack in graphics... At the moment i have 8 `ImageView` and `setImageResource` at needed time. It does not give OOM anymore. It is a little bit difficult to understand 9-patch at the moment. I need to make test project and test it :) – Martynas Nov 06 '14 at 06:15
  • @Martynas It is difficult to understand but worth the effort. With out this you can't scale your application much. – ZAN Nov 06 '14 at 10:15
2

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 
mmlooloo
  • 18,937
  • 5
  • 45
  • 64
  • hi mmlooloo. I reduced images to 1920x1080. I can't set images as background of button because they overlay, so i just make them invisible and size they won't overlay. At click moment i show image so user gets feedback. @kha gave me some thoughts and i am working on it. But there are 24hrs only in a day:) – Martynas Nov 06 '14 at 06:07
  • hi :-)."I can't set images as background of button because they overlay" I just want you to put your images in layer-list and handle it like you have just one background. if you have some time try it. – mmlooloo Nov 06 '14 at 06:16
  • To make it clear. There are 3 states for each "button" 1. Begin(or no image displayed when not pressed) 2. In progress(orange image when pressed) 3. Done(blue image not shown in question image when feedback got). States changes when gets feedback from server. Its kind of not the state of button – Martynas Nov 06 '14 at 06:43
  • To make it clearer, you can handle as many states as you want with my solution. it is just a sample that I used 2 bitmaps. anyway do what ever you like. – mmlooloo Nov 06 '14 at 07:00
  • i will try your suggestion :) i just wanted to be as much clear as i can :) i feel like i disapointed you **anyway do what ever you like.** – Martynas Nov 06 '14 at 07:02
  • just to show you 3 states take a look at this [post](http://stackoverflow.com/questions/25867376/animation-in-viewpager-tab-change-fadein-fadeout-as-like-linkedin-introduction/26154181#26154181) which I used 3 wallpapers. – mmlooloo Nov 06 '14 at 07:05
  • I cant get it working. I use your 3 wallpaper example. Imports are `android.support.v4.app.*` and i get `fragment=new FragmentA;` with an error `Type mismatch` – Martynas Nov 06 '14 at 13:55
  • that post is not suitable for your project I just want to show you that it is possible to have 3 states. What you should do is in my answer, create layer drawable and set it as a background and .... you do not need fragment and ... – mmlooloo Nov 06 '14 at 13:59
  • anyway i liked it :) and i want to give a shot. There are more work and i think this could be helpful – Martynas Nov 06 '14 at 14:04