0

I have created a map of a country, when I click on each region I want the region to be highlighted. There are 17 regions and any combination can be selected. To do this I set all the images to invisible except the background country map, then set each image to visible when the user clicks the associated region.

As I am loading multiple images at once the amount of memory used is approaching 120mb. How can I reduce the amount of memory used and still accomplish the same problem?

Each image is only about 30kb.

I modified the code from here

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

/**
 * Created by Sam Murtagh on 23/10/13.
 */


public class MetActivity extends Activity
        implements View.OnTouchListener {

    static Integer colorFN = Color.rgb(0, 0, 255);
    static Integer colorTA = Color.rgb(255, 0, 0);
    static Integer colorED = Color.rgb(255, 255, 0);
    static Integer colorTK = Color.rgb(0, 255, 0);
    static Integer colorCP = Color.rgb(0, 255, 255);
    static Integer colorMH = Color.rgb(255, 0, 255);
    static Integer colorSA = Color.rgb(0, 166, 81);
    static Integer colorDV = Color.rgb(255, 255, 255);
    static Integer colorTN = Color.rgb(0, 174, 240);
    static Integer colorST = Color.rgb(247, 151, 121);
    static Integer colorWW = Color.rgb(83, 71, 65);
    static Integer colorKA = Color.rgb(189, 140, 191);
    static Integer colorAL = Color.rgb(96, 57, 19);
    static Integer colorPL = Color.rgb(0, 54, 99);
    static Integer colorFD = Color.rgb(125, 167, 217);
    static Integer colorCY = Color.rgb(172, 212, 115);
    static Integer colorGE = Color.rgb(75, 0, 73);

    private String metflightCode = null;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_met);

        ImageView iv = (ImageView) findViewById(R.id.image);
        if (iv != null) {
            iv.setOnTouchListener(this);
        }
    }

    public boolean onTouch(View v, MotionEvent ev) {
        boolean handledHere = false;

        final int action = ev.getAction();

        final int evX = (int) ev.getX();
        final int evY = (int) ev.getY();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                int touchColor = getHotspotColor(R.id.image_areas, evX, evY);
                int tolerance = 25;

                ImageView imageView2 = null;
                String arforCode = null;

                if (closeMatch(colorFN, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_fn);
                    arforCode = "FN";
                } else if (closeMatch(colorTA, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_ta);
                    arforCode = "TA";
                } else if (closeMatch(colorED, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_ed);
                    arforCode = "ED";
                } else if (closeMatch(colorTK, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_tk);
                    arforCode = "TK";
                } else if (closeMatch(colorCP, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_cp);
                    arforCode = "CP";
                } else if (closeMatch(colorMH, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_mh);
                    arforCode = "MH";
                } else if (closeMatch(colorSA, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_sa);
                    arforCode = "SA";
                } else if (closeMatch(colorDV, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_dv);
                    arforCode = "DV";
                } else if (closeMatch(colorTN, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_tn);
                    arforCode = "TN";
                } else if (closeMatch(colorST, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_st);
                    arforCode = "ST";
                } else if (closeMatch(colorWW, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_ww);
                    arforCode = "WW";
                } else if (closeMatch(colorKA, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_ka);
                    arforCode = "KA";
                } else if (closeMatch(colorAL, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_al);
                    arforCode = "AL";
                } else if (closeMatch(colorPL, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_pl);
                    arforCode = "PL";
                } else if (closeMatch(colorFD, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_fd);
                    arforCode = "FD";
                } else if (closeMatch(colorCY, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_cy);
                    arforCode = "CY";
                } else if (closeMatch(colorGE, touchColor, tolerance)) {
                    imageView2 = (ImageView) findViewById(R.id.image_ge);
                    arforCode = "GE";
                }

                if (imageView2 != null) {
                    if (imageView2.getVisibility() == View.INVISIBLE) {
                        imageView2.setVisibility(View.VISIBLE);
                        if (metflightCode == null)
                            metflightCode = arforCode;
                        else
                            metflightCode = metflightCode + ' ' + arforCode;
                    } else {
                        imageView2.setVisibility(View.INVISIBLE);
                        String[] extract = metflightCode.split(arforCode);
                        metflightCode = TextUtils.join("", extract);
                    }
                }

                Toast.makeText(this, metflightCode, Toast.LENGTH_LONG).show();

                handledHere = true;
                break;
            default:
                handledHere = false;
        } // end switch

        return handledHere;
    }

    public int getHotspotColor(int hotspotId, int x, int y) {
        ImageView img = (ImageView) findViewById(hotspotId);
        img.setDrawingCacheEnabled(true);
        Bitmap hotspots = Bitmap.createBitmap(img.getDrawingCache());
        img.setDrawingCacheEnabled(false);
        return hotspots.getPixel(x, y);
    }

    /**
     * Return true if the two colors are a pretty good match.
     * To be a good match, all three color values (RGB) must be within the tolerance value given.
     *
     * @param color1    int
     * @param color2    int
     * @param tolerance int - the max difference that is allowed for any of the RGB components
     * @return boolean
     */

    public boolean closeMatch(int color1, int color2, int tolerance) {
        if ((int) Math.abs(Color.red(color1) - Color.red(color2)) > tolerance) return false;
        if ((int) Math.abs(Color.green(color1) - Color.green(color2)) > tolerance) return false;
        if ((int) Math.abs(Color.blue(color1) - Color.blue(color2)) > tolerance) return false;
        return true;
    }


}

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_frame"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
     >
    <ImageView
        android:id="@+id/image_areas"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_clickable"
        />

    <ImageView
        android:id="@+id/image"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:src="@drawable/arfor_outline"
        />

    <ImageView
        android:id="@+id/image_fn"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_fn"
        />

    <ImageView
        android:id="@+id/image_ta"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_ta"
        />

    <ImageView
        android:id="@+id/image_ed"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_ed"
        />

    <ImageView
        android:id="@+id/image_tk"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_tk"
        />

    <ImageView
        android:id="@+id/image_cp"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_cp"
        />

    <ImageView
        android:id="@+id/image_mh"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_mh"
        />

    <ImageView
        android:id="@+id/image_sa"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_sa"
        />

    <ImageView
        android:id="@+id/image_st"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_st"
        />

    <ImageView
        android:id="@+id/image_dv"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_dv"
        />

    <ImageView
        android:id="@+id/image_tn"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_tn"
        />

    <ImageView
        android:id="@+id/image_ww"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_ww"
        />

    <ImageView
        android:id="@+id/image_ka"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_ka"
        />

    <ImageView
        android:id="@+id/image_al"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_al"
        />

    <ImageView
        android:id="@+id/image_pl"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_pl"
        />

    <ImageView
        android:id="@+id/image_fd"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_fd"
        />

    <ImageView
        android:id="@+id/image_cy"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_cy"
        />

    <ImageView
        android:id="@+id/image_ge"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="invisible"
        android:src="@drawable/arfor_ge"
        />

    <ImageView
        android:id="@+id/image_text"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitCenter"
        android:visibility="visible"
        android:src="@drawable/arfor_text"
        />
</FrameLayout>
Reafidy
  • 8,240
  • 5
  • 51
  • 83

2 Answers2

1

It is difficult to do this task by having multiple images.It is going to occupy lots of memory.I would like to suggest you to follow concept of ImageMapping instead of dealing with multiple images.you can achieve same functionality in more precise manner and obviously by using just one image.

Reference:

the detailed answer which I have posted here

the library and example project from github : AndroidImageMap

Community
  • 1
  • 1
Mehul Joisar
  • 15,348
  • 6
  • 48
  • 57
  • Thanks @Mehul, I had a read over your article and had a go at the image mapping online tool and managed to get some coordinates. But I don't quite understand how I would highlight the selected regions once the user has clicked them. Your solution only seems to help with the clickable areas. What am I missing? – Reafidy Oct 23 '13 at 09:24
  • @Reafidy: yes,it deals with only click handling.but you already know the co-ordinates of clicked portion.so you may try to overlay some other view on that particular portion which may have some color to represent highlighted area. – Mehul Joisar Oct 23 '13 at 09:31
  • its the overlay that's the problem, the only way I know how to do it is with images which means the memory blows out. I don't know any other way to overlay a colour as you mention. – Reafidy Oct 23 '13 at 09:35
  • Unfortunately this solution does not work @Mehul. The clickable areas are a less complicated shape than the actual regions to be highlighted. Thanks anyway. – Reafidy Oct 24 '13 at 06:21
1

It's not the compressed file size of the image that matters but the dimensions and the RGB config you choose, because you are decoding to Bitmaps.

I would recommend you programmatically load the "invisible" images when a click occurs instead of preloading a layout with a bunch of images with their corresponding ImageViews.

When you load the image you can use at BitmapFactory.decodeResource and pass it a BitmapFactory.Options with inPrefferedConfig set to the RGB_565 (2 bytes per pixel) or ALPHA_8 (1 byte per pixel) depending on the nature of the images, by default it is ARGB_8888 which takes up 4 bytes per pixel. In my use cases RGB_565 gives you quite a good quality at half the bytes, so I would go with that.

Emil Davtyan
  • 13,808
  • 5
  • 44
  • 66
  • Thanks @Emil, it is possible that all regions could be clicked and need to be displayed at once. So wouldnt that cause the same problem as loading them all at the start? – Reafidy Oct 23 '13 at 09:30
  • Well if you do it this way you could at least manage the RGB options I mentioned, and that could half the memory usage. – Emil Davtyan Oct 23 '13 at 09:31
  • Also if something is not visible you should attempt to unload it from memory and doing this programmatically will help you do that. Because the phone should be able to hold a `Bitmap` a few times the dimensions of the screen in memory. – Emil Davtyan Oct 23 '13 at 09:34
  • I see what you mean by changing the RGB settings now. I will give that a try and see if it helps. – Reafidy Oct 23 '13 at 09:43
  • I have tried your suggestion does not seem to make any difference to the memory used. Here is what I am using: `options.inPreferredConfig = Bitmap.Config.RGB_565; options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options);` – Reafidy Oct 23 '13 at 21:17
  • I managed to test your solution using `Bitmap.CreateBitmap` instead @Emil. I could not get it to work as you suggested with `BitmapFactory.decodeResource`. Unfortunately your method does not work because RGB sets the transparent background in my images to black. Thanks for your help anyway. – Reafidy Oct 24 '13 at 06:17
  • The solution was to use decodeStream instead of decodeResource. I placed all images in the assets folder. For some reason when loaded from decodeStream android uses less memory. It still uses lots of RAM so not an ideal solution but its the best I have got. Cheers. – Reafidy Oct 25 '13 at 19:53