2010. 7. 24. 06:50

[번역] Back 그리고 다른 하드 키들: 세가지 이야기


Back 그리고 다른 하드 키들: 세가지 이야기

 

http://android-developers.blogspot.com/2009/12/back-and-other-hard-keys-three-stories.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+blogspot/hsDu+(Android+Developers+Blog)

 

번역: SSKK (http://codemuri.tistory.com/)

 

Back 그리고 다른 하드 키들: 세가지 이야기

 

2009 12 18일 오전 9시에 Dianne Hackborn 에 의해 포스팅 됨

 

안드로이드 2.0 Droid 와 같은 최신 장치에 나타난 가상 하드키를 지원하기 위한 몇가지 특수 기능을 포함하여 새로운 기능과 BACK, MENU 와 같은 하드키 처리 지원 등을 새로이 소개한다.

 

이 문서는 이러한 변화에 대한 세가지 이야기를 소개할 것이다: 아주 단순한 것부터 굉장히 상세한 것까지. 선호하는 한가지를 선택하라.

 

첫 번째 이야기: 개발자들을 위해 좀더 쉽게 만들기

 

만약 Android 플랫폼의 base 애플리케이션을 살펴보았다면, 아주 흔한 패턴을 알게 될 것이다. BACK 키를 가로채기 위해 약간의 마법을 추가하고 먼가 다르게 처리한다. 이렇게 하기 위한 그 마법은 아래처럼 보일 것이다.

 

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)  {
   
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
       
// do something on back.
       
return true;
   
}

   
return super.onKeyDown(keyCode, event);
}

 

액티비티 내에서 BACK 키를 가로채는 방법은 역시 개발자들이 묻는 가장 흔한 질문 중의 하나이다, 그래서 2.0 에서는 이것을 좀 더 단순하고 쉽게 만들기 위해 몇 가지 새로운 API 를 가지고 있다.

 

@Override
public void onBackPressed() {
// do something on back.
return;
}

 

만약 이것이 당신이 관심을 두는 전부라면, 2.0 이전의 플랫폼이 지원하는 지에 대해서 걱정할 필요가 없다. 그게 다라면 여기서 읽기를 멈출 수 있다. 그렇지 않다면 좀 더 읽어 보라.

 

두 번째 이야기: 긴 입력 받아들이기

 

안드로이드 플랫폼에 아주 최근에 더해진 것 중의 하나는 액션을 처리하기 위한 하드 키에 대한 긴 입력의 사용이었다. 1.0 에서는 최근 app 전환을 위한 HOME 키에 대한 긴 입력과 음성 다이얼을 위한 CALL 키의 긴 입력이었다. 1.1 에서는 음성 검색을 위한 SEARCH 키에 대한 긴 입력을 소개하였다. 그리고 1.5에서는 아직 IME 를 인지할 수 없었던 애플리케이션을 위한 백워드 호환성을 위해 소프트 키보드를 보여주기 위한 MENU 키의 긴 입력을 소개하였다.

 

(여담으로, MENU 키의 긴 입력은 백워드 호환성만을 위한 것이었고 그리하여 이 것이 사용될 때 소프트 키보드가 강력하게 화면에 머무르는 약간 놀라운 행위를 가지고 있다. – BACK 키를 누르기 전까지는 소프트 키보드가 사라지지 않는다 -  이 것은 소프트 키보드에 접근하기 위한 표준 방법이 아니고, 요즈음 작성된 모든 앱들은 보다 표준적이고 시각적인 방법으로 IME 를 가져오도록 해야 한다.)

 

불행히도 이러한 기능의 진화는 최상의 구현보다는 좀 못하게 만들었다: 모든 긴 입력 감지는 timed 메시지를 사용하여 클라이언트 측 프레임워크의 디폴트 키 처리 코드 내에 구현되었다. 이것은 많은 중복 코드를 낳았고 어떤 동작은 문제를 야기했다: 실제 이벤트 처리 코드는 긴 입력에 대한 개념이 없었고 그들에 대한 모든 타이밍은 애플리케이션의 메인 쓰레드 내에서 이루어 졌기 때문에, 애플리케이션은 긴 입력이 타임아웃 될 때 까지 충분히 업데이트하지 않아 느려질 수 있었다.

 

안드로이드 2.0에서는 긴 입력을 위한 실제 KeyEvent API 콜백 함수를 제공함으로써 모든 것이 변화하였다. 이들은 애플리케이션에 있어 긴 입력 처리를 굉장히 단순하게 해주었고 프레임워크와 올바르게 상호작용하도록 하였다. 예를 들어: 어떤 하드 키에 대한 긴 입력을 위한 고유의 액션을 제공하기 위해 프레임워크에서 제공하는 디폴트 액션 Activity.onKeyLongPressed() 를 재정의 할 수 있다.

 

개발자에게 있어 가장 중요한 것은 아마도 BACK 키 처리의 변화이다. 이전에 디폴트 키 처리는 다른 하드 키와 달리 이 키가 눌렸을 때 이 키에 대한 액션을 실행하였다. 2.0 에서는 BACK 키는 이제 키가 릴리스 되었을 때 실행된다. 그러나 현존하는 App 들을 위해, 프레임워크는 호환성의 이유로 인해 키 다운 시 액션을 계속해서 실행할 것이다. App 에서 새로운 동작을 활성화 하기 위해서는 매니페스트 내에 android:targetSdkVersion 5 이상으로 설정해야만 한다.

 

CALL 키에 대한 긴 입력과 짧은 입력에 대한 특수한 액션을 구현하는 액티비티 하위클래스의 코드 예를 소개한다.

 

@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
   
if (keyCode == KeyEvent.KEYCODE_CALL) {
       
// a long press of the call key.
       
// do our work, returning true to consume it.  by
       
// returning true, the framework knows an action has
       
// been performed on the long press, so will set the
       
// canceled flag for the following up event.
       
return true;
   
}
   
return super.onKeyLongPress(keyCode, event);
}

 

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
   
if (keyCode == KeyEvent.KEYCODE_CALL && event.isTracking()
           
&& !event.isCanceled()) {
       
// if the call key is being released, AND we are tracking
       
// it from an initial key down, AND it is not canceled,
       
// then handle it.
       
return true;
   
}
   
return super.onKeyUp(keyCode, event);
}

위의 코드는 프레임워크에 의해 일반적으로 처리되는 키에 대한 다른 행위를 구현하고 있다는 가정을 하고 있는 것을 기억하라. 다른 키에 대한 긴 입력을 처리하고 싶다면, 프레임워크가 그 키를 트랙킹 하도록 onKeyDown 을 재정의할 필요가 있다.

 

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)  {
   
if (keyCode == KeyEvent.KEYCODE_0) {
       
// this tells the framework to start tracking for
       
// a long press and eventual key up.  it will only
       
// do so if this is the first down (not a repeat).
       
event.startTracking();
       
return true;
   
}
   
return super.onKeyDown(keyCode, event);
}

 

세 번째 이야기: 가상키를 이용하여 엉망으로 만들기 (Making a mess with virtual Keys)

 

이제 이러한 모든 변화에 대한 본래의 동기에 관한 이야기를 시작한다: Droid 와 새롭게 출시되는 장치들에서는 가상 하드 키를 지원한다. 이러한 장치들은 물리적인 버튼 대신 터치에 민감한 구역 내에 하드 키를 위한 영역을 생성하여 시각적인 화면 밖에 확장된 터치 센서를 가지고 있다. 저수준 입력 시스템은 이 영역 내에서 스크린의 터치를 찾고 이들을 적절한 가상 하드 키로 변환한다.

 

비록 생성된 이벤트에 이 이벤트를 확인하기 위한 새로운 FLAG_VIRTAL_HARD_KEY 비트가 설정되어 있지만 애플리케이션에게는 이것이 기본적으로 실제 하드키 처럼 보인다. 이 플래그에 상관없이 거의 모든 경우에 애플리케이션은 이 하드 키 이벤트를 실제 하드키를 처리했던 것과 동일한 방법으로 처리할 수 있다.

 

그러나, 이 키들은 사용자 액션에 있어 약간의 문제를 야기한다. 가장 중요한 것은 사용자 인터페이스의 나머지로서 동일한 화면에 존재함으로 인해 동일한 터치로 쉽게 눌려질 수 있다는 것이다. 예를 들어, 가상 키들이 화면 하단에 위치해 있을 때 이슈가 될 수 있다: 스크롤을 위한 화면을 swipe up(화면을 손가락으로 밀어 올리는 행위)은 일반적인 제스쳐이고 이 제스처를 할 때 화면 하단의 가상 키를 우연히 건드리는 일이 매우 쉽게 발생할 수 있다.

 

2.0에서는 이것에 대한 해결책으로서 취소된키 이벤트 개념을 소개한다. 긴 입력 처리가 뒤이은 이벤트를 취소한다는 것은 앞 이야기에서 이미 소개되었다. 비슷한 방법으로, 가상 키를 누른 채 화면 위를 움직이는 것은 그 키가 릴리즈 될 때 그 가상 키가 취소 되도록 할 것이다.

 

사실상 이전의 코드는 이미 이것을 염두에 두고 있다 Up isCanceled() 를 체크함으로써 취소된 가상 키와 긴 입력은 무시될 것이다. 이 두 경우에 대한 개인적인 플래그가 역시 존재하지만 애플리케이션에서는 거의 사용되지 않고 키 이벤트가 취소된 것에 대한 좀 더 많은 원인이 있을 수 있다는 것에 대한 이해를 항상 가지고 있어야 한다.

 

현존하는 애플리케이션의 경우, 액션이 다운 되었을 때 실행하도록 하는 BACK 키 호환성이 켜져 있다면, 손가락으로 밀기(swipe)를 수행하려고 할 때 우연히 BACK 키가 눌려졌다고 감지하는 문제가 여전히 남아 있다. 비록 애플리케이션을 SDK 버전 5 이상으로 명시하여 업데이트하는 것 말고는 어떠한 해결책이 없지만, 다행히 BACK 키는 일반적으로 가상 키 영역에서 맨 끝에 위치하기 때문에 사용자는 다른 키와는 달리 BACK 키가 우연히 히트될 가능성이 낮다.

 

2.0 이후 및 2.0 이전 버전에 모두 동작하는 애플리케이션을 작성하는 것 역시 대부분의 경우에 매우 쉽다. 예를 들어, 모든 버전의 플랫폼에서 액티비티 내에서 올바르게 BACK 키를 처리하기 위한 코드는 아래와 같다.

 

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)  {
   
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR
           
&& keyCode == KeyEvent.KEYCODE_BACK
           
&& event.getRepeatCount() == 0) {
       
// Take care of calling this method on earlier versions of
       
// the platform where it doesn't exist.
        onBackPressed
();
   
}

   
return super.onKeyDown(keyCode, event);
}

@Override
public void onBackPressed() {
   
// This will be called either automatically for you on 2.0
   
// or later, or by the code above on earlier versions of the
   
// platform.
   
return;
}

 

하드 코어용: 올바르게 이벤트 처리하기

 

다룰 가치가 있는 마지막 주제는 onDispatchEvent() 또는 onPreIme() 와 같은 원시 dispatch 함수에서 올바르게 이벤트를 다루는 방법이다. onKeyDown() 과 같은 고수준 함수를 호출할 때 프레임워크가 제공하는 도움을 받을 수 없기 때문에, 이것들에 대해서는 좀 더 많은 주의가 요구된다. 아래 코드는 BACK 키가 릴리즈 될 때 올바르게 액션을 수행하기 위한 BACK 키 처리를 가로채는 방법을 보여준다.

 

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
   
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
       
if (event.getAction() == KeyEvent.ACTION_DOWN
               
&& event.getRepeatCount() == 0) {

           
// Tell the framework to start tracking this event.
            getKeyDispatcherState
().startTracking(event, this);
           
return true;

       
} else if (event.getAction() == KeyEvent.ACTION_UP) {
            getKeyDispatcherState
().handleUpEvent(event);
           
if (event.isTracking() && !event.isCanceled()) {

               
// DO BACK ACTION HERE
               
return true;

           
}
       
}
       
return super.dispatchKeyEvent(event);
   
} else {
       
return super.dispatchKeyEvent(event);
   
}
}

 

getKeyDispatchState() 호출은 윈도우의 현재 키 상태를 추적하는 데 사용되는 객체를 반환한다. View 클래스에서 일반적으로 사용가능하고 액티비티는 필요하면 그 객체를 가져 오기 위해 임의의 View를 사용할 수 있다.

 

.