태그:                     

안드로이드 프로그래밍 중 사진을 출력할 필요가 있었다. 처음에는 단순 무식하게 이미지뷰에 그냥 사진을 로딩해서 붙여넣었다.
처음에 디자이어 HD로 테스트할 때는 이상없이 잘 출력되었다.
그래서 이상없이 프로그래밍이 된 걸으로 알고 구글 플레이에 퍼블리싱을 했다.

그런데 막내의 넥서스S에서 시험하려고 이미지를 등록하고 화면을 몇 번 전환했더니 바로 프로그램이 죽어버렸다.
왜 그런지 이유를 알 수 없었다.

다음으로 갤럭시 S2에서 시험해봤다. 사진을 등록하고 화면에 표시된 것을 보는 순간…사진이 180도 회전되서 출력이 되는 것이다.

이런…이런…퍼블리싱하기 전에 더 테스트를 해봤어야 되는데…했지만 이미 늦은…

구글링으로 이유를 찾아봤다.

이유는 사진이 너무 커서 메모리를 다 먹어버리니까 강제 종료가 된 것이었다.

사진 크기를 줄이는 방법은 쉽게 찾을 수 있었다.

바로 처리하고, 더불어서 모서리까지 라운드로 처리해서 출력하도록 수정했다(그 소스는 위의 참조 링크 두 번째에 있다).

일단 급한 버그를 수정해서 업그레이드하고나니 사진이 90도, 180도, 270도 회전되서 출력하는 문제가 남았다.

그 것도 이리저리 찾아보니 사진의 EXIF 정보를 읽어서 사진 방향을 보정해야 된단다.

다행히도 위의 참조 링크 첫 번째에 있는 방법으로 처리하니 잘 출력이 된다.

이미지 처리와 관련된 부분만 뽑아서 ImageUtil이란 클래스로 만들고, 메소드는 다 static으로 해서 바로 뽑아쓸 수 있게 만들었다.

아래에 예제 프로젝트 전문을 게재한다. 예제 프로젝트는 화면 가운데에 ImageView를 하나 놓고, ImageView 부분을 누르면 갤러리(또는 사진 보는 프로그램)에서 사진을 불러와서 적절하게 가공해서 ImageView에 표시한다.

main.xml

[code]
<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
    android:layout_width=”fill_parent”
    android:layout_height=”fill_parent”
    android:orientation=”vertical” >

    <TextView
        android:id=”@+id/txtSizeInfo”
        android:layout_width=”fill_parent”
        android:layout_height=”wrap_content”
        android:text=”@string/hello”
        />

    <ImageView
        android:id=”@+id/imgView”
        android:layout_width=”240dip”
        android:layout_height=”240dip”
        android:layout_gravity=”center_horizontal”
        android:padding=”2dip”
        android:background=”#fccc”
        android:contentDescription=”@string/hello”
        />
</LinearLayout>
[/code]

MyImageViewActivity.java

[code]
public class MyImageViewActivity extends Activity {
    public static final int REQUEST_CODE_PICKALBUM = 101;

    private String mImgPath;

    ImageView iv;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        iv = (ImageView)findViewById(R.id.imgView);
        iv.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_PICK,
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
                startActivityForResult(Intent.createChooser(intent, “앨범에서 불러오기”),
                        REQUEST_CODE_PICKALBUM);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        if (requestCode == REQUEST_CODE_PICKALBUM) {
            if (resultCode == RESULT_OK) {
                // 앨범인 경우
                Uri mImageUri = data.getData();

                // 이미지 Path 취득
                mImgPath = getPath(mImageUri);
                updateImageView();
            }
        }
    }

    private String getPath(Uri uri) {
        String[] projection = { MediaStore.Images.Media.DATA };

        Cursor cursor = managedQuery(uri, projection, null, null, null);
        int column_index = cursor
        .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();

        return cursor.getString(column_index);
    }

    private void updateImageView() {
        int degree = ImageUtil.GetExifOrientation(mImgPath);
        Bitmap resizeBitmap = ImageUtil.loadBackgroundBitmap(
                MyImageViewActivity.this, mImgPath);
        Bitmap rotateBitmap = ImageUtil.GetRotatedBitmap(resizeBitmap, degree);
        Bitmap roundBitmap = ImageUtil.getRoundedCornerBitmap(rotateBitmap);
        iv.setImageBitmap(roundBitmap);
        resizeBitmap.recycle();
    }
}
[/code]

ImageUtil.java

[code]
/**
  * Image 처리에 관련된 기능들을 모아놓은 유틸리티 클래스.
  *
  * @author : nexturbo
  * @create : 2012.4.24
  */
public class ImageUtil {
    /**
      * 비트맵의 모서리를 라운드 처리 한 후 Bitmap을 리턴
      *
      * @param bitmap
      *       bitmap handle
      * @return Bitmap
      */
    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
            bitmap.getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = 10;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        bitmap.recycle();
        bitmap = output;

        return bitmap;
    }

    /**
      * 지정한 패스의 파일을 화면 크기에 맞게 읽어서 Bitmap을 리턴
      *
      * @param context
      *       application context
      * @param imgFilePath
      *       bitmap file path
      * @return Bitmap
      * @throws IOException
      */
    public static Bitmap loadBackgroundBitmap(Context context, String imgFilePath) {
        File file = new File(imgFilePath);
        if (file.exists() == false) {
            return null;
        }

        // 폰의 화면 사이즈를 구한다.
        Display display = ((WindowManager)context.getSystemService(
                Context.WINDOW_SERVICE)).getDefaultDisplay();
        int displayWidth = display.getWidth();
        int displayHeight = display.getHeight();

        // 읽어들일 이미지의 사이즈를 구한다.
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Config.RGB_565;
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imgFilePath, options);

        // 화면 사이즈에 가장 근접하는 이미지의 스케일 팩터를 구한다.
        // 스케일 팩터는 이미지 손실을 최소화하기 위해 짝수로 한다.
        float widthScale = options.outWidth / displayWidth;
        float heightScale = options.outHeight / displayHeight;
        float scale = widthScale > heightScale ? widthScale : heightScale;
               
        if (scale >= 8)
            options.inSampleSize = 8;
        else if (scale >= 6)
            options.inSampleSize = 6;
        else if (scale >= 4)
            options.inSampleSize = 4;
        else if (scale >= 2)
            options.inSampleSize = 2;
        else
            options.inSampleSize = 1;
        options.inJustDecodeBounds = false;

        return BitmapFactory.decodeFile(imgFilePath, options);
    }      

    /**
      * 지정한 패스의 파일의 EXIF 정보를 읽어서 회전시킬 각도 구하기
      *
      * @param imgFilePath
      *       bitmap file path
      * @return degree
      */
    public synchronized static int GetExifOrientation(String filepath) {
        int degree = 0;
        ExifInterface exif = null;
       
        try {
            exif = new ExifInterface(filepath);
        }
        catch (IOException e) {
            Log.e(“TAG”, “cannot read exif”);
            e.printStackTrace();
        }
       
        if (exif != null) {
            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
           
            if (orientation != -1) {
                // We only recognize a subset of orientation tag values.
                switch(orientation) {
                    case ExifInterface.ORIENTATION_ROTATE_90:
                        degree = 90;
                        break;
                       
                    case ExifInterface.ORIENTATION_ROTATE_180:
                        degree = 180;
                        break;
                       
                    case ExifInterface.ORIENTATION_ROTATE_270:
                        degree = 270;
                        break;
                }
            }
        }
       
        return degree;
    }

    /**
      * 지정한 패스의 파일을 EXIF 정보에 맞춰 회전시키기
      *
      * @param bitmap
      *       bitmap handle
      * @return Bitmap
      */
    public synchronized static Bitmap GetRotatedBitmap(Bitmap bitmap, int degrees) {
        if (degrees != 0 && bitmap != null) {
            Matrix m = new Matrix();
            m.setRotate(degrees, (float) bitmap.getWidth() / 2,
                    (float) bitmap.getHeight() / 2 );
            try {
                Bitmap b2 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                        bitmap.getHeight(), m, true);
                if (bitmap != b2) {
                    bitmap.recycle();
                    bitmap = b2;
                }
            }
            catch (OutOfMemoryError ex) {
                // We have no memory to rotate. Return the original bitmap.
            }
        }
       
        return bitmap;
    }
}
[/code]

관련글
안드로이드 이미지 라운드처리하기 및 가로세로 비율 맞춰... imageview에 출력할 이미지의 모서리 라운드 처리하기 원문 링크 : 안드로이드 이미지 라운드처리하기 public Bitmap getRoundedCornerBitmap(Bitmap bitmap) { &nb...
Intel HAXM 설치 후 동작하는지 확인하기... 안드로이드나 타이젠 에뮬레이터를 띄우려면 상당한 리소스와 시간이 소요된다. 이걸 더 원할하게 할 수 있도록 도와주는 역할을 하는게 Intel HAXM인데 설치를 해도 백그라운드에서 조용히 돌아가는 프로그램이라 그냥 ...
Tizen Security Profiles 만들기... 처음으로 타이젠 프로젝트를 만들고 빌드 한 뒤 실행을 하려면 패키지에 사인을 해야 된다고 아래와 같은 경고 창이 뜬다. 이 상태에서 창의 링크(Preferences > Security Profiles)를 ...
안드로이드 – 카운트 다운 타이머(Count... Handler를 이용하지 않고도 일정 시간이 지난 뒤에 뭔가를 하고 싶은 경우에 사용할 수 있는 CountDownTimer 클래스가 있다. 카운트 다운 타이머는 총 시간과 인터벌을 주고 시작하면 총 시간동안 인터벌...
안드로이드 다국어 지원 앱 작성시…... 안드로이드로 다국어를 지원하는 앱을 만드는 방법은 아주 간단하다. 프로그램 코드는 언어에 따라 변경할 것이 거의 없고, 문자열과 화면에 출력할 아이콘이나 그림 파일만 해당 언어에 맞게 작성해서 별도의 폴더에 넣으면 ...
ActionBarSherlock를 이용한 탭 내비게이션... 바로 아래 글에 이어 ActionBarSherlock를 이용한 탭 내비게이션 프로그램을 만들어보자. 탭을 3개로 했을 때 필요한 소스 파일은 총 5개. 메인 액티비티 탭 리스너 댑1 처리 소스 탭2 처리 소스 ...
이미지뷰에 맞게 사진 로딩하기(회전 보정 추가)

이미지뷰에 맞게 사진 로딩하기(회전 보정 추가)”에 대한 4개의 생각

  • 2015년 7월 5일 7:42 오전
    고유주소

    앱 개발 도중에 이미지가 회전하고

    크기 때문에 오류가 나서 이것저것 찾아보고 있었는데

    덕분에 큰 문제 2개나 해결되었습니다 ㅠㅠ

    정말 유용한 정보 감사드립니다!!!

    응답
    • 2015년 7월 6일 6:32 오후
      고유주소

      도움이 되었다니 저도 기분이 좋네요. ^^

      응답
  • 2015년 8월 18일 9:25 오후
    고유주소

    상세한 코드 감사합니다.
    급했는데 복붙해서 살짝만 손봐주니까 바로 되네요 !!

    응답
    • 2015년 8월 26일 4:01 오전
      고유주소

      네…유용하게 쓰세요. ^^

      응답

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다