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