0

I have 20 images in my app. All the images are in 640 * 360 resolution with not more than 60KB each.

I make use of Viewpager to slide the images. And use ViewFlipper inside ViewPager to flip the images.. When the user clicks on it, I show the corresponding text for the image.

The issue is that I get OutOfMemory exception when I swipe back and forth for 5 times. I read various Stackoverflow threads here!, here!, here! and Handling Large Bitmaps Efficiently! but not able to fix the issue,

Here is the code,

In Main_Fragment.java,

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {

--- some code ---

--- some more code --- 

View v = inflater.inflate(R.layout.main_fragment, container,false);

viewAnimator = ((ViewAnimator) v.findViewById(R.id.cardFlipper));

TextView caption_text = (TextView) v.findViewById(R.id.caption_text);
caption_text.setText(caption.toUpperCase());

ImageView main_img = (ImageView) v.findViewById(R.id.main_img);

int mainimg_resID = getResources().getIdentifier(mainimg,"drawable",context.getPackageName());

Bitmap icon = decodeSampledBitmapFromResource(getResources(),mainimg_resID,640,360);

main_img.setImageBitmap(icon);

viewAnimator.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
  AnimationFactory.flipTransition((ViewAnimator) v,FlipDirection.LEFT_RIGHT);
}
});

return v;
}


public static int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2; 
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and
            // keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
            int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

Can you please tell me what I'm doing wrong?? This is driving me nuts for the past few days :(

Community
  • 1
  • 1
Naveen
  • 1,040
  • 4
  • 15
  • 38

1 Answers1

0

yes, I'm seen something very wrong that you are doing:

Bitmap icon = decodeSampledBitmapFromResource(getResources(),mainimg_resID,640,360);

you got it all wrong: the reqHeight and reqWidth parameters should be the image view width and height, and not the original image dimension!! that's all what calculateInSampleSize() is all about..

so, you should do the following:

Bitmap icon = decodeSampledBitmapFromResource(getResources(),mainimg_resID,main_img.getWidth(), main_img.getHeight());

by decoding bitmap in scale calculated based on the dimensions needed only for display - the result bitmap allocation would be minimal.

and by the way - the fact that the size of the resource you are using is only 60KB does not change anything. in fact - the image memory size (Kilobites/Megas) does not have any impact on the allocated bitmap. it's the resolution only that makes the difference!

EDIT

because the imageView dimentions are still zero at the onCreateView() method - you should prform the decoding only after it got it dimentions:

--- some code ---

--- some more code --- 

View v = inflater.inflate(R.layout.main_fragment, container,false);

viewAnimator = ((ViewAnimator) v.findViewById(R.id.cardFlipper));

TextView caption_text = (TextView) v.findViewById(R.id.caption_text);
caption_text.setText(caption.toUpperCase());

final ImageView main_img = (ImageView) v.findViewById(R.id.main_img);

ViewTreeObserver vto = main_img.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {

            final ViewTreeObserver obs = main_img.getViewTreeObserver();

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            obs.removeOnGlobalLayoutListener(this);
            } else {
               obs.removeGlobalOnLayoutListener(this);
            }

            // do here the image decoding + setting the image to the image view
            Bitmap icon = decodeSampledBitmapFromResource(getResources(),mainimg_resID,main_img.getWidth(), main_img.getHeight());

            //  setImage....
      }

});

viewAnimator.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
    AnimationFactory.flipTransition((ViewAnimator) v,FlipDirection.LEFT_RIGHT);
    }
});

return v;

EDIT 2

you should never use wrap_content for ImageView. that's because it forces the view to be in the size of the original image (potentially very large) instead of the opposite. when calculating required scale - the whole point is to compare between the ImageView dimentions to the original image(resource/file/stream) dimentions. that's why it does not make any sense use wrap_content.

Tal Kanel
  • 10,475
  • 10
  • 60
  • 98
  • Thanks for your response.. I followed as per this [example](http://developer.android.com/training/displaying-bitmaps/load-bitmap.html)! It asked us to send the resolution only.. However when I say `main_img.getHeight()` as per your suggestion, it is showing the blank image only.. because it will not know the exact width or height. Am I missing something here? – Naveen Apr 03 '14 at 19:00
  • @Naveen: you following the right example, but as I sad - you understood it wrong. the BitmapFactory.decodeResource() knows to calculate the original image dimensions. you get empty bitmap because the height and the width of the imageView not yet calculated on that stage ( onCreateView method), so the height and the width of the imageView are zero at that stage. follow this post how to detect when the actual dimentions are applied - http://stackoverflow.com/questions/4142090/how-do-you-to-retrieve-dimensions-of-a-view-getheight-and-getwidth-always-r – Tal Kanel Apr 03 '14 at 19:17
  • Sorry I'm still confused.. Even after reading the thread that you posted in the above comment, I'm not completely clear on how to get the width and height of the image. Can you point me some working example to slide images using ViewPager and use ViewFlipper in it. Sorry my Google search didn't yield the desired result :( – Naveen Apr 03 '14 at 20:03
  • @Naveen: You don't need to get the width and height of the image. you need to get the height and width of the imageView! that's two different things. – Tal Kanel Apr 03 '14 at 20:09
  • read also this example: http://stackoverflow.com/questions/12352920/measure-view-in-fragment – Tal Kanel Apr 03 '14 at 20:11
  • what happens is that only after onGlobalLayout() method is called - your image view getting it dimentions. that's the place you should call the decode method, with the width and height of the imageView - as I wrote in my answer – Tal Kanel Apr 03 '14 at 20:12
  • Thanks for all your efforts.. I have added `GlobalLayoutListener` as per your code.. but it still gives me ZERO width and height. so no image is not displayed :( BTW I'm testing the app in Samsung Note II and Samsung SIII – Naveen Apr 04 '14 at 05:23
  • @Naveen: does your imageview defined with wrap_content in the xml? – Tal Kanel Apr 04 '14 at 05:32
  • Yes the imageview has `android:layout_width="wrap_content" android:layout_height="wrap_content"` – Naveen Apr 04 '14 at 05:38
  • Ahh thats the ptoblem ! Now I understand why you did not understand what defines the imageview dimntions. You must set to the imageview explicit dimetiomds , or matching it to a layout container (match_parent) with an explicit dimentions – Tal Kanel Apr 04 '14 at 05:40
  • I found out the problem.. ViewFlipper inside Viewpager is consuming quite a lot of memory. If I use only one imageview inside ViewPager the old code itself is working fine. I've removed `calculateInSampleSize` and `decodeSampledBitmapFromResource` functions as well. And use this one line of code `Bitmap icon = BitmapFactory.decodeResource(getResources(), mainimg_resID);` So the OutOfMemory occurs if I use ViewFlipper inside ViewPager. Any ideas on how to fix that? – Naveen Apr 04 '14 at 06:37
  • @Naveen: did you took my advice about the "wrap_content"? (view my EDIT 2 section...) – Tal Kanel Apr 04 '14 at 06:47
  • By using `wrap_content` I made the code work - Please see my above comment. I cannot hardcode any dimension.. It should be `wrap_content` only. After analysis, I found that using ViewFlipper inside ViewPager is the reason behind OutOfMemory. – Naveen Apr 04 '14 at 06:54
  • @Naveen: I don't have any special insights regarding ViewPager+ViewFlipper, but not using them should not be your choice. solving the problem by not doing what you want to do is not a solution.. I insists you should not use wrap_content, even if it makes you reconsider the layout design implementation.. this is very important!!!!! please listen to this advise. I know what I'm saying. – Tal Kanel Apr 04 '14 at 07:32