2014/08/05

Android ImageView OutOfMemoryError

最近當Android助教時,有同學詢問為什麼切換圖片時會錯誤


接著我在他的LogCat,看到


再來看圖片的資訊,看到某一張為…




以ImageView來說,OutOfMemoryError有可能是載入的圖片檔案太大,造成記憶體不足
再這邊我只提出一種解決方式,使用InputStream方式讀取圖片,並透過BitmapFactory來縮小圖片以及自動釋放記憶體



XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.cy.myimagview.MainActivity" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/button2"
        android:src="@drawable/ic_launcher" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:text="上一張" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/button1"
        android:layout_alignRight="@+id/button1"
        android:layout_below="@+id/button1"
        android:text="下一張" />

</RelativeLayout>


修改前的Code:
package com.cy.myimagview;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class MainActivity extends Activity {
 private Button button1, button2;
 private ImageView imageView;
 private static final int IMAGE_ID[] = { R.drawable.a1, R.drawable.a2,
   R.drawable.a3, R.drawable.a4, R.drawable.a5, R.drawable.a6,
   R.drawable.a7 };

 // 目前圖片的位置
 private static int index = 0;

 // 第一張圖片的位置
 private static final int MIN = 0;

 // 最後一張圖片的位置
 private static final int MAX = IMAGE_ID.length - 1;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  button1 = (Button) findViewById(R.id.button1);
  button2 = (Button) findViewById(R.id.button2);
  imageView = (ImageView) findViewById(R.id.imageView1);
  imageView.setImageResource(IMAGE_ID[0]);

  button1.setOnClickListener(clickListener);
  button2.setOnClickListener(clickListener);
 }

 private Button.OnClickListener clickListener = new OnClickListener() {

  @Override
  public void onClick(View v) {
   switch (v.getId()) {
   case R.id.button1:
    prevImage(); // 上一張
    break;
   case R.id.button2:
    nextImage(); // 下一張
    break;
   default:
    break;
   }
  }
 };

 /***
  * 
  * @param b
  *            true=上一張,false=下一張
  */
 private final void changeImage(boolean b) {

  if (b) { // 上一張
   if (index == MIN)
    index = MAX;
   else
    index--;
  } else { // 下一張
   if (index == MAX)
    index = MIN;
   else
    index++;
  }

  imageView.setImageResource(IMAGE_ID[index]);
 }

 /**
  * 上一張圖片
  */
 private final void prevImage() {
  changeImage(true);
 }

 /**
  * 下一張圖片
  */
 private final void nextImage() {
  changeImage(false);
 }

}



修改後的Code:
package com.cy.myimagview;

import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class MainActivity extends Activity {
 private Button button1, button2;
 private ImageView imageView;
 private static final int IMAGE_ID[] = { R.drawable.a1, R.drawable.a2,
   R.drawable.a3, R.drawable.a4, R.drawable.a5, R.drawable.a6,
   R.drawable.a7 };

 // 目前圖片的位置
 private static int index = 0;

 // 第一張圖片的位置
 private static final int MIN = 0;

 // 最後一張圖片的位置
 private static final int MAX = IMAGE_ID.length - 1;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  button1 = (Button) findViewById(R.id.button1);
  button2 = (Button) findViewById(R.id.button2);
  imageView = (ImageView) findViewById(R.id.imageView1);
  imageView.setImageResource(IMAGE_ID[0]);

  button1.setOnClickListener(clickListener);
  button2.setOnClickListener(clickListener);
 }

 private Button.OnClickListener clickListener = new OnClickListener() {

  @Override
  public void onClick(View v) {
   switch (v.getId()) {
   case R.id.button1:
    prevImage(); // 上一張
    break;
   case R.id.button2:
    nextImage(); // 下一張
    break;
   default:
    break;
   }
  }
 };

 /***
  * 
  * @param b
  *            true=上一張,false=下一張
  * @throws IOException
  */
 private final void changeImage(boolean b) throws IOException {

  if (b) { // 上一張
   if (index == MIN)
    index = MAX;
   else
    index--;
  } else { // 下一張
   if (index == MAX)
    index = MIN;
   else
    index++;
  }
  
  
  BitmapFactory.Options options = new BitmapFactory.Options();
  
  // 自動釋放記憶體
  options.inPurgeable = true;
  options.inInputShareable = true;
  
  // 縮小
  options.inSampleSize = 2;

  InputStream inputStream = this.getResources().openRawResource(
    IMAGE_ID[index]);
  Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
  imageView.setImageBitmap(bitmap);
  inputStream.close();
 }

 /**
  * 上一張圖片
  */
 private final void prevImage() {
  try {
   changeImage(true);
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

 /**
  * 下一張圖片
  */
 private final void nextImage() {
  try {
   changeImage(false);
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

}

執行結果:



參考資料:
http://givemepass.blogspot.tw/2011/11/blog-post_16.html