안드로이드 프로그래밍 중 사진을 출력할 필요가 있었다. 처음에는 단순 무식하게 이미지뷰에 그냥 사진을 로딩해서 붙여넣었다.
처음에 디자이어 HD로 테스트할 때는 이상없이 잘 출력되었다.
그래서 이상없이 프로그래밍이 된 걸으로 알고 구글 플레이에 퍼블리싱을 했다.
그런데 막내의 넥서스S에서 시험하려고 이미지를 등록하고 화면을 몇 번 전환했더니 바로 프로그램이 죽어버렸다.
왜 그런지 이유를 알 수 없었다.
다음으로 갤럭시 S2에서 시험해봤다. 사진을 등록하고 화면에 표시된 것을 보는 순간…사진이 180도 회전되서 출력이 되는 것이다.
이런…이런…퍼블리싱하기 전에 더 테스트를 해봤어야 되는데…했지만 이미 늦은…
구글링으로 이유를 찾아봤다.
이유는 사진이 너무 커서 메모리를 다 먹어버리니까 강제 종료가 된 것이었다.
사진 크기를 줄이는 방법은 쉽게 찾을 수 있었다.
바로 처리하고, 더불어서 모서리까지 라운드로 처리해서 출력하도록 수정했다(그 소스는 위의 참조 링크 두 번째에 있다).
일단 급한 버그를 수정해서 업그레이드하고나니 사진이 90도, 180도, 270도 회전되서 출력하는 문제가 남았다.
그 것도 이리저리 찾아보니 사진의 EXIF 정보를 읽어서 사진 방향을 보정해야 된단다.
다행히도 위의 참조 링크 첫 번째에 있는 방법으로 처리하니 잘 출력이 된다.
이미지 처리와 관련된 부분만 뽑아서 ImageUtil이란 클래스로 만들고, 메소드는 다 static으로 해서 바로 뽑아쓸 수 있게 만들었다.
아래에 예제 프로젝트 전문을 게재한다. 예제 프로젝트는 화면 가운데에 ImageView를 하나 놓고, ImageView 부분을 누르면 갤러리(또는 사진 보는 프로그램)에서 사진을 불러와서 적절하게 가공해서 ImageView에 표시한다.
main.xml<?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>
MyImageViewActivity.javapublic 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(); } }
ImageUtil.java/** * 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; } }
앱 개발 도중에 이미지가 회전하고
크기 때문에 오류가 나서 이것저것 찾아보고 있었는데
덕분에 큰 문제 2개나 해결되었습니다 ㅠㅠ
정말 유용한 정보 감사드립니다!!!
도움이 되었다니 저도 기분이 좋네요. ^^
상세한 코드 감사합니다.
급했는데 복붙해서 살짝만 손봐주니까 바로 되네요 !!
네…유용하게 쓰세요. ^^