posted by By훈트 2011.01.20 09:32
ImageButton을 이용하여 클릭했을 경우, 포커스 있을 경우, 눌렀을 경우 등등 이벤트를 발생하는 법을 알려드리겠습니다.

이와 같은 효과를 얻기 위해서는 SELECTOR라는 속성을 이용해야 합니다.

1. drawable 폴더에 "btn_on.png","btn_off.png","icon_on_off.xml" 을 생성합니다.

2. icon_on_off.xml 을 열어 아래 코드를 삽입합니다.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
     <!-- selected -->
     <item android:state_selected="true"
        android:drawable="@drawable/btn_on" />
     <!-- pressed -->
     <item android:state_pressed="true"
        android:drawable="@drawable/btn_on" />
    <!-- focused -->
    <item android:state_focused="true"
        android:drawable="@drawable/btn_on" />
    <!-- default -->
    <item android:drawable="@drawable/btn_off" />
</selector>

※ 여기서 주의할 점 default 이미지 값은 맨 하단에 명시해야 정상적으로 작동된다는 점 잊지 마세요!!

3. ImageButton 을 사용하는 레이아웃 XML 파일을 열어서 아래와 같이 속성을 지정합니다.

<ImageButton android:text="Button On Off" 
   android:id="@+id/menu1" 
  android:background="#000000"
  android:src="@drawable/menu_n1" 
  ... ></ImageButton>

※ 여기서 또 주의할 점 background에 #00000000을 넣어주지 않으면 안드로이드 기본 버튼 위에 이미지를 올린다는 점!

4. 끝 ^^

여러분 모두 다이나믹하고 멋진 레이아웃 구성해 보시길 바랍니다..ㅎㅎ

p.s) 다양한 상태에 따른 이미지 지정도 가능 하다는 점.. ^^
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/bt_2" />
    <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/bt_2" />
    <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/bt_2" />
    <item android:drawable="@drawable/bt_1" />
</selector>

[출처]안드로즈님의 블로그
저작자 표시
신고
posted by By훈트 2010.10.14 11:02

안드로이드 OS VMWare 설치하기

 

개발환경 : JDK 1.5, VMware 6.5.3, 안드로이드 1.6, window XP

 

http://www.android-x86.org/download 사이트에 가서 이미지를 다운받는다.


다운받은 iso 파일을 설치하기 위해 New Virtual Machine Wizard 를 실행한다.

그리고 Installer disc Image File 체크해서 파일을 넣는다나머지는 기본으로 선택하고

넘어간다.

이미지만 링크시켜서 로딩하게 되면 아래 화면과 같이 선택사항이 나오게 된다.

첫번째 Run Android-x86 without installation 선택하게 되면 설치 과정없이 바로

실행시켜 준다.

실행화면은 다음과 같다에뮬레이터 보다 훨씬 속도가 빠르기 때문에 내부 프로그램

테스트나 어플리케이션등을 다운받아 빠른 속도로 테스트 해보기 좋다.


저작자 표시

[출처] 녹두장군님의 블로그~



저작자 표시
신고
posted by By훈트 2010.09.13 09:06

안드로이드 설계/코드 노하우

안드로이드사이드 운영자
한동호

안드로이드 개발 단계

아이디어 > 기획  개발 > 테스트 > 리리즈

설계 > 코드

좋은 설계에 집중하라.

설계

좋은 설계에 집중하라.
변경이 유연한 설계를 하라.
초반부터 성능을 고려하지 말라.
필요한 기능에 집중하라.

설계시 꼭 지켜야 할 5가지
 1. UI 스레드는 UI만 처리하게 하라.
 2. 사용자를 방해하지 말라.
 3. 다양한 화면 해상도를 지원하라.

 1) wrap_content, fill_parent와 DIP를 사용하라.

  layout_width="100dip"

  = 100 pixels on a HVGA@160 density
  = 150 pixels on a WVGA@240 density

 * 사용자가 보기에는 거의 동일한 크기로 보여짐.

 2) AbsoluteLayout을 사용하지 말아라.
  1.5에서 deprecated된 클래스
  *FrameLayout과 이 레이아웃의 layout_margn 속석으로 동일한 
  화면을 구성할 수 있음.

 3) 코드에서 픽셀로 크기를 지정하지 말아라.
   안드로이드는 내부적으로 픽셀을 기본 단위로 사용

  * 필요하다면 DIP를 PIXEL로 변경해야 함.

 4) 화면 해상도에 맞게 다양한 리소스를 제공하라.

  * 다양하게 제공할수 없을 경우에는 HDPI를 제공함.
  
  텍스트를 지정할 때 사용해야 하는 단위는?

  SP : 사용자 설정에 따라 텍스트 크기 변경됨.
  (Scale-independent pixel)

  DP : 사용자 설정에 따라 텍스트 크기 변경되지 않음.
  (Density-independent pixel)

 4. 네트웍크는 항상 느리다고 가정하라.

 5. 다양한 하드웨어에 대비하라.
  처티스크린
  키보드
  센서

  좋은 코드를 작성하라.

  모바일 기기에서 어떤 코드가 좋은 코드일까?

   CPU 낮고, 메모리 낮고, 가독성 높은.
  
  코드
  
  좋은 코드를 작성하라.
  필요한 기능을 최대한 간결히 작성하라.
  클래스/메소드/변수 이름을 명확히 하라.
  코드르 어렵게/복잡하게 작성하지 말아라.

  코드 작성시 알아둬야 할 10가지
  1. 객체는 최대한 적게 생성하라.
   static 메소드를 사용해서 객체 생성을 줄일 수 있다.
   반복문에서는 객체 생성을 중여야 한다.
   객체를 재사용한다.
   
   int myNum = Integer.valueOf("123").intValue();
   
   int myNum = Integer.parseInt("123");
   
   String result = "";
   for(int i = 0; i < 10; i++){
    String temp = new String("" + i);
    result += temp;
   }
   
   StringBuffer result = new StringBuffer();
   
   for(int i = 0; i < 10; i++){
    result.append("" + i);
   }
  ================================= 
   StringBuffer total = new StringBuffer();
   total.append("PRE");
   //....
   total = new StringBuffer();
   total.append("AFTER");
   
   StringBuffer total = new StringBuffer();
   total.append("PRE");
   //....
   total.setLength(0);
   total.append("AFTER");
   
  2. 불필요한 코드는 제거하라.
   쓸데없는 캐스팅을 줄여야 한다.
   
   for(int i = 0; i < vect.size(); i++){
    if(((String) vect.get(i)).equals("1")){
     //처리
    } else if(((String) vect.get(i).equals("2")){
     //처리 
    } else if(((String) vect.get(i).equals("3")){
     //처리
    }
   } 
  ================================= 
   String val = null;
   
   for(int i = 0; i < vect.size(); i++){
    val = (String) vect.get(i);
    
    if(val.equals("1")){
     //처리
    } else if(val.equals("2")){
     //처리
    } else if(val.equals("3")){
     //처리
    }
   }   
   
   
   
  3. 구현타입을 사용하라.
   List list1 = new ArrayList();
   
   ArrayLists list1 = new ArrayList();
 
  4. 메소드는 정적 메소드로 선언하라.
   class State{
    int currentNum;
    
    public int getMaxNum(){
     return 100;
    }
   } 
  
   class State{
    int currentNum;
    
    public static int getMaxNm(){
     return 100;
    }
   } 
  
  
  5. 클래스 내에서는 Getter와 Setter 를 사용해서 변수에 접근하지 말아라.
   getAge() -> age
   
   class Person{
    int age;
    
    private int getAge(){
     return age;
    }
    
    public int getPersonAge(){
    if(getAge() <0) return 0;
    else return getAge();
    }
   }
  ================================= 
   class Person{
    int age;
    
    public int getPersonAge(){
     if(age < 0) return 0;
     else return age;
    }
   }
   
  6. 값이 고정된 변수는 상수로 선언하라.
   static in intVal = 42;
   static final int intVal = 42;
   
  7. enum은 되도록 사용하지 말아라.
   class WINTER{
    private final int num;
    
    private WINTER(int num){
     this.num = num;
    }
    
    public int getNum(){
     return num;
    }
   }
   
  8. 부동소수형은 되도로 사용하지 말아라.
   부동소수형   정수형
   float     int
   dougle    long
   
  9. 네이티브 코드는 되도록 사용하지 말아라.
   JNI - Java Native Interface
   자바에서 C/C++를 호출하기 위한 메커니즘
   
   자바보다 더 나은 속도를 위해 사용됨.
   하드웨어를 제어하는 용도로 사용됨.
   
   디바이스에 따라 호환되지 않을 수 있음.
   디버깅을 하는 것이 쉽지 않음.
   
  10. 자바의 코드 노하우가 반드시 안드로이드의 코드 노하우는 아니다.
   가독성을 위해 중첩 클래스를 사용하지 마라.
   클래스는 되도록 작게 생성하라.
 
오늘의 내용을 한 마디로 정리하자면.....
 작은 차이가 큰 차이를 만듭니다.

저작자 표시
신고
posted by By훈트 2010.08.19 21:52

화면 가로/세로 고정하기

 

AndroidManifest.xml에서

<activity android:name=".challenge"
     
android:label="@string/app_name"
     
android:screenOrientation="portrait">
     <intent-filter>
         
 <action android:name="android.intent.action.MAIN" />
          
<category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
</activity>

 

<activity>안에서 android:screenOrientration 값을 넣어주시면 됩니다.

가로 = landscape

세로 = portrait

신고
posted by By훈트 2010.08.19 09:25
안드로이드 개발 시 리소스(xml, 이미지, 위젯 등)의 고유 ID를 가져오기 위해서 R클래스와 findViewById 를 주로 사용하는데 경우에 따라서 R 클래스를 쓰지 못하는 경우 다음과 같은 방법을 사용할 수 있다.

※ R 클래스와 findViewById를 사용하는 예제

mapView = (MapView)findViewById(R.id.map);


※ R 클래스 대신 findViewWithTag 를 사용하는 예제

currentNumber = (TextView) this.findViewWithTag("txt_currentnumber");

이때의 this는 context 객체였던거 같다. 이때 findViewWithTag 메소드를 사용하기 위해서는 resource xml 에 아래와 같이 태그를 설정해줘야 한다.

<TextView
            android:id="@+id/txt_currentnumber"
            android:textSize = "10dip"
            android:text="current" android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:tag="txt_currentnumber"></TextView>


위의 예제는 위젯의 동적 ID를 가져오는 예제이고, resource xml 의 ID 값은 아래와 같이 가져올 수 있다.

※ R 클래스이용 시

int layoutResId = R.layout.page_navigation;


※ R 클래스이용하지 않고 동적 ID 값을 얻어올 경우


int layoutResId = context.getResources().getIdentifier("page_navigation", "layout", context.getPackageName());

속성값은 파일명(xml), 디렉토리명, 패키지명 이다.


참고로 SD 메모리의 경로를 동적으로 얻어오는 방법은 아래와 같다. (해보진 않았다;;)

Environment.getExternalStorageDirectory().getAbsolutePath()




신고
posted by By훈트 2010.08.17 17:04

 안드로이드는 저에게 굉장히 생소한 플랫폼입니다. 기존 어플리케이션과는 다른 라이프 사이클, 서로 다른 어플리케이션이 상대방 구성요소를 자유롭게 활용할 수 있는 점, 공통된 자원 접근 경로로서 Uri 의 적극적인 활용 등등... 여러가지 생소한 개념을 포함하고 있습니다.


 그중에서도 Intent 와 Intent Filter 그리고 Intent Routing 관련 기능은 어떻게 이런일이 가능할까? 라고 스스로에게 물어보면, 명쾌히 답할 수 없는 그런 애매한 문제였습니다. 그러다 마침, 참가중인 안드로이드 스터디 모임에서, 관련 내용을 발표하기 위해 Intent 와 Intent Filter 그리고 Intent Routing 에 대해서, 나름대로 정리할 기회가 생겼습니다. 그 내용을 공유해 봅니다.

1. Intent?
 인텐트는 '의도' 라고 번역됩니다. 또한 기능적으로 말하면 '메세지' 라고 할 수 있습니다. 예를 들어 'Intent.ACTION_VIEW' 값을 갖는 인텐트는 무언가를 '본다' 라는 의도를 내포한 '메세지' 라고 할 수 있습니다. 이 메세지는 안드로이드의 어플리케이션 컴포넌트(Activity / Service / BroadcastReceiver)들 사이의 의사 소통 수단 중 하나로 사용됩니다. 


<짜장면을 시키기 위해서는 중국집에 전화를 한 후에 주문을 전달해야 합니다.>

 그런데 여기서 중요한 것은 '메세지' 자체 만으로 어떤 일을 할 수 있는 건 아니라는 점입니다. '메세지' 가 힘을 발휘하는 건, 누군가가 그 '메세지' 를 읽고, 해당 '메세지' 의 내용을 직접 수행할 때 입니다. 짜장면을 배달 시키는 과정을 생각해보면 이해하기 쉽습니다. 예를들어, 내가 자취방에 앉아 아무리 '짜장면 배달해 주세요!' 라고 혼자 소리쳐도  결코 짜장면이 배달되어 오지는 않습니다. 중국집에 전화를 걸어, 내 주소를 말하고, 짜장면을 배달해 달라고 정확한 메세지를 전달하면, 중국집 주방장이 짜장면을 만들고, 그 후에야 짜장면이 오토바이에 실려 집까지 배달 오게 됩니다.

2. Intent Filter?
 인텐트는 어떤 의도를 나타내는 '메세지' 입니다. 메세지가 힘을 갖기 위해서는 누군가에게 전달되야 합니다. 인텐트 필터는 '필터' 라는 말이 의미하는대로, 나는 특정 '의도'를 포함하는 메세지를 받고 싶다라고 정의하는 방법이라고 할 수 있습니다. 예를 살펴보면 훨씬 쉽게 알 수 있습니다. 

<intent-filter android:label="@string/resolve_edit">
 
<action android:name="android.intent.action.VIEW" />
   
<action android:name="android.intent.action.EDIT" />
    <category android:name="android.intent.category.DEFAULT"/>              <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
<intent-filter>
   
<action android:name="android.intent.action.INSERT" />
   
<category android:name="android.intent.category.DEFAULT" />
 
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>

 첫번째 인텐트 필터는 VIEW 혹은 EDIT 라는 액션 값을 갖고, DEFAULT 카테고리라는 값을 갖으며, 구글의 Note 타입 데이터를 갖는 메세지를 수신하겠다고 선언하는 인텐트 필터입니다. 또한, 두번째 인텐트 필터는 INSERT 라는 액션 값을 갖고, DEFAULT 라는 카테고리 값을 갖으며, 구글 Note 목록 형식의 데이타 타입을 갖는 인텐트를 수신하겠다는 뜻이지요.

 인텐트는 보통 다섯 가지 구성요소 (Component/Action/Data/Category/Extra) 로 이루어 지고, 각각의 구성요소가 저마다 우선순위가 있고, 여러가지 미리 정의된 값들이 있긴 합니다만 (상세한 내용은 구글 개발자 사이트를 참고 하시면 좋습니다.), 본질적으로는 어떤 '의도' 를 내포하는 '메세지' 에 불과합니다. 또한, 인텐트 필터는 여러가지 메세지중 특정 '의도' 를 갖은 '메세지' 만을 받겠다는 선언에 불과한 것이지요.

 예를 들어, 개인의 휴대폰 번호는 나와 관계된 메세지만을 수신하는 인텐트 필터로 생각 할 수 있겠고, 도미노 피자의 주문 센터는 전국 어느 매장이든 관계 없이 도미노 피자를 주문하고자 하는 메세지를 수신하기 위해 큰 범위의 인텐트 필터를 갖고 있다고 생각할 수 있겠습니다. 

3. Intent Routing?
  인텐트는 '의도를 갖는 메세지' 인텐트 필터는 '특정 의도를 수신하겠다는 선언' 이라고 한다면, 인텐트와 인텐트 필터를 서로 연결 해주는 과정이 필요합니다. 그 것이 바로 인텐트 라우팅 과정이라고 할 수 있습니다. 

  즉, 인텐트 라우팅이란, 특정 인텐트가 주어지면, 해당 인텐트 - 의도 에 가장 잘 부합하는 인텐트 필터를 갖은 단 하나의 컴포넌트를 찾아주는 일입니다. 이때, 메세지 자체에 해당 메세지를 전달해야하는 수신처(컴포넌트 이름)가 명확하게 명시되어 있다면, 별다른 고민없이 해당 컴포넌트에 인텐트를 전달하면 됩니다. 이런 경우를 명시적 인텐트(Explicit Intent)라고 합니다. 만일 그렇지 않을 경우, 추가적으로 주어진 단서(Action/Data/Category)를 기반으로 가장 적합한 하나의 컴포넌트를 찾아내야 합니다. 이른바 암시적 인텐트(Implicit Intent) 입니다.


<기본적으로 인텐트는 반드시 하나의 컴포넌트에게만 전달되어야 한다> 

 이 과정은 몇 가지 규칙에 의해 이루어 지는데, (예를 들어, 인텐트에 액션 값이 명시되어 있다면, 인텐트 필터에 해당 액션 값이 명시되어 있어야 한다. 등등) 모든 규칙을 적용한 후에도, 해당 '의도'를 정확하게 만족시키는 컴포넌트가 여러개일 경우, 사용자에게 여러 후보 중 하나만 고르라고 위와 같은 UI 가 나타납니다. 

4. Behind the Scenes
 인텐트에 관련되어 가장 놀라운 점은, 특정 인텐트를 전달하고자 할 때, 해당 인텐트를 전달받아야 하는 대상 컴포넌트가 생명주기 상 어떤 단계에 있건 관계없이 (컴포넌트가 작동중인 상태이던 그렇지 않던...) 해당 인텐트를 수신할 수 있다는 점입니다. 

 예를 들어, 개발자가 'ACTION_VIEW' 라는 액션 값을 갖고, 'image/jpeg' 라는 타입을 갖는 인텐트를 하나 생성해서, startActivity() 함수를 통해 해당 인텐트를 전달 한다면, 이미지 뷰어 엑티비티가 화면에 나타납니다. 해당 엑티비티가 이미 실행중이던, 그렇지 않던 관계 없이 말이지요. 비유하자면, 휴대폰이 꺼져있어도 만일 누군가가 나에게 전화를 걸면 휴대폰이 자동으로 켜지는 것과 비슷한 일입니다. 

 이러한 일은 어떻게 가능할까요?

 답은 어쩌면 뻔하면서도 단순합니다. 안드로이드 어플리케이션 프레임워크단에서 세 가지 중요한 일을 수행해 주고 있기 때문입니다. 첫째로 각각의 컴포넌트들이 어떤 메세지를 수신하고자 하는지 그 내용(인텐트 필터) 를 모두 모아서 하나의 주소록처럼 관리하는 일을 수행합니다. 두 번째는, 각 컴포넌트들이 전송하는 인텐트를 우선 수신한 후, 보관된 주소록을 바탕으로 대상 컴포넌트를 찾는 역할을 수행합니다. 마지막으로, 인텐트를 수신해야할 해당 컴포넌트의 상황에 따라 적절한 일을 수행합니다. (예를 들어 작동중이 아닌 컴포넌트는 새롭게 시작시키고, 이미 백그라운드에서 작동중인 컴포넌트인 경우 포그라운드로 옮겨 주는 등...) 


<인텐트 메카니즘에서 핵심 역할을 수행하는 것은 ActivityManager 와 PackageManager 이다>


 안드로이드 플랫폼에서 첫번째 일을 수행하는 것은 PackageManagerService 이고, 두번째와 세번째 일을 수행하는 것은 ActivityManagerService 입니다. 또한 이 두개의 서비스는 시스템 서비스로, 안드로이드 플랫폼이 시작되면 항상 실행되는, 시스템 프로세스 내에서 상주하고 있습니다. 

 PackageManager 는 최초 실행 시, 안드로이드플랫상에서 어플리케이션이 설치되는 특정 경로들(system/app, data/app 등등)을 검색해, 설치된 APK 파일들을 모두 모은 후, 해당 패키지 파일에 포함되어 있는 메니페스트 파일의 내용을 파싱합니다. 특정 패키지에 들어있는 Activity / Service / BroadcastReceiver 들의 각종 권한 정보, 인텐트 필터정보 등등을 모두 수집하여 메모리 상에 관리합니다. 또한, 새롭게 패키지가 추가되거나 삭제될 때 발생하는 Broadcast Intent 를 수신하며, 관리하고 있는 주소록을 업데이트하는 일도 동시에 수행합니다.

 ActivityManger 는 한층 더 다양한 일을 수행합니다. 안드로이드 어플리케이션 컴포넌트가 인텐트를 전달하기 위해 사용하는 API 는 크게 startActivity / startService / sendBroadcast 세 가지로 나누어 집니다. 그리고, 이러한 API 가 호출될 때, 최종적으로 호출되는 API 는 ActivityManagerService 단의 API 들입니다. 세가지 API 모두 IBinder 를 통해 Remote Server 의 API 를 호출하는 샘입니다. 다시말해, 안드로이드 플랫폼 상에서 발생하는 모든 인텐트는 우선적으로 ActivityManagerService 에 전달되는 것이지요. 

 ActivityManagerService 는 인텐트를 수신 받으면, PackageManagerService 에 해당 인텐트에 가장 잘 부합하는 컴포넌트가 무엇인지 물어 봅니다. 그리고 그 결과를 바탕으로, 인텐트를 타켓 컴포넌트에게 전달해 줍니다. ActivityManagerService 는 말 그대로, 작동중인 컴포넌트들의 상태 정보 추가로, 활성화된 패키지들의 상태 정보들을 모두 관리하고 있기 때문에, 타겟 컴포넌트의 현재 상태를 확인 할 수 있습니다. 때문에, 특정 컴포넌트가 잠들어 있는 상태라면 해당 컴포넌트를 되살린 후에, 인텐트를 전달하게 됩니다. (DHL 도 울고 갈만큼 투철한 서비스 정신입니다.)


<전화한통으로 원하는 메세지를 상대방에게 확실하게 전달하는 방법>

 즉, 개념적으로 살펴 보자면, 모든 '인텐트' 는 특정 컴포넌트에게 직접 전달되는게 아니고, 택배회사와 같은 ActivityManager 와 PackageManager 를 통해, 위탁 전달된다고 생각 할 수 있습니다. 

신고
posted by By훈트 2010.08.17 17:01


<Intent 의 변신. PendingIntent 와 IntentSender>

 안드로이드 인텐트 관련해서, 두 가지 신기한 클래스가 제공됩니다. 이름만 들어서는 그 쓸모가 무엇인지 애매한, PendingIntent 와 IntentSender 가 바로 그 주인공입니다. 개발자 사이트에 서술된 내용을 살펴보아도 두 가지가 하는일이 정확히 무엇인지, 그리고 특히, 둘 사이의 차이점이 무엇인지 좀 헷갈립니다. 두 가지 클래스는 어떤 역할을 수행하고, 어떤 차이점이 있으며, 안드로이드 어플리케이션을 개발할 때, 어떻게 유용하게 사용될 수 있는지 간단하게 정리해 보았습니다. 


1.PendingIntent

 PendingIntent 은 비교적 이해하기 쉽습니다. 커뮤니케이션에는 세 가지 기본 요소가 있습니다. 메세지, 송신자, 수신자. 인텐트는 메세지 입니다. 수신자는 해당 인텐트를 수신하기 위한 인텐트 필터를 갖고 있는 컴포넌트 입니다. 송신자는 인텐트를 보내기 위한 API (startActivity/startService 등)를 호출한 컴포넌트입니다. 


<부디 나쁜데 쓰지 말고 내가 시킨것만 잘 하시오...>

 PendingIntent 는 인텐트를 전송하고자 하는 '송신자'가 인텐트를 하나 생성한 후, 별 도의 컴포넌트에게 '이 인텐트를 나중에 나 대신 보내 주렴.' 하고 전달하고자 할 때 사용되는 클래스입니다. 즉, 내가 친구에게 은행 통장에서 돈을 대신 뽑아달라고 부탁하며, 뽑을 돈의 액수를 알려주고 (인텐트), 내 카드를 빌려주는 것과 비슷한 개념이라고 생각 할 수 있습니다. 당연히, PendingIntent 를 사용할 때는 내가 맡긴 카드가 악용되지 않도록, '권한' 문제에 관해서 신경을 기울일 필요가 있으며, 이와 동시에 안드로이드 플랫폼 상에서도 PendingIntent 의 권한 문제를 제어할 수 있는 API는 물론이고, 다양한 FLAG를 제공해 주고 있습니다. 

int FLAG_CANCEL_CURRENT 이전에 생성한 PendingIntent 는 취소하고, 새롭게 하나를 만듭니다. (친구에게 예전에 빌려준 카드를 정지 시키고 새롭게 하나 신청합니다.)
int FLAG_NO_CREATE 현재 생성된 PendingIntent 를 반환 합니다. (친구 보고 내가 빌려준 카드를 당장 가져와 보라고 요청합니다. 이 후에, 해당 카드를 회수 할 수도 있습니다.)
int FLAG_ONE_SHOT 이 플래그를 이용해 생성된 PendingIntent 는 단 한번 밖에 사용될 수 없습니다. (일회용 카드)
int FLAG_UPDATE_CURRENT 만일 이미 생성된 PendingIntent 가 존재 한다면, 해당 Intent 의 내용을 변경합니다. (친구에게 전화해서, 인출할 돈의 액수를 다시 알려줍니다.)

 따라서, 저는 의미적으로 'PendingIntent' 를 '위임된 인텐트' 라고 해석할 수 있지 않을까 생각합니다.  PendingIntent 는 여러가지로 유용하게 사용될 수 있는데(여러가지 이유로 특정 패키지만 사용가능한 컴포넌트를 다른 컴포넌트와 상호 작용시킬 필요가 있는 경우 등....)그 중에서도, 안드로이드 화면 상단에 위치한 'Notification Bar' 와 상호 작용하는 어플리케이션을 작성할 때 널리 사용됩니다.

 예를 들어, 인터넷 상에서 음악을 다운로드 받는 어플리케이션이 있습니다. 음악 다운로드가 진행 중일 때, 'Notification Bar' 에 진척 상황이 나타나도록 구현하였습니다. 그리고 다운로드가 완료된 후에 사용자가 해당 알림 정보를 클릭하면, 다운로드 완료된 음악이 재생됩니다. 얼핏 생각해보면 'NotificationBar' 가 특정 아이콘이 클릭되었을 때, 어떻게 알고 음악 재생 Activity 를 띄우는지 신기하게 여겨질 수도 있습니다. 이 과정은 우리가 'Notification Manager' 에게 특정 아이콘을 등록함과 동시에 해당 아이콘이 클릭되는 순간에 전달되어야 PendingIntent 를 넘겨 주기 때문에 가능합니다. 

public void setLatestEventInfo (Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent)


 위와 같이, Notification 클래스에는 해당 Notification 이 수행할 PendingIntent 값을 설정할 수 있도록 되어 있습니다. 즉, PendingIntent 를 잘 사용하면, 다른 컴포넌트들과 상호 작용할 때, 실재로 수행할 일 자체를 PendingIntent 를 통해 추상화 할 수 있음으로(음악을 재생해라... 라는 구체적인 명령 대신, 전달받은 PendingIntent 를 수행하라 식으로...), 보다 더 확장성있는 어플리케이션 컴포넌트를 구성할 수 있습니다.

2.IntentSender
 IntentSender 는 좀 애매합니다. 무엇보다도 IntentSender 가 사용되는 꼭 맞는 샘플 코드를 찾기가 어렵더군요. IntentSender 를 생성하는 방법도 조금 색다른데, PendingIntent 를 우선 생성한 뒤, getIntentSender() API 를 호출해야합니다. 처음에는 IntentSender 는 좀 더 추상화된 PendingIntent 가 아닐까 생각했습니다. (두 클래스 간의 직접적인 상속관계가 있는 것은 아니지만...)

 하지만, 여러가지 자료를 좀 뒤져보고 (내용은 거의 없지만...) 결정적으로 안드로이드 프레임워크 소스를 살펴본 결과 허무한 결론을 내리고 말았습니다. IntentSender 는 PendingIntent 의 쌍둥이 클래스 입니다. 말하자면, PendingIntent 에 비하면 조금 못난... 쌍둥이 동생이라고 할 수 있습니다. 위임 받은 인텐트를 전달 할 수 있다는 점에서는 PendingIntent 와 정확하게 동일하지만, PendingIntent 와 비교하면 사용하기가 좀 어렵고 결정적으로 '권한' 을 관리하기 위한 API 를 제공해 주지도 않습니다. 

<멸종 위기 클래스... IntentSender. 다음 버전에는 없어지지 않을까요?>

 이 녀석 도데체 어디에 쓰이는 걸까요? 결론은 쓰이는 곳이 없다;;; 입니다. 안드로이드 풀소스를 뒤져봐도 사용되는 곳은 전무합니다. 심지어 IntentSenderTest 에서도 사용되는 것은 PendingIntent 뿐입니다. 혹시라도 저처럼 IntentSender 가 뭐에 쓰이는 물건인고... 하고 궁금해 하시는 분이 있다면, 아 그 못난 PendingIntent 쌍둥이 동생? 정도로 가볍게 넘어가셔도 좋을 듯 하네요.



신고
posted by By훈트 2010.08.17 13:42
[훈트 2010-08-17 화요일]

구글의 안드로이드OS를 탑제한 스마트폰인 안드로이드폰.
경쟁사인 애플의 아이폰과 상반되는 여러가지 특징 중 다음 특징에 대해 이야기하고자 한다.
  • 다양한 회사제품
  • 다양한 디자인 및 해상도
스터디 위주로 안드로이드 프로젝트를 하던중 치과 솔루션 업체에 취업을 하게 되었다. 
이번에 취업된 회사에서 C#기반으로 제작된 치과솔루션 프로그램을 안드로이드폰과 곧 추가될 안드로이드 OS를 탑제한 타블렛을 겨냥해 안드로이드 버젼으로 개발하는 프로젝트를 진행하게 되었다.
테스트 데모버젼을 만들기위해 그동안 스터디 위주의 학습에선 간과했던 안드로이드폰의 다양한 해상도에 고민을 하게됬다.

현재 국내에 출시되어 있는 핸드폰 기기와 출시예정인 폰중 인기 폰들만 해도 10여종에 이른다. 최고의 인기를 얻고있는 
  • 삼성 - 갤럭시A, 갤럭시S
  • 팬텍 - 시리우스, 이자르, 베가
  • HTC - 디자이어, 넥서스원
  • 모토로라 - 드로이드, 모토글램, 드로이드x

위에 폰 외에도 여러가지 출시및 출시예정된 폰들이 있는데 이들은 각각 지원하는 해상도가 다르다.
WVGA, HVGA, FWVGA 등 지원 해상도에 따라서 가로와 세로의 비율 등이 차이가 나게되어 어플 개발시 UI설계에 고민이된다.

해상도 UI에  대해서 알고 싶다면 안드로이드펍의 운영을 맡고있는 회색님의 글의 첨부 PDF파일을 참고하자.


위의 해상도관련 PDF를 보면 안드로이드의 UI 를 위해 XML코드 작성시 px 단위가 아닌 dip 라는 단위의 사용을 권장하고있다.

예를들어 로고화면을 메인에 ImageView로 넣고싶다고 가정하자. 
일단 테스트로 필자가 가지고있는 WVGA(480x800)해상도를 지원하는 팬텍의 시리우스에 테스트를 목적으로 작성한다고 하자.
가로 480px 세로 800px 이므로 로고의 크기를 가로 360px 세로 300px의 크기로 만들어 화면에 상단에 배치하고 싶다.

위에 링크의 PDF파일을 읽었다면 실제 xml 작성시 이 ImageView에 가로와 세로 속성을 dip단위로 작성해야 하는데..
문제는 여기서 생긴다, 그렇다면 이 px와 dip의 단위는 보통 1:1로 매핑되지 않기때문에 변환 작업을 거쳐야한다. 그렇기 때문에
로고 이미지를 제작할때 이미지의 px 크기에 맞춰 xml에 dip크기를 주기가 쉽지않다. 
물론 HVGA의 경우에는 dpi(dots per inch)의 크기가 기본 px과 1:1로 매핑되는 160dpi 이기 때문에 이미지 px의 크기 그대로 xml의 dip 크기로 작성이 가능하지만,
갤럭시S나 시리우스가 지원하는 WVGA의 경우는 240dpi를 지원하기 때문에 이미지의 px을 그대로 xml의 dip로 가져올수 없다.
쉽게 말해서 360x300의 px단위의 이미지를 xml에 ImageView의 크기를 width: 360dip, height: 300dip 로 가져올수 없다.
dpi를 고려하여 변환을 거쳐 width: 240dip, height: 200dip 으로 설정해야 이미지가 일그러지거나 깨지지 않고 그대로 보여줄수 있게 된다.

이런 작업을 위해서 다양한 해상도의 기기별로 변환식에 px을 대입해 해당 매치되는 dip값을 얻어야 하는데 다양한 해상도 별로 모두 하기에 상당히 번거로운 과정이다. 그래서 점심시간을이용해서 간단하게 엑셀로 안드로이드 폰 DB 라는 이름으로 작성을 해보았다.


엑셀을 처음 사용해보는거라 많이 부족하지만 필요한것 위주로 만들어보았다.
왼쪽 파란색 타이틀은 기본 폰 정보이고 
오른쪽 보라색 타이틀은 xml에서 사용하기위한 변환 수치 정보이다.

입력 dip, 입력 px에 수치를 적으면 결과px, 결과dip에 왼쪽에 기기의 해상도 정보에 맞춰 변환되어 나온다.
기기및 해상도를 추가하고싶다면 한 행의 셀을 복사해서 제일 아래 추가하고
  • 스크린타입
  • 스크린크기(px) - 가로, 세로
  • 스크린 밀도(dpi)
위의 3가지 수치 정보만 바꿔주고 
선택사항으로 기타정보인 제조사,기종,통신사,스크린타입만 바꾸어주면 
원하는 기기를 등록하여 활용할수 있다.

해당 Exel 파일은 따로 이 글에 첨부하였다.


안드로이드 화면사이즈 DB.xlsx 파일은 만약 저장이 알집형태로 저장된다면 뒤에 알집 확장자를 지우면된다.
안드로이드 화면사이즈 DB.xlsx.zip 으로 저장된다면 이름바꾸기에서 .zip을 지운후 열면된다


Ps. 엑셀파일 작성이 처음이라 작성및 데이터입력까지 1시간넘게걸렸다. 거기에 글쓰는데 점심시간 30분..
힘내라고 코맨트 달아주면 앞으로 힘내서 더 좋은정보 공유하겠습니다 ㅋ



신고
posted by By훈트 2010.08.16 20:03

Android 는 Event를 처리하는 방법이 6가지가 있다.

1. 콜백 메소드 정의

해당 클래스를 재정의해 콜백 메소드를 작성하는 것이다.
대표적인 콜백 메소드는 아래와 같다.
boolean onTouchEvent(MotionEvent event)
boolean onKeyDown(int keyCode, KeyEvent event)
...

콜백을 재정의 하면 특정 이벤트가 발생한 시점을 정확하게 가로챌 수 있을 뿐만 아니라 이벤트에 대한 상세한 정보를 얻을 수 있다.

public class HandlerEvent extends Activity {
  ...

  protected class MyViiew extends View {
    ...

   public boolean onTouchEvent(MotionEvent event) {
     super.onTouchEvent(event);
     if(event.getAction() == MotionEvent.ACTION_DOWN) {
       ...
     }
   }
  }
}

위 코드는 MyView를 터치했을때 발생하는 이벤트를 콜백함수를 통해 처리는 코드이다.

콜백 메소드를 재정의 하는 방법은 아주 간단하고 직관적이다. 특정사건에 대해 특정메소드를 호출하기로 프레임워크와 약속되어 있으므로 지정된 원형대로 메소드를 재정의 하면 된다. 
그러나 여기에는 몇가지 단점 및 한계가 있다.
(1) 메소드를 재정의 하기 위해서는 반드시 슈퍼클래스를 상속받아야 한다. 그리고 Button이나 TextView같은 위젯의 경우 이벤트를 처리하기 위해 MyButton, MyTextView 와 같은 클래스를 만들어야 하는 번거로움이 있다.
(2) 프레임워크는 자주 발생하는 일반적인 이벤트에 대해서는 콜백 메소드를 제공하지만 모든 이벤트에 대한 콜백이 정의되어 있는것은 아니다. 그러므로 콜백메소드는 일반적인 방법이 될 수 없다.


2. 리스너 인터페이스 구현

리스너(listener)는 특정 이벤트를 처리하는 interface이다. 이벤트 발생 여부를 기다리고 있는 객체라고 할 수 있다.
하지만 interface이기 때문에 우리가 직접 구현을 해주어야 한다.

대표적인 listerner는 아래와 같다.
View.OnTouchListener : boolean onTouch (View v, MotionEvent event)
View.OnKeyListener:boolean onKey (View v, int keyCode, KeyEvent event)
...

이벤트를 처리하려면 listener를 구현하는 클래스를 선언하고 그 객체를 만들어야 한다. 
그 다음 뷰에 이벤트가 발생했을때 구현된 listener가 호출되도록 연결해야 한다. set+listener이름 으로 정의되는 등록메소드를 이용하여 연결하면 된다.
void setOnTouchListener(View.OnTouchListener I)
void setOnKeyListener(View.OnKeyListener I)
...

아래는 리스너 클래스를 구현한 코드이다.

 public class CustomView extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View vw = new View(this);
        vw.setOnTouchListener(touchListener);
        setContentView(vw);
    }
    
    TouchListenerClass touchListener = new TouchListenerClass();
     
    class TouchListenerClass implements View.OnTouchListener {
        public boolean onTouch(View v, MotionEvent event) {
          // TODO Auto-generated method stub
          if(event.getAction() == MotionEvent.ACTION_DOWN) {
            return true;
          }
          return false;
        } 
    }
}


3. Activity가 listener 구현

Activity가 직접 listener interface를 구현하는 방식이다.

아래는 구현한 코드이다.

 public class CustomView extends Activity implements View.OnTouchListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View vw = new View(this);
        vw.setOnTouchListener(this);
        setContentView(vw);
    }

    public boolean onTouch(View v, MotionEvent event) {
        // TODO Auto-generated method stub
        if(event.getAction() == MotionEvent.ACTION_DOWN) {
           return true;
        }
        return false;
    }
}


이렇게 만들어진 view는 activity에 강하게 종속되게 된다. 


4. View가 listener 구현

View 스스로가 자신이 필요로 하는 listener interface를 상속받아 구현하는 방식이다.

아래는 구현된 코드이다.

 public class CustomView extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyView vw = new MyView(this);
        vw.setOnTouchListener(vw);
        setContentView(vw);
    }
    
    protected class MyView extends View implements View.OnTouchListener {
       public MyView(Context context) {
          super(context);
          // TODO Auto-generated constructor stub
       }
     
       public void onDraw(Canvas canvas) {
           Paint pnt = new Paint();
           pnt.setColor(Color.BLUE);
           canvas.drawColor(Color.WHITE);
           canvas.drawCircle(100, 100, 80, pnt);
       }

       public boolean onTouch(View v, MotionEvent event) {
           // TODO Auto-generated method stub
           return false;
       }
    }
}


자신에게 발생되는 이벤트를 자기 스스로 처리하는 방식이다. 이벤트를 처리하는 메소드를 내부에 포함한다는 면에서 구조상 깔끔하고 view를 재활용하기도 유리하다.


5. 익명 inner Class 사용

2번 listener interface를 구현하는 방식과 거의동일하나 문법적인 차이가 좀 있다.

아래는 구현 코드이다.
 public class CustomView extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View vw = new View(this);
        vw.setOnTouchListener(touchListener);
        setContentView(vw);
    }
    
    private View.OnTouchListener touchListener = new View.OnTouchListener() {
       public boolean onTouch(View v, MotionEvent event) {
           // TODO Auto-generated method stub
           return false;
       }
    }; // 생성과 정의를 동시에 할 수 있다.

}

위 방식은 구현한 listener 클래스를 재활용하기 힘들다는 점이다. 익명 inner class의 특성상 클래스의 이름이 없기 때문이다.


6. 익명 inner class의 임시 객체 사용

이름이 좀 복잡하다. 객체 생성문을 vw.setOnTouchListener 호출문안에 넣어버리는 방식이다.
역시 문법상의 차이이다.

아래는 구현 코드이다.

 public class CustomView extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View vw = new View(this);
        vw.setOnTouchListener(
            new View.OnTouchListener() {
               public boolean onTouch(View v, MotionEvent event) {
                  // TODO Auto-generated method stub
                  return false;
               }
            }
        );
        setContentView(vw);
    }
}



신고
posted by By훈트 2010.08.14 17:40
public class ScreenCapture extends Activity {
LinearLayout view;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

view = (LinearLayout) findViewById(R.id.screen);
Button myBtn = (Button) findViewById(R.id.myBtn);

myBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
View v1 = view.getRootView();
System.out.println("Root View : " + v1);
v1.setDrawingCacheEnabled(true);
Bitmap bm = v1.getDrawingCache();

System.out.println("Bitmap : " + bm);
saveScreen(bm);
}
});

}

public void saveScreen(Bitmap bm) {
try {
FileOutputStream out = new FileOutputStream(filename);
bmp.compress(Bitmap.CompressFormat.PNG, 90, out);
} catch (Exception e) {
e.printStackTrace();
}
}
}



신고