티스토리 뷰



이 자료들은 팁스소프트에서 제공하는 [ 알짜배기 ] 프로그램을 이용하면 더 편리하게 볼수 있습니다.
* 알짜배기 프로그램 받기 - http://www.tipssoft.com/bulletin/tb.php/QnA/8406
* 안드로이드 강좌 목록 - http://www.tipssoft.com/bulletin/tb.php/old_bbs/501
안드로이드 시스템은 어플리케이션을 개발할때, 모든 기능을 프로그래머가 일일이 개발하기보다
유사한 기능을 가진 액티비티가 있다면 해당 액티비티를 사용하기를 권장하고 있습니다. 따라서
어떤 기능이 필요하다면 해당 기능을 구현하기전에 유사한 기능을 가진 액티비티가 존재하는지
먼저 살펴보는것이 좋습니다.
이번 강좌에서는 사진 촬영을 하는 어플리케이션을 만들것인데, 사진 촬영의 기능을 직접 구현하게되면
사용자가 만족할만한 기능을 제공하기위해서 상당한 노력이 필요할것입니다. 따라서 직접 구현하지않고
다른 사진 촬영 어플리케이션의 엑티비티를 호출하여 사진 촬영을 하도록 하겠습니다.
하지만, 안드로이드 시스템이 탑제된 기기마다 사진 촬영 어플리케이션의 이름이나 액티비티 클래스명이
서로 달라서 명시적인 방법으로 해당 액티비티를 호출하기는 어렵습니다. 따라서 이 강좌에서는
원하는 행위를 명시하여 액티비티를 호출하는 암시적인 방법을 사용할 것입니다.
이제부터 메인 액티비티에서 위와 같은 방법으로 카메라 액티비티를 호출하고, 카메라 액티비티로
촬영한 사진 이미지를 메인 액티비티에서 출력하는 방법에 대하여 알아보도록 하겠습니다. 촬영한
이미지를 출력하는 방법으로는 카메라 액티비티를 사용하여 반환 데이터로 넘어오는 이미지를 출력하는
방법과 촬영한 사진의 저장 경로를 지정해주어 해당 경로에 저장하게 하게하고 저장된 경로를 이용해서
출력하는 방법에 대해 소개하겠습니다.
1. 실행 화면
이 강좌에서 다루는 예제의 구현 방법을 설명하기 전에 이 예제가 어떤 순서로 실행되고, 어떤 출력
형태를 가지는지 먼저 알아보도록 하겠습니다.
① 메뉴에서 SendPictureActivity 를 클릭하여 실행시킵니다. 실행시 메인 액티비티에는 텍스트뷰
하나와 버튼 하나 그리고 이미지뷰가 하나 배치되어 있습니다.
② 메인 액티비티에서 버튼을 클릭하여 카메라 액티비티를 호출합니다. 기존 카메라 어플리케이션의
이미지 촬영용 액티비티가 활성화됩니다.
③ 카메라 액티비티에서 촬영 버튼을 클릭하여 이미지가 저장되면 카메라 액티비티는 종료되고,
메인 액티비티가 다시 활성화됩니다. 이 때, 기존에 아무 것도 출력되지 않던 이미지뷰에 방금
촬영한 이미지가 출력됩니다.
예제를 구현하는 방법은 여러 가지이지만 어플리케이션의 출력 형태나 동작 방법은 모두 동일합니다.
2. 카메라 액티비티의 반환값을 사용하여 촬영 이미지 출력하기
이전 강좌에서 설명한것처럼 액티비티를 실행할때 startActivityForResult 메소드를 사용하면
해당 액티비티가 종료되면서 원하는 실행결과값을 얻을수 있습니다. 하지만, 카메라 액티비티가
반환하는 이미지 정보는 촬영된 실제 이미지가 아니라 일정 비율로 축소된 이미지이고 그 비율은
제품에 따라서 다소 차이가 있습니다.
< 삼성 - 갤럭시 S >
< 팬택 - 베가 레이서 >
이 방법은 구현이 단순해서 사용하기는 편하나 실제 촬영된 이미지를 그대로 얻을수 없기 때문에
간단한 용도로 사용할때에만 사용합니다.
public void onClick(View view)
{
// 카메라 촬영을 할 수 있는 액티비티를 실행할 수 있도록 인텐트 객체를 생성한다.
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
// 인텐트 정보에 맞는 액티비티를 실행한다.
startActivityForResult(intent, 0);
}
public void onActivityResult(int request_code, int result_code, Intent data)
{
// 결과 값이 있고, 액티비티를 시작할 때 0로 설정해둔 결과값인 경우
if(request_code == 0 && result_code == RESULT_OK) {
// intent 의 여분 데이터로 저장된 비트맵 이미지를 얻는다.
Bitmap bmp = (Bitmap)data.getExtras().get("data");
// id_image 라는 이름의 이미지 뷰를 얻는다.
ImageView img_view = (ImageView) findViewById(R.id.id_image);
// 이미지뷰에 비트맵 이미지를 출력한다.
img_view.setImageBitmap(bmp);
}
}
3. 저장한 원래의 이미지 파일을 읽어와서 출력하기
2번 항목에서 소개한 방법은 실제 촬영이미지가 아니라 축소된 형태의 이미지이기 때문에 실제
촬영된 이미지를 사용하고 싶다면 카메라 엑티비티가 직접 저장한 이미지 파일을 사용해야 합니다.
이 방법을 사용하려면 액티비티를 호출할때, putExtra 메소드를 사용해서 카메라 액티비티가
저장할 이미지 경로를 직접 지정한후 액티비티를 호출하면 됩니다. 그리고 호출된 카메라 액티비티가
종료되면 그 반환값을 사용하지 않고 자신이 전달한 이미지 경로에서 이미지 파일을 사용하면 됩니다.
이때 액티비티로 전달하는 이미지 경로는 URL 이 아닌 URI 형식으로 지정하는데, URI는 URL의
상위개념으로서 역할은 URL과 유사하다고 보시면 됩니다. ( URI는 URL과 URN을 포함하는 개념이며
URI의 다양한 형태중에 한 형태가 URL 이라고 보시면 됩니다. 현재 웹관련 자료에서 URL을 사용하고
있긴하지만 공식적으로는 URL 대신 URI로 대체되고 있는 상황입니다. )
이미지 파일을 직접 사용하는 어플리케이션은 파일을 생성하고 저장할 때 내장 메모리의 고유 영역을
사용합니다. 이렇게 저장된 파일은 보안상의 문제로 자신 이외의 다른 어플리케이션이 접근하지
못하는것은 물론이고, 사용자도 직접 접근할수 없습니다.
메인 액티비티와 카메라 액티비티는 하나의 어플리케이션에서 동작하는 액티비티로 보이지만 카메라
액티비티가 다른 어플리케이션에 소속되어 있기 때문에 1 - ② 의 그림에서처럼 카메라 액티비티가
활성화되면 작업관리자에는 아래의 그림처럼 두개의 어플리케이션이 실행된 것처럼 나옵니다.
따라서 호출된 "카메라" 어플리케이션 소속의 카메라 액티비티가 넘겨받은 경로는 다른 어플리케이션의
고유 영역이기 때문에 해당 경로에 접근할수 없어서 촬영된 이미지를 저장할수 없습니다.
이러한 문제를 해결하기 위해서 3.1 처럼 파일에 별도의 속성을 부여하여 다른 어플리케이션의 접근을
허락하도록 하거나 3.2 처럼 아예 외장 메모리에 파일을 저장하여 접근에 대한 문제를 신경쓰지 않는
방법을 사용할 수 있습니다.
3.1 어플리케이션 고유 메모리 영역에 저장된 이미지 출력하기
어플리케이션의 영역에 파일을 저장할 때 사용하는 openFileOutput 메소드를 이용하여 미리 다른
어플리케이션이 파일을 쓸 수 있는 모드로 파일을 생성해 두는 방법입니다. 다른 어플리케이션이
파일에 접근할 수 있도록 파일을 생성하는 것은 권장되는 사항은 아니지만 카메라 액티비티가
해당 파일의 경로에 이미지 데이터를 저장하기 위해서는 이러한 권한문제를 해결해야만 합니다.
public void onClick(View view)
{
// 카메라 촬영을 할 수 있는 액티비티를 실행할 수 있도록 인텐트 객체를 생성한다.
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
try {
// 고유영역에 test_picture.jpg 라는 쓰기용 파일을 생성한다.
// 이 때, 다른 어플리케이션이 이 파일에 데이터를 쓸 수 있도록 속성을 부여한다.
FileOutputStream fos = openFileOutput("test_picture.jpg", Context.MODE_WORLD_WRITEABLE);
// 파일 출력 스트림을 닫는다.
fos.close();
// 어플리케이션의 고유영역 경로를 File 객체로 얻는다.
File path = getFilesDir();
// 고유영역에 있는 test_picture.jpg 파일의 객체를 얻는다.
File file = new File(path, "test_picture.jpg");
// File 객체의 URI 를 얻는다.
Uri uri = Uri.fromFile(file);
// 인텐트에 URI 정보를 저장한다.
// 카메라 액티비티는 이 URI 에 입력된 경로에 촬영한 이미지를 저장한다.
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
// 인텐트 정보에 맞는 액티비티를 실행한다.
startActivityForResult(intent, 0);
} catch (IOException ie) {
// 파일 입출력에 관한 예외 발생시 예외사항을 출력한다.
TextView tv = (TextView) findViewById(R.id.id_tv);
tv.setText(ie.toString());
}
}
public void onActivityResult(int request_code, int result_code, Intent data)
{
// 결과 값이 있고, 액티비티를 시작할 때 0로 설정해둔 결과값인 경우
if(request_code == 0 && result_code == RESULT_OK) {
try {
// 어플리케이션의 고유영역 경로를 File 객체로 얻는다.
File path = getFilesDir();
// 고유영역에 있는 test_picture.jpg 파일의 객체를 얻는다.
File file = new File(path, "test_picture.jpg");
// 파일 객체를 이용하여 파일 입력 스트림을 생성한다.
FileInputStream input_stream = new FileInputStream(file);
// id_image 라는 ID 의 이미지뷰를 얻는다.
ImageView img_view = (ImageView) findViewById(R.id.id_image);
// 기존에 이미지가 설정되어 있었다면 설정된 이미지를 해제한다.
img_view.setImageDrawable(null);

// 입력 스트림을 이용하여 Drawable 객체를 생성하고,
// 해당 객체를 이미지뷰에 출력시킨다.
img_view.setImageDrawable(Drawable.createFromStream(input_stream, "image.jpg"));

// 파일 입력 스트림을 닫는다.
input_stream.close();
// 가비지 컬렉션을 수행한다.
// 메모리 해제가 수동으로 되지 않기때문에 직접 수행시켜야
// 메모리 부족으로 프로그램이 중지되지 않는다.
// 단, 이 작업이 부하가 크기때문에 자주 사용하는 것을 권장하지 않는다.
// 나중에 이것을 보완할 다른 방법에 대하여 알아보도록 하겠다.
System.gc();
} catch (Exception ie) {
// 파일 입출력에 관한 예외 발생시 예외사항을 출력한다.
TextView tv = (TextView) findViewById(R.id.id_tv);
tv.setText(ie.toString());
}
}
}
3.2 외장 메모리 영역에 저장된 이미지 출력하기
안드로이드 시스템은 내장 메모리의 경우 보안에 관련된 부분때문에 어플리케이션이 생성하는
파일에 대하여 접근권한을 두지만 외장 메모리의 경우 확장성과 이동성을 높이기 위한 목적에
맞게 어플리케이션이 생성하는 파일에 대하여 접근 권한을 두지 않습니다.
또한 요즘에 출시되는 안드로이드 기기는 내장된 메모리의 용량이 기가바이트 단위여서 그 크기가
매우 크기때문에 일부를 내장 메모리로 사용하고, 대부분의 공간을 외장 메모리로 인식하기도
합니다. 물론 SD카드를 직접 삽입하여 사용할 수도 있습니다.
public void onClick(View view)
{
// 카메라 촬영을 할 수 있는 액티비티를 실행할 수 있도록 인텐트 객체를 생성한다.
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
// 외장 메모리에서 공유를 목적으로 하는 사진을 저장할 수 있는 폴더 경로를
// File 객체로 얻는다.
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

// 해당 경로에 test_img.jpg 파일명으로 File 객체를 생성한다.
File file = new File(path, "test_img.jpg");
// 폴더 경로에 해당하는 폴더가 존재하지 않으면 폴더를 생성한다.
if(!path.exists()) path.mkdirs();
// 파일 경로가 저장된 File 객체의 URI 를 얻는다.
Uri uri = Uri.fromFile(file);
// 인텐트에 URI 정보를 저장한다.
// 카메라 액티비티는 이 URI 에 입력된 경로에 촬영한 이미지를 저장한다.
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
// 인텐트 정보에 맞는 액티비티를 실행한다.
startActivityForResult(intent, 0);
}
public void onActivityResult(int request_code, int result_code, Intent data)
{
// 결과 값이 있고, 액티비티를 시작할 때 0로 설정해둔 결과값인 경우
if(request_code == 0 && result_code == RESULT_OK) {
try {
// 외장 메모리에서 공유를 목적으로 하는 사진을 저장할 수 있는 폴더 경로를
// File 객체로 얻는다.
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
// 해당 경로에 test_img.jpg 파일명으로 File 객체를 생성한다.
File file = new File(path, "test_img.jpg");

// 파일 객체를 이용하여 파일 입력 스트림을 생성한다.
FileInputStream input_stream = new FileInputStream(file);
// id_image 라는 ID 의 이미지뷰를 얻는다.
ImageView img_view = (ImageView) findViewById(R.id.id_image);
// 기존에 이미지가 설정되어 있었다면 설정된 이미지를 해제한다.
img_view.setImageDrawable(null);

// 입력 스트림을 이용하여 Drawable 객체를 생성하고,
// 해당 객체를 이미지뷰에 출력시킨다.
img_view.setImageDrawable(Drawable.createFromStream(input_stream, "image.jpg"));

// 파일 입력 스트림을 닫는다.
input_stream.close();
// 가비지 컬렉션을 수행한다.
// 메모리 해제가 수동으로 되지 않기때문에 직접 수행시켜야
// 메모리 부족으로 프로그램이 중지되지 않는다.
// 단, 이 작업이 부하가 크기때문에 자주 사용하는 것을 권장하지 않는다.
// 나중에 이것을 보완할 다른 방법에 대하여 알아보도록 하겠다.
System.gc();
} catch (Exception ie) {
// 파일 스트림에 관한 예외 발생시 예외사항을 출력한다.
TextView tv = (TextView) findViewById(R.id.id_tv);
tv.setText(ie.toString());
}
}
}


'프로그램 > 안드로이드 강좌' 카테고리의 다른 글

사진 촬영 서버전송  (0) 2012.08.15
TTS(TextToSpeech)  (0) 2012.08.15
컨텍스트(Context)  (0) 2012.08.15
엑티비티 전환하기 명시적  (0) 2012.08.15
엑티비티의 이해  (0) 2012.08.15
댓글