<activity  android:name="view.PopupLayout"
           parent="android:style/Theme.Translucent"
           android:screenOrientation="landscape" 
android:theme="@style/full_screen"
           android:configChanges="keyboardHidden">
       </activity>


import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.widget.ImageView;


public class IntroViewAcitivity extends BaseActivity {
 
 protected static final String TAG = "IntroViewActivity";

 private ImageView logoView;
 private AnimationDrawable mAni;
 
 final Handler mHandler = new Handler();
 Runnable startAni;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  // TODO Auto-generated method stub
  super.onCreate(savedInstanceState);
  Log.i(TAG, "OnCreate() ::::::::::::::::::::");

  setContentView(R.layout.intro_view_ani);
  
  
  
  Animation animation = new AlphaAnimation(0.0f, 1.0f);
  animation.setAnimationListener(new AnimationListener() {
   
   @Override
   public void onAnimationStart(Animation animation) {
    // TODO Auto-generated method stub
    
   }
   
   @Override
   public void onAnimationRepeat(Animation animation) {
    // TODO Auto-generated method stub
    
   }
   
   @Override
   public void onAnimationEnd(Animation animation) {
    // TODO Auto-generated method stub
    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
     
     @Override
     public void run() {
      // TODO Auto-generated method stub
      Intent intent = new Intent(IntroViewAcitivity.this, TitleViewActivity.class);
      startActivity(intent);
      IntroViewAcitivity.this.finish();
      
     }
    }, 3000);
   }
  });
  animation.setDuration(2000);
  logoView = (ImageView)findViewById(R.id.logoAni);
  logoView.startAnimation(animation);
 }
  
 
 @Override
 protected void onPause() {
  // TODO Auto-generated method stub
  
  super.onPause();
 }

 @Override
 protected void onResume() {
  // TODO Auto-generated method stub
  super.onResume();
  
 }


}


안드로이드는 UI 그릴 때

layout -> Measurement -> Arrangement -> draw

순으로 그립니다.

requestLayout() 은 함수명대로 layout을 하도록 요청하는 거고.
invalidate() 는 화면이 유효하지 않게 되었으니가 다시 그리라는 것입니다.

[출처 : requestLayout() 와 invalidate()  차이점 답변 댓글]

Device 연결해서 구동시 아래 에러 발생 해결법.

[2011-04-07 14:50:14 - EcoAgentUITest1] Failed to install xxxxxxxx.apk on device 'M110axxxxx': timeout

[2011-04-07 14:50:14 - EcoAgentUITest1] Launch canceled!

Application 이 무거워 질경우 읽어 오는데 시간이 걸려서 실행 및 업데이트 시 Time out 에러가 발생한다.
기본 값이 5초여서 5초 이상 응답이 없을 경우 에러가 발생하므로 시간을 늘려 주면 된다.


Eclipse -> Preferences(환경설정) -> Android -> DDMS -> ADB connection time out (ms) : default (5000)

[출처 : http://dark2pee.tistory.com/25]
GridView 내 어댑터 선택 영역 비활성화 방법 :

<GridView
....
gridview.setselector(Resource) 이랑 android:listSelector="#00000000"
android:drawSelectorOnTop="true"
/>

EditText 커서에 대하여...

SMART_Phone/Android 2011. 6. 7. 16:08 Posted by Request


커서 보이기
소스 : setCursorVisible(true);
xml : android:cursorVisible="true"

에디트 텍스트 내용 모두 선택된 상태
소스 : setSelectAllOnFocus(true);
xml : android.selectAllOnFocus="true"

유니코드 한글 영역

SMART_Phone/Android 2011. 5. 31. 14:08 Posted by Request

유니코드 사이트(http://www.unicode.org/)

private static final char[] INITIAL_SOUND = { 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ',  
//    'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' };
 private static final char[] INITIAL_SOUND = {0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141, 0x3142, 0x3143, 0x3145, 0x3146,
  0x3147, 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e };

안드로이드 권한(Permission) 종류 -


<uses-permission android:name="android.permission.INTERNET"/> 와 같은 방식으로 사용


ACCESS_CHECKIN_PROPERTIES 체크인데이터베이스의_속성테이블로_액세스

ACCESS_COARSE_LOCATION 코스_로케이션_액세스_(Cell-ID/WiFi)

ACCESS_FINE_LOCATION 파인로케이션_액세스(GPS)

ACCESS_LOCATION_EXTRA_COMMANDS 로케이션_옵션_커맨드_액세스

ACCESS_MOCK_LOCATION 목_로케이션_프로바이더_생성_(테스트용)

ACCESS_NETWORK_STATE 네트워크_상태_접근

ACCESS_SURFACE_FLINGER 서피스_플링거_접근

ACCESS_WIFI_STATE WiFi상태_접근

ADD_SYSTEM_SERVICE 시스템서비스_추가

BATTERY_STATS 배터리_상태

BLUETOOTH 블루투스

BLUETOOTH_ADMIN 블루투스_어드민

BRICK 디바이스_실효성_지정

BROADCAST_PACKAGE_REMOVED 제거된_패키지에_대한_notification_브로드캐스트

BROADCAST_SMS SMS에_대한_브로드캐스트

BROADCAST_STICKY 인텐트_브로드캐스트

CALL_PHONE 통화

CALL_PRIVILEGED 통화(긴급전화_포함)

CAMERA 카메라

CHANGE_COMPONENT_ENABLED_STATE 컴포넌트의_실효성_변경

CHANGE_CONFIGURATION 컨피그_변경

CHANGE_NETWORK_STATE 통신상태_변경

CHANGE_WIFI_STATE WiFi상태_변경

CLEAR_APP_CACHE 어플리케이션_캐시_클리어

CLEAR_APP_USER_DATA 어플리케이션의_유저데이터_클리어

CONTROL_LOCATION_UPDATES 위치정보_갱신

DELETE_CACHE_FILES 캐시파일_제거

DELETE_PACKAGES 패키지_제거

DEVICE_POWER 전원상태에_대한_로우레벨_접근

DIAGNOSTIC 진단리소스_읽고쓰기

DISABLE_KEYGUARD 키_가드_끄기_DUMP_덤?

EXPAND_STATUS_BAR 상태표시줄_확장

FACTORY_TEST 팩토리_테스트

FLASHLIGHT 플래시라이트

FORCE_BACK 포스백

GET_ACCOUNTS 어카운트_획득

GET_PACKAGE_SIZE 패키지_획득

GET_TASKS 태스크_획득

HARDWARE_TEST 하드웨어테스트

INJECT_EVENTS 유저이벤트_키/트랙볼

INSTALL_PACKAGES 패키지_인스톨

INTERNAL_SYSTEM_WINDOW 내부_시스템윈도_활용

INTERNET 인터넷

MANAGE_APP_TOKENS 어플리케이션_토큰관리

MASTER_CLEAR 마스터_클리어

MODIFY_AUDIO_SETTINGS 오디오설정_편집

MODIFY_PHONE_STATE 전화상태_편집

MOUNT_UNMOUNT_FILESYSTEMS 파일시스템_편집

PERSISTENT_ACTIVITY 액티비티_지속

PROCESS_OUTGOING_CALLS 전화_발신처리_접근

READ_CALENDAR 캘린더_읽어오기

READ_CONTACTS 주소록_읽어오기

READ_FRAME_BUFFER 프레임버퍼_읽어오기

READ_INPUT_STATE 입력상태_읽어오기

READ_LOGS 로그_읽어오기

READ_OWNER_DATA owner_data읽어오기

READ_PHONE_STATE 통화상태_읽어오기_READ_SMS_SMS읽어오기

READ_SYNC_SETTINGS 동기설정_읽어오기

READ_SYNC_STATS 동기상태_읽어오기

REBOOT reboot

RECEIVE_BOOT_COMPLETED boot완료

RECEIVE_MMS MMS수신

RECEIVE_SMS SMS수신

RECEIVE_WAP_PUSH WAP수신

RECORD_AUDIO 오디오_수신

REORDER_TASKS 태스크_Z오더

RESTART_PACKAGES 패키지_리스타트

SEND_SMS SMS송신

SET_ACTIVITY_WATCHER 액티비티_왓쳐지정

SET_ALWAYS_FINISH 액티비티_전체_종료

SET_ANIMATION_SCALE 스케일_애니메이션_지정

SET_DEBUG_APP 디버그어플리케이션_지정

SET_ORIENTATION 스크린_로테이션지정

SET_PREFERRED_APPLICATIONS 자주_사용하는_어플리케이션_지정

SET_PROCESS_FOREGROUND 포어그라운드_처리지정

SET_PROCESS_LIMIT 제한처리_지정

SET_TIME_ZONE 타임존_지정

SET_WALLPAPER 배경화면_지정

SET_WALLPAPER_HINTS 배경화면_힌트_지정

SIGNAL_PERSISTENT_PROCESSES 지속처리_시그널_지정

STATUS_BAR 상태표시줄_지정

SUBSCRIBED_FEEDS_READ 서브스트립드_피즈_읽어오기

SUBSCRIBED_FEEDS_WRITE 서브스트립드_피즈_쓰기

SYSTEM_ALERT_WINDOW 알림_윈도우

VIBRATE 진동

WAKE_LOCK 알람

WRITE_APN_SETTINGS APN설정_쓰기

WRITE_CALENDAR 캘린더_쓰기

WRITE_CONTACTS 주소록_쓰기

WRITE_GSERVICES G서비스_쓰기

WRITE_OWNER_DATA owner_data쓰기

WRITE_SETTINGS 설정_쓰기

WRITE_SMS SMS쓰기

WRITE_SYNC_SETTINGS 동기설정_쓰기

카메라 어플 만들기

SMART_Phone/Android 2011. 5. 31. 10:45 Posted by Request

4.카메라

1.Camera

렌즈로부터 영상을 얻는 카메라는 이제 스마트폰에 빠질 수 없는 필수 기능이며 실제로도 카메라가 없는 폰을 보기 힘들다. 사진을 찍는 본질적인 용도는 물론이고 이제는 영상 통화까지도 기본 기능으로 정착되었으며 바코드 스캔이나 문자 인식 등의 최신 기술에도 카메라가 두루 사용된다. 특히 최근에 화두로 떠오르는 증강 현실은 카메라로부터 입수한 영상에 가상의 정보를 가미해 상황에 맞는 최적의 정보를 제공한다.

카메라의 물리적인 성능은 장비마다 천차 만별로 다르다. 각 제조사들은 하드웨어의 모든 기능을 아낌없이 발휘하고 타사 제품과의 차별화를 위해 고유의 기능을 확장해서 적용한다. 카메라의 성능이 곧 폰의 성능으로 인식되며 선택의 중요한 기준으로 작용한다. 그래서 카메라 앱은 하드웨어에 강하게 의존적이며 제조사가 제공한 기본 카메라가 해당 장비에서는 가장 좋은 성능을 낼 수밖에 없다.

모든 장비에 두루 사용할 수 있는 앱을 제작하려면 공통적인 기본 기능만 사용하거나 특정 기능의 존재 유무를 확인한 후 하드웨어의 능력치를 최대한 활용해야 한다. 안드로이드의 카메라 서비스는 복잡도에 비해 굉장히 깔끔하게 잘 정비되어 있으며 신뢰성도 높아서 기본 API만 잘 숙지해도 원하는 품질의 영상을 쉽게 얻을 수 있다. 카메라 기능을 사용하려면 매니페스트에 다음 선언문을 작성한다.

<uses-permission android:name="android.permission.CAMERA" />

<uses-feature android:name="android.hardware.camera" />

<uses-feature android:name="android.hardware.camera.autofocus" />

물리적인 하드웨어를 사용하는 것이므로 카메라를 사용하겠다는 퍼미션이 필요하며 사용자에게 허가를 받아야 한다. 퍼미션 외에도 uses-feature 엘리먼트로 카메라 하드웨어와 오토 포커스 기능을 사용한다는 것을 알려 해당 기능이 없는 장비에 앱이 설치되는 것을 방지할 필요가 있다. 카메라 기능은 운영체제의 서비스 형태로 제공되며 서비스와 앱 사이를 연결해 주는 것이 바로 Camera 클래스이다. 별도의 생성자는 없으므로 다음 메서드로 생성 및 파괴한다.

static Camera open ()

void release ()

정적 메서드로 카메라 객체를 생성하고 다 사용한 후 release 메서드로 해제한다. 카메라는 입수된 영상의 미리 보기를 표시하기 위해 표면 객체를 요구하므로 객체 생성 후 미리 보기 표면을 제공해야 한다. 미리보기 표면은 렌즈로부터 영상을 공급받으므로 별도의 버퍼를 가질 필요는 없으며 그래서 타입을 SURFACE_TYPE_PUSH_BUFFERS 로 설정한다. 다음 메서드로 미리 보기 표면과 방향을 지정한다.

void setPreviewDisplay (SurfaceHolder holder)

void setDisplayOrientation (int degrees)

미리보기는 기본적으로 가로 방향으로 표시되는데 대부분의 사진이 가로 방향으로 촬영되며 동영상도 세로로 된 것은 없기 때문이다. 그래서 카메라는 장비의 방향에 상관없이 항상 가로 전용으로 실행된다. 촬영시에 장비를 많이 움직이는데 이때 화면이 전환되면 오히려 불편하므로 일반적으로 카메라는 방향 전환을 하지 않는다. 하지만 카메라를 특수한 용도로 사용하는 앱에서는 세로로 촬영해야 하는 경우도 있어 2.2 버전부터는 미리보기의 방향을 변경하는 메서드가 추가되었다. 0, 90, 180, 270 식으로 미리보기의 각도를 지정한다.

표면을 지정한 후 카메라의 동작 방식이나 여러 가지 옵션을 지정하는 파라미터를 전달한다. 파라미터는 Camera의 내부 클래스인 Parameters 클래스로 표현되며 다음 메서드로 조사 및 변경한다. get 메서드로 현재 파라미터를 얻고 원하는 값을 수정한 후 set 메서드로 편집한 파라미터를 다시 전달하면 이후부터 카메라는 수정된 파라미터대로 동작한다. 실행중에라도 파라미터는 언제든지 수정할 수 있다.

Camera.Parameters getParameters ()

void setParameters (Camera.Parameters params)

파라미터로 조정할 수 있는 값은 해상도, 이미지 품질, 미리보기의 크기, 장면, 효과, 줌, 포커스, 플래시, 화이트 밸런스, 회전 모드 등등 아주 다양하다. 일반적인 디지털 카메라가 지원하는 옵션들이 대부분 지원된다. 그러나 모든 옵션이 항상 다 지원되는 것은 아니며 카메라의 물리적인 능력치를 초과할 수는 없다. 또 제조사별로 노출 시간, ISO, 손떨림 보정, GPS 좌표 기록 등의 고급 옵션들을 지원하기도 하며 얼굴 인식, 웃는 표정 인식 등 표준에 없는 커스텀 기능을 지원하는 모델도 있다.

따라서 파라미터를 조정할 때는 항상 장비의 능력치를 먼저 조사해 보고 사용할 수 있는 파라미터인지를 점검한 후 적용해야 한다. 플래시가 없는 카메라에 적목 감소 기능을 적용한다거나 300만 화소 카메라에 1000만 화소를 지정하는 것은 아무 의미가 없다. Parameters 클래스는 능력치를 조사하는 메서드와 옵션을 변경하는 메서드가 같이 제공된다. 다음은 미리 보기 영역의 크기 목록을 조사하고 설정하는 메서드이다.

List<Camera.Size> Camera.Parameters.getSupportedPreviewSizes ()

void Camera.Parameters.setPreviewSize (int width, int height)

카메라가 지원하는 미리 보기 크기의 목록을 먼저 구하고 표현하고자 하는 미리 보기와 비교하여 종횡비가 가장 근접하고 가급적이면 비슷한 크기로 미리 보기를 표시해야 한다. 다음은 사진의 해상도를 조사 및 지정하는데 방식은 동일하다.

List<Camera.Size> getSupportedPictureSizes ()

void setPictureSize (int width, int height)

해상도 목록으로 1600*1200, 2560*1920 등의 사용 가능한 크기가 조사된다. 이 목록을 사용자에게 보여주고 사용자가 선택한 해상도를 선택하면 이후 사진이 이 크기대로 촬영된다. 파라미터로 옵션을 설정했으면 다음 메서드로 미리 보기를 표시한다.

void startPreview ()

void stopPreview ()

startPreview를 호출하면 지정한 표면에 카메라 렌즈로부터 입수된 영상이 반복적으로 출력된다. 미리 보기의 프레임 비율이나 포맷도 파라미터로 지정할 수 있다. 미리 보기까지 나왔으면 언제든지 사진을 촬영할 수 있지만 좀 더 질좋은 이미지를 얻기 위해 오토 포커싱 과정을 거쳐야 한다.

void autoFocus (Camera.AutoFocusCallback cb)

void AutoFocusCallback.onAutoFocus (boolean success, Camera camera)

void cancelAutoFocus ()

autoFocus 메서드는 카메라와 영상간의 거리를 자동 판별하여 초점을 조절한다. 렌즈의 모터를 동작시켜 움직여야 하므로 다소 시간이 걸리며 그래서 이 메서드는 비동기적으로 동작한다. 오토 포커싱 콜백을 등록해 놓으면 포커싱 완료 후에 콜백이 호출되며 이때 인수로 포커싱 성공 여부가 전달된다. 대개의 경우 성공하겠지만 너무 근접한 거리에서는 실패할 수도 있다. 포커싱 중에 사용자의 다른 요청이 들어왔다면 중간에 취소할 수도 있다.

만약 장비가 오토 포커싱을 지원하지 않으면 콜백이 즉시 호출되며 이때 포커싱은 성공한 것으로 가정한다. 따라서 오토 포커싱은 기능의 제공 여부를 조사할 필요없이 무조건 호출해도 상관없다. 대상에 대해 초점을 정확하게 잡았으면 이제 사진을 찍을 차례이다. 이때는 다음 메서드를 호출한다.

void takePicture (Camera.ShutterCallback shutter, Camera.PictureCallback raw, [Camera.PictureCallback postview, ] Camera.PictureCallback jpeg)

사진 한장을 촬영하는데는 굉장히 많은 절차를 거쳐야 하는데다 대용량의 데이터를 조작해야 한다. 입수된 영상을 해상도에 맞게 축소하고 효과를 입히고 Jpeg 포맷으로 변환 및 압축까지 해야 하므로 상당한 시간이 걸린다. 그래서 takePicture 메서드는 카메라 서비스에게 사진 촬영 명령만 내린 후 즉시 리턴하되 각 단계마다 호출될 콜백 메서드를 전달해 놓는다. 필요치 않은 콜백은 null로 지정하여 생략할 수 있다.

셔터 콜백은 셔터를 닫을 때 호출되는데 보통 이 시점에서 찰칵 하는 셔터음을 낸다. 이 콜백에서 아무 것도 하지 않으면 소리가 안나야 정상이지만 무음 촬영이 법적으로 금지되어 있는 경우에는 강제로 소리가 나기도 한다. 우리 나라의 경우도 그런데 심지어 진동 모드인 상태에서도 촬영음이 들린다. PictureCallback 인터페이스에는 다음 콜백 메서드가 정의되어 있는데 각 단계의 이미지를 얻을 때마다 이 메서드가 호출된다.

void onPictureTaken (byte[] data, Camera camera)

data 인수는 이미지의 래스터 정보이다. 로(raw) 콜백으로는 압축하기 전의 원본 이미지 데이터가 전달되는데 용량이 대단히 크다. 바코드 리더기나 OCR처럼 손실없는 영상이 필요할 때 이 콜백을 처리하되 메모리가 충분하지 않을 경우는 원본 영상이 전달되지 않는다. 포스트 뷰 콜백은 촬영 후 액정으로 보여줄 이미지가 크기 조정이 완료된 상태로 전달된다.

카메라의 경우 가장 중요한 콜백은 이미지 데이터를 압축해서 전달하는 Jpeg 콜백이다. 미리 정한 포맷으로 압축된 이미지가 전달되는데 통상 JPEG 포맷이며 이 데이터를 파일로 저장하면 촬영된 사진 파일이 되는 것이다. 이미지를 저장할 때 GPS 좌표나 화면 방향 등의 상세 정보를 Exif 헤더에 저장할 수도 있다. 또 새 이미지가 추가되었으므로 미디어 DB에게 신호를 보내는 처리도 필요하다.

takePicture 메서드는 촬영전에 미리보기를 자동으로 중지하므로 stopPreview를 호출할 필요는 없다. 렌즈로부터 촬영 영상을 읽어들이는 동안에는 미리보기가 강제로 중지된다. 그러나 촬영이 끝난 후 미리보기를 자동으로 재시작하지는 않으므로 startPreview는 직접 호출해야 한다. 이 처리는 보통 Jpeg 콜백에서 파일 저장까지 완료한 후에 수행한다. 미리보기가 다시 나오면 다음 촬영을 계속할 수 있다.

2.간단한 카메라

앞 항에서 Camera 클래스를 프로그래밍하는 대략적인 절차에 대해 소개했는데 좀 길기는 하지만 지극히 상식적이고 작업 과정이 직선적이어서 이해하기는 쉽다. 카메라 열고 미리 보기 지정한 후 옵션 설정하고 찍으면 되는 것이다. 그러나 미리 보기 표면을 따로 준비해야 하는데다 오토 포커싱과 촬영 과정이 모두 비동기적으로 수행되므로 언제 어떤 메서드를 순서에 맞게 호출해야 하는지를 파악하는 것이 다소 어렵고 필요한 콜백을 선정하기도 쉽지 않다.

렌즈로부터 외부의 영상을 받아 파일 형태로 저장하기까지의 과정이 다소 복잡한 편이다. 이런 실습을 할 때는 고급 기능은 죄다 무시하고 가장 기본적인 촬영까지만 해 보는 간단한 예제가 도움이 된다. 다음 예제로 일단 카메라 프로그래밍의 기본을 익혀 보자. 짧지만 포커싱, 촬영, 저장 정도의 기능을 제공한다. 이 정도 길이의 소스로 카메라가 구현된다는 것이 놀랍다.

mm_Camera

public class mm_Camera extends Activity {

MyCameraSurface mSurface;

Button mShutter;

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.mm_camera);

mSurface = (MyCameraSurface)findViewById(R.id.preview);

// 오토 포커스 시작

findViewById(R.id.focus).setOnClickListener(new Button.OnClickListener() {

public void onClick(View v) {

mShutter.setEnabled(false);

mSurface.mCamera.autoFocus(mAutoFocus);

}

});

// 사진 촬영

mShutter = (Button)findViewById(R.id.shutter);

mShutter.setOnClickListener(new Button.OnClickListener() {

public void onClick(View v) {

mSurface.mCamera.takePicture(null, null, mPicture);

}

});

}

// 포커싱 성공하면 촬영 허가

AutoFocusCallback mAutoFocus = new AutoFocusCallback() {

public void onAutoFocus(boolean success, Camera camera) {

mShutter.setEnabled(success);

}

};

// 사진 저장.

PictureCallback mPicture = new PictureCallback() {

public void onPictureTaken(byte[] data, Camera camera) {

String path = "/sdcard/cameratest.jpg";

File file = new File(path);

try {

FileOutputStream fos = new FileOutputStream(file);

fos.write(data);

fos.flush();

fos.close();

} catch (Exception e) {

Toast.makeText(mm_Camera.this, "파일 저장 중 에러 발생 : " +

e.getMessage(), 0).show();

return;

}

Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);

Uri uri = Uri.parse("file://" + path);

intent.setData(uri);

sendBroadcast(intent);

Toast.makeText(mm_Camera.this, "사진 저장 완료 : " + path, 0).show();

mSurface.mCamera.startPreview();

}

};

}

// 미리보기 표면 클래스

class MyCameraSurface extends SurfaceView implements SurfaceHolder.Callback {

SurfaceHolder mHolder;

Camera mCamera;

public MyCameraSurface(Context context, AttributeSet attrs) {

super(context, attrs);

mHolder = getHolder();

mHolder.addCallback(this);

mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

}

// 표면 생성시 카메라 오픈하고 미리보기 설정

public void surfaceCreated(SurfaceHolder holder) {

mCamera = Camera.open();

try {

mCamera.setPreviewDisplay(mHolder);

} catch (IOException e) {

mCamera.release();

mCamera = null;

}

}

// 표면 파괴시 카메라도 파괴한다.

public void surfaceDestroyed(SurfaceHolder holder) {

if (mCamera != null) {

mCamera.stopPreview();

mCamera.release();

mCamera = null;

}

}

// 표면의 크기가 결정될 때 최적의 미리보기 크기를 구해 설정한다.

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

Camera.Parameters params = mCamera.getParameters();

List<Size> arSize = params.getSupportedPreviewSizes();

if (arSize == null) {

params.setPreviewSize(width, height);

} else {

int diff = 10000;

Size opti = null;

for (Size s : arSize) {

if (Math.abs(s.height - height) < diff) {

diff = Math.abs(s.height - height);

opti = s;

}

}

params.setPreviewSize(opti.width, opti.height);

}

mCamera.setParameters(params);

mCamera.startPreview();

}

}

레이아웃에는 포커싱과 촬영을 위한 버튼 두개와 미리 보기 표면이 배치되어 있다. 버튼 영역에 118dip 폭을 할당하고 미리 보기가 나머지 262dip 폭을 차지하는데 이는 일반적으로 많이 촬영되는 해상도의 4:3 비율을 에뮬레이터 환경에 맞춘 것이다. 와이드 해상도를 지원하는 장비는 사용자가 선택한 해상도의 종횡비와 장비의 실제 해상도에 맞게 미리 보기의 크기를 동적으로 조정해야 한다.

액티비티는 가로 전용으로 설정하여 방향 전환을 금지했다. 세로 촬영도 가능하기는 하지만 아직 2.2 이상의 장비가 일반화되지 않아 호환성 확보를 위해 방향을 고정하는 것이 좋다. 사실 카메라는 촬영중에 방향이 함부로 전환되면 오히려 불편하므로 가로든, 세로든 방향을 고정하는 것이 현실적으로 합당하다. 다음은 에뮬레이터에서 실행한 모습이다.

에뮬레이터는 렌즈가 없으므로 실제 미리 보기 영상이 나타나지 않는다. 대신 체크 무늬위를 움직이는 사각형을 반복적으로 보여줌으로써 미리보기를 흉내낸다. 에뮬레이터답게 미리보기 영상까지도 그럴듯하게 흉내를 내 주는 것이다. 이 애니메이션이 실장비에서는 렌즈로부터 입수된 영상으로 대체된다. 렌즈가 없으므로 오토 포커싱도 지원되지 않지만 항상 성공하는 것으로 가정하므로 사진 촬영은 가능하다.

에뮬레이터에서 촬영을 하면 더미 이미지 하나가 생성되는데 이 역시 가짜 이미지이다. 사진이 제대로 촬영되는지 확인해 보려면 렌즈를 가진 실장비가 필요하다. 다음은 실장비에서 이 예제를 실행한 모습이다. 미리 보기가 나타나 움직이며 Focus 버튼을 누르면 초점을 잡고 Shutter 버튼을 누르면 실제 촬영도 잘 된다. UI가 극단적으로 촌스럽기는 하지만 촬영은 훌륭하게 잘 수행된다.

소스를 분석해 보자. 미리 보기는 카메라의 필수 요소이므로 아래쪽에 미리 보기를 위한 표면인 MyCameraSurface 클래스를 정의했다. 생성자에서 콜백을 스스로 처리함을 명시했으며 버퍼 타입을 설정한다. 표면과 카메라는 생명 주기가 일치하며 표면이 먼저 준비되어야 카메라가 동작할 수 있다. 그래서 카메라의 초기화와 해제 코드는 표면의 콜백 메서드에 작성된다. 표면이 생성될 때 카메라를 생성하고 자신을 미리 보기 영역으로 제공한다.

표면이 변경될 때, 즉 표면의 크기가 처음 결정될 때 미리 보기의 크기를 결정하는데 이 코드는 다소 복잡하다. 장비가 지원하는 미리 보기 영역의 목록을 파라미터에서 구하고 그 중 표면의 실제 크기와 가장 근접한 크기를 선택한다. 만약 지원 목록을 구할 수 없으면 어쩔 수 없이 표면의 크기를 그대로 전달한다. 크기를 정한 후 startPreview를 호출하면 표면에 미리 보기가 출력된다. 표면이 파괴될 때는 미리 보기를 끝내고 카메라도 해제한다.

표면의 콜백만 제대로 처리해도 미리 보기까지는 잘 나오며 이 상태에서 바로 촬영할 수 있다. Focus 버튼을 누르면 셔터 버튼을 잠시 사용 금지시키고 autoFocus 메서드를 호출하여 초점을 잡는다. 포커스 콜백에서 포커싱이 성공하면 셔터 버튼을 허가한다. 포커싱과 촬영 두 과정을 명확하게 보이기 위해 의도적으로 버튼을 따로 두었는데 버튼 하나로 두 작업을 순차 처리할 수도 있다. 다만 중간에 버튼을 놓으면 취소하는 과정이 좀 복잡해진다.

촬영 버튼을 누르면 takePicture 메서드로 촬영을 한다. 카메라는 결국 사진을 얻는 것이 궁극의 목적이므로 다른 콜백은 무시하고 Jpeg 콜백만 처리했다. 파일에 래스터 데이터를 저장하고 새로 촬영된 이미지를 DB에 추가하기 위해 SCAN_FILE 방송을 보낸다. 좀 더 신속하게 삽입하려면 방송을 하는 대신 미디어 DB에 직접 레코드를 삽입하는 방법도 가능하다. 촬영 후 갤러리를 열어 보면 새로 찍은 사진이 보일 것이다.

이 예제는 비록 짧지만 카메라 제작의 기본 뼈대를 잘 보여준다. 디자인을 좀 더 쌈박하게 장식하고 해상도 변경이나 타이머 정도의 기능만 넣어도 실용성이 훨씬 더 개선될 것이다. mm_SHCamera 예제는 기본 기능에 약간의 추가 기능을 더해 실용적으로도 쓸만하게 만들어 본 것이되 이 정도 되면 예제로서의 가치는 떨어지므로 소스 분석은 생략하기로 한다. 원고 집필에 시달리다 보니 요즘은 다음 간식을 종종 먹어야 한다.

앞 예제와는 다소 달라졌는데 주로 오른손으로 촬영을 하므로 버튼들을 오른쪽으로 옮겼으며 몇 가지 기능을 더 넣었다. 촬영 버튼을 누르면 포커스 잡고 바로 촬영을 하는데 화면 터치 시점과 촬영 시점을 분리하여 떨림을 방지하자는 의도이다. 간단한 아이디어지만 터치폰에서는 화질 향상에 꽤 도움이 된다. 해상도 선택 기능과 접사 모드 정도를 추가했고 최후로 찍은 사진이 제대로 찍혔는지 확인하는 리뷰 기능도 제공한다.

이외에도 아주 많은 기능들을 더 넣을 수 있지만 그다지 실용성은 없어 보인다. 장면 모드니 반전, 세피아 같은 효과들은 찍은 후에 적용하는 것이 더 품질이 좋고 디지털 줌은 있으나 마나한 기능이다. 가장 빈번하게 사용하는 기능이 싱글 샷이므로 이 기능에만 집중해서 간단하게 만들어 본 것이며 개인적으로 유용하게 잘 쓰고 있다. 여러분들도 자신만의 카메라를 만들어 사용해 보기 바란다.

[출처 : http://www.winapi.co.kr/android/annex/18-4.htm ]

[android]ImageView 클래스 분석하기

SMART_Phone/Android 2011. 5. 30. 18:40 Posted by Request
ImageView 클래스는 상위 클래스 SurfaceView 클래스 상속받으며
SurfaceHolder.Callback 인터페이스 구현

 public class ImageView extends SurfacefView implements SurfaceHolder.Callback{....}

표면(surface)에 그리기 위해 Canvas클래스를 가지고 있으며, 레이어와 표면을 리사이즈하기 위한 다양한 메소드들을 제공한다.

프로그래머는 Surface 클래스에 직접 사용 할수 없고, SurfaceVIew 클래스를 이용하여 표면에 접근할수 있다.

Canvas클래스그림을 그리기 위한 도구.(표준 메소드, bitmap, line, circle, rectangles, text등)
SurfaceHolder는 일종의 중간자적인 역할을 하는 인터페이스로

SurfaceView가 Surface에 접근할때 SurfaceHolder를 경유
SurfaceHolder는 SurfaceHolder.Callback 인터페이스 포함하여 표면이 변경되는 등의 변화에 대한 이벤트받고 싶다면 SurfaceHolder.Callback로 사용하면 됨.

[발생 되는 이벤트]
surfaceCreated
:surface를 생성한 후 호출

surfaceChanged
:surface가 변경되었을 경우에 호출

surfaceDestroyed
:Surface가 제거 될 때 호출. 이 메소드에서 리소스 해제 작업을 수행


[2.SurfaceHolder.lockCanvas()와 SurfaceHolder.UnlockCanvasANdPost(Canvas c)의 이해]
lockCanvas()를 통해 얻은 Canvas를 이용하여 그림을 그리는 것은 백버퍼에서 그리는 것으로 화면에 표시 하지 않는다.
unlockCanvasAndPost()메소드가 실행되어야 비로소 작성한 이미지가 화면에 표현됨.


 SurfaceHolder 구하기

(1) ImageView 에서 다음과 같이 실행하여 SurfacefaceHolder를 구한다.
 Canvas mCanvas = null;
SurfaceHolder mHolder =getHolder();

(2)ImageView 클래스의 생성자에서 다음을 실행하여 콜백을 가능하도록 한다.
 mHolder.addCallback(this);

(3)표면이 생성될때 surfaceCreated()메소드 호출된다.
try{
 mCanvas = mHolder.lockCanvas(null);
draw(mCanvas);
}finally{
if(mCanvas != null){
 mHolder.unlockCanvasAndPost(mCanvas);
}


(4)draw()메소드에서 Canvas를 이용해서 이미지를 그린다.
 canvas.drawBitmap(mImgBack0, 0, 0, null);