posted by By훈트 2011.03.03 17:02
BitmapFactory Class에서 Stream을 읽어오는 기능을 하는데, Stream을 다 읽어 오기전에 연결을 다 끊어 NULL을 반환할 경우가 있다. 아래 처럼 하면 해결 될 듯..

HttpGet httpRequest = null;

URL url = new URL(CommunicationManager.getUrlEncode(url));
httpRequest = new HttpGet(url.toURL());
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = (HttpResponse)httpclient.execute(httpRequest);
HttpEntity entity = response.getEntity();
BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
InputStream instream = bufHttpEntity.getContent();
Bitmap bm = BitmapFactory.decodeStream(instream);

출처: http://cafe.naver.com/busanandroid.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=96
저작자 표시
신고
posted by By훈트 2011.02.18 11:22

이번에는 Bitmap 데이터 형을 byte Array로 변환하거나 혹은 그 역으로 변환하는 예제를 다루어 보도록 하겠습니다.

해결책

ByteArrayOutputStream 인스턴스를 생성한 후, Bitmap의 compress 메서드를 통해 비트맵을 압축하여 stream에 담습니다.

BitmapFactory의 decodeByteArray 메서드를 통해 byte Array를 Bitmap으로 변환합니다.

토의

안드로이드의 Bitmap은 비트맵 데이터를 stream에 넣어주는 compress 메서드를 제공하고 있습니다.

  1. public byte[] bitmapToByteArray( Bitmap $bitmap ) {  
  2.         ByteArrayOutputStream stream = new ByteArrayOutputStream() ;  
  3.         $bitmap.compress( CompressFormat.JPEG, 100, stream) ;  
  4.         byte[] byteArray = stream.toByteArray() ;  
  5.         return byteArray ;  
  6.     }  

compress 인자 값에는 압축 옵션( JPEG, PNG ) 와 품질 설정 ( 0 - 100까지의 int형 ), 그리고 압축된 바이트배열을 담을 stream을 넘겨줍니다.

byteArray는 stream의 toByteArray() 메서드를 통해 반환받을 수 있습니다.

다음은 역으로, 바이트 배열로부터 비트맵을 생성하는 코드 입니다.

  1. public Bitmap byteArrayToBitmap( byte[] $byteArray ) {  
  2.     Bitmap bitmap = BitmapFactory.decodeByteArray( $byteArray, 0, $byteArray.length ) ;  
  3.     return bitmap ;  
  4. }  

바이트 배열로부터 비트맵 생성은 BitmapFactory의 decodeByteArray의 메서드를 통해 간단히 생성할 수 있습니다. decodeByteArray의 메서드 인자값으로 바이트 배열과 offset(배열의 시작점), length(decode할 바이트 배열의 길이)를 넘겨줍니다.

결과

관련예제

안드로이드 카메라 캡쳐해서 bitmap 얻어오기



[출처] 시스님의 블로그

신고
posted by By훈트 2011.02.15 09:42

안드로이드에서 기본적으로 지원하지 않는 UI 를 만들때 CustomView 를 사용합니다.

이러한 CustomView 의 기본적인 작성방법을 알아보도록 하겠습니다.

 

CustomView 는 “android.view.View” 클래스를 상속해서 만들어 집니다.

기본적으로 onDraw() 메소드만 재정의해서 xml 에 view 태그만 추가하면 오류없이 출력되는것을 볼 수 있습니다.

 

이번 포스트에서는 간단히 클릭하면 반응하는 CustomView 를 만들어 보도록 하겠습니다.

먼저 CustomView 소스를 확인해 보도록 하겠습니다.

 

- CustomView.java

package net.cranix.android.customviewtest;

import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View;
public class CustomView extends View {
private String text = null; private int backgroundColor = Color.RED;
private String tempText;
// 속성이 없는 생성자는 소스상에서 직접 생성할때만 쓰인다. public CustomView(Context context) { super(context); Log.w(Constants.TAG,"CustomView("+context+")"); }
/* * 리소스 xml 파일에서 정의하면 이 생성자가 사용된다. * * 대부분 this 를 이용해 3번째 생성자로 넘기고 모든 처리를 3번째 생성자에서 한다. */ public CustomView(Context context,AttributeSet attrs) { this(context,attrs,0); Log.w(Constants.TAG,"CustomView("+context+","+attrs+")"); }

/* * xml 에서 넘어온 속성을 멤버변수로 셋팅하는 역할을 한다. */ public CustomView(Context context,AttributeSet attrs,int defStyle) { super(context,attrs,defStyle); this.text = attrs.getAttributeValue(null,"text"); Log.w(Constants.TAG,"CustomView("+context+","+attrs+","+defStyle+"),text:"+text); }

/* * xml 로 부터 모든 뷰를 inflate 를 끝내고 실행된다. * * 대부분 이 함수에서는 각종 변수 초기화가 이루어 진다. * * super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다. */ @Override protected void onFinishInflate() { setClickable(true); Log.w(Constants.TAG,"onFinishInflate()"); }
/* * 넘어오는 파라메터는 부모뷰로부터 결정된 치수제한을 의미한다. * 또한 파라메터에는 bit 연산자를 사용해서 모드와 크기를 같이 담고있다. * 모드는 MeasureSpec.getMode(spec) 형태로 얻어오며 다음과 같은 3종류가 있다. * MeasureSpec.AT_MOST : wrap_content (뷰 내부의 크기에 따라 크기가 달라짐) * MeasureSpec.EXACTLY : fill_parent, match_parent (외부에서 이미 크기가 지정되었음) * MeasureSpec.UNSPECIFIED : MODE 가 셋팅되지 않은 크기가 넘어올때 (대부분 이 경우는 없다) * * fill_parent, match_parent 를 사용하면 윗단에서 이미 크기가 계산되어 EXACTLY 로 넘어온다. * 이러한 크기는 MeasureSpec.getSize(spec) 으로 얻어낼 수 있다. * * 이 메소드에서는 setMeasuredDimension(measuredWidth,measuredHeight) 를 호출해 주어야 하는데 * super.onMeasure() 에서는 기본으로 이를 기본으로 계산하는 함수를 포함하고 있다. * * 만약 xml 에서 크기를 wrap_content 로 설정했다면 이 함수에서 크기를 계산해서 셋팅해 줘야한다. * 그렇지 않으면 무조껀 fill_parent 로 나오게 된다. */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// height 진짜 크기 구하기 int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = 0; switch(heightMode) { case MeasureSpec.UNSPECIFIED://mode 가 셋팅되지 않은 크기가 넘어올때 heightSize = heightMeasureSpec; break; case MeasureSpec.AT_MOST://wrap_content (뷰 내부의 크기에 따라 크기가 달라짐) heightSize = 20; break; case MeasureSpec.EXACTLY://fill_parent, match_parent (외부에서 이미 크기가 지정되었음) heightSize = MeasureSpec.getSize(heightMeasureSpec); break; }
// width 진짜 크기 구하기 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = 0; switch(widthMode) { case MeasureSpec.UNSPECIFIED://mode 가 셋팅되지 않은 크기가 넘어올때 widthSize = widthMeasureSpec; break; case MeasureSpec.AT_MOST://wrap_content (뷰 내부의 크기에 따라 크기가 달라짐) widthSize = 100; break; case MeasureSpec.EXACTLY://fill_parent, match_parent (외부에서 이미 크기가 지정되었음) widthSize = MeasureSpec.getSize(widthMeasureSpec); break; }
Log.w(Constants.TAG,"onMeasure("+widthMeasureSpec+","+heightMeasureSpec+")"); setMeasuredDimension(widthSize, heightSize); }
/* * onMeasure() 메소드에서 결정된 width 와 height 을 가지고 어플리케이션 전체 화면에서 현재 뷰가 * 그려지는 bound 를 돌려준다. * 이 메소드에서는 일반적으로 이 뷰에 딸린 children 들을 위치시키고 크기를 조정하는 작업을 한다. * 유의할점은 넘어오는 파라메터가 어플리케이션 전체를 기준으로 위치를 돌려준다. * * super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다. */ @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { Log.w(Constants.TAG,"onLayout("+changed+","+left+","+top+","+right+","+bottom+")"); }
/* * 이 뷰의 크기가 변경되었을때 호출된다. * * super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다. */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { Log.w(Constants.TAG,"onSizeChanged("+w+","+h+","+oldw+","+oldh+")"); }
/* * 실제로 화면에 그리는 영역으로 View 를 상속하고 이 메소드만 구현해도 제대로 보여지게 된다. * * 그릴 위치는 0,0 으로 시작해서 getMeasuredWidth(), getMeasuredHeight() 까지 그리면 된다. * * super 메소드에서는 아무것도 하지않기때문에 쓰지 않는다. */ @Override protected void onDraw(Canvas canvas) { final Paint p = new Paint(); p.setColor(backgroundColor); canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(), p); if (text != null) { p.setColor(Color.BLACK); canvas.drawText(text, 10, 15, p); // 왼쪽 아래를 0,0 으로 보고있음 } Log.w(Constants.TAG,"onDraw("+canvas+")"); }
/* * 현재 view 가 focus 상태일때 key 를 누르면 이 메소드가 호출됨. * 즉 이 메소드를 사용하려면 setFocusable(true) 여야함. *
     * 그리고 super 메소드에서는 기본적인 키 작업(예를들면 BACK 키 누르면 종료)을 처리하기 때문에 
     * 일반적으로 return 시에 호출하는게 좋다.
     * 만약 기본적인 작업을 하지않게 하려면 super 함수를 호출하지 않아도 된다.
     * 
     * 다른 event 메소드들도 유사하게 동작한다.
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.w(Constants.TAG,"onKeyDown("+keyCode+","+event+")");
        return super.onKeyDown(keyCode, event); 
    }
/* * 이 view 에 touch 가 일어날때 실행됨. * * 기본적으로 touch up 이벤트가 일어날때만 잡아내며 * setClickable(true) 로 셋팅하면 up,move,down 모두 잡아냄 */ @Override public boolean onTouchEvent(MotionEvent event) { Log.w(Constants.TAG,"onTouchEvent("+event+")"); switch(event.getAction()) { case MotionEvent.ACTION_UP: backgroundColor = Color.RED; text = tempText; break; case MotionEvent.ACTION_DOWN: backgroundColor = Color.YELLOW; tempText = text; text = "Clicked!"; break; case MotionEvent.ACTION_MOVE: backgroundColor = Color.BLUE; text = "Moved!"; break; } invalidate(); return super.onTouchEvent(event); }
public String getText() { return text; }
public void setText(String text) { this.text = text; } }

- 크기 계산하기

여기서 중요한 메소드는 onMeasure() 메소드 입니다.

이 메소드는 뷰의 전체 크기를 정하는 메소드 인데 안드로이드의 크기 정하는 방법에 따라 구현법이 달라져야 합니다.

 

안드로이드 레이아웃 xml 파일에서 크기를 지정하는 방법은 4가지가 있습니다.

   - fill_parent (상위 View 의 크기에 따름)

   - match_parent (상위 View 의 크기에 따름)

   - fixed (100px 와 같이 픽셀로 박아놨을때)

   - wrap_content (현재 뷰의 내용에 따름)

 

이렇게 4가지 방법의 특성에 따라서 넘어오는 크기의 종류는 3가지로 구분됩니다.

   - MeasureSpec.EXACTLY : fill_parent, match_parent, fixed 와 같이 상위에서 이미 결정되어버린 크기가 넘어올때 선택됩니다.

   - MeasureSpec.AT_MOST : wrap_content 를 선택했을때 선택됩니다.

   - MeasureSpec.UNSPECIFIED : xml 에 의하지 않고 소스상에서 직접 넣었을 때 나옵니다.

 

여기서 EXACTLY 과 UNSPECIFIED 는 외부에서 크기가 구해져서 내려오는 것이기 때문에 따로 계산할 것이 없으나 AT_MOST 는 내부적으로 크기계산을 해 주어야 합니다.

위의 소스에서는 간단하게 100,20 으로 박아놨지만 실제로 CustomView 를 구현하게 된다면 뷰의 특성에 따라 구현이 달라져야 할 것입니다.

 

 

- xml 에서 파라메터 받아내기

안드로이드 리소스 xml 에서 파라메터를 받아내려면 위 소스의 3번째 생성자에 있는것 처럼 아래와 같은 구문을 써야 합니다.

this.text = attrs.getAttributeValue(null,"text");

 

 

- xml 파일 구성하기

이렇게 만든 CustomView 를 xml 파일에서 사용하려면 아래와같은 xml 구성이 필요합니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<view class="net.cranix.android.customviewtest.CustomView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
text="test"
/>
</LinearLayout>

 

- 실행해 보기

이렇게 구성된 뷰를 Activity 에 넣고 실행해 보면 아래와 같은 화면이 나옵니다.

마우스를 클릭,이동 할때마다 색깔이 변경되는것을 볼 수 있습니다.

image

image

image


[출처] 닉스로그님의 블로그


저작자 표시
신고
posted by By훈트 2011.02.08 16:28
1. Collection
  • Map은 key와 value를 가진 집합이며, 중복을 허용하지 않는다.
  • 즉, 한개의 key에 한개의 value가 매칭된다.
  • java.util 패키지에 여러 집합들을 사용하기 위한 여러 interface와 class 들이 정의되어 있다.
2. HashMap
  • HashMap은 Map interface를 implements 한 클래스로서 중복을 허용하지 않는다.
  • Map의 특징인 key와 value의 쌍으로 이루어지며, key 또는 value 값으로써 null을 허용한다.
  • 아래의 예는 HashMap을 사용한 간단한 예제이다.

    import java.util.*;

     

    public class HashMapTest
    {
        public static void main(String argv[])
        {
            HashMap hm = new HashMap();
            System.out.println(hm.put("aaa", "111"));
            System.out.println(hm.put("bbb", "222"));
            System.out.println(hm.put("aaa", "444"));
            System.out.println(hm.put("ccc", "333"));    
            System.out.println(hm.put("ccc", null));       
            
            System.out.println("HashMap size : " + hm.size());
            
            Set set = hm.keySet();
            Object []hmKeys = set.toArray();
            for(int i = 0; i < hmKeys.length; i++)
            {
                String key = (String)hmKeys[i];   
                System.out.print(key);
                System.out.print(" - ");
                System.out.println((String)hm.get(key));
            }
        }
    }


    /**
    실행:java HashMapTest
    결과:
    null
    null
    111
    null
    333
    HashMap size : 3
    ccc - null
    bbb - 222
    aaa - 444
    */

3. TreeMap
  • TreeMap역시 중복을 허용하지 않으며, key와 value의 쌍으로 이루어져 있다.
  • HashMap과 다른 점은 SortedMap을 implements 하였으므로, key 값들에 대한 정렬이 이루어진다는 점이다.
  • 아래의 예는 TreeMap을 사용하여 각 요소가 몇몇 이나 나왔는지 알아보는 간단한 예제이다.

    import java.util.*;

    public class Freq

    {
        private static final Integer ONE = new Integer(1);

        public static void main(String args[])

        {
            Map m = new TreeMap();

            // Initialize frequency table from command line
            for (int i=0; i < args.length; i++)

            {
                Integer freq = (Integer) m.get(args[i]);
                m.put(args[i], (freq==null ? ONE :
                                new Integer(freq.intValue() + 1)));
            }

            System.out.println(m.size()+" distinct words detected:");
            System.out.println(m);
        }
    }


    /**
    실행:java Freq if it is to be it is up to me to delegate
    결과:
    8 distinct words detected:
    {be=1, delegate=1, if=1, is=2, it=2, me=1, to=3, up=1}
    */

4. Hashtable
  • Hashtable Map interface를 implements 한 클래스로서 중복을 허용하지 않는다.
  • Map의 특징인 key와 value의 쌍으로 이루어지며, key 또는 value 값으로써 null을 허용하지 않는다.(HashMap과의 차이점)
  • 아래의 예는 HashTable을 사용한 간단한 예제이다.

    import java.util.*;

    public class HashtableTest

    {

        public static void main(String argv[])
        {
            Hashtable ht = new Hashtable();
            System.out.println(ht.put("aaa", "111"));
            System.out.println(ht.put("bbb", "222"));
            System.out.println(ht.put("aaa", "444"));
            System.out.println(ht.put("ccc", "333"));    
            
            System.out.println("Hashtable size : " + ht.size());
            
            System.out.println("aaa value : " + (String)ht.get("aaa");
            
        }
    }


    /**
    실행:java HashMapTest
    결과:
    null
    null
    111
    null
    Hashtable size : 3
    aaa value : 444
    */

저작자 표시
신고
posted by By훈트 2011.01.28 12:18

01./** Get Bitmap's Width **/
02.public static int getBitmapOfWidth( String fileName ){
03.try {
04.BitmapFactory.Options options = new BitmapFactory.Options();
05.options.inJustDecodeBounds = true;
06.BitmapFactory.decodeFile(fileName, options);
07.return options.outWidth;
08.catch(Exception e) {
09.return 0;
10.}
11.}
12. 
13./** Get Bitmap's height **/
14.public static int getBitmapOfHeight( String fileName ){
15. 
16.try {
17.BitmapFactory.Options options = new BitmapFactory.Options();
18.options.inJustDecodeBounds = true;
19.BitmapFactory.decodeFile(fileName, options);
20. 
21.return options.outHeight;
22.catch(Exception e) {
23.return 0;
24.}
25.}

[출처] 안드로이드펍 SSamDDak님의 답변
저작자 표시
신고
posted by By훈트 2011.01.27 18:18

출처 : http://penta82.egloos.com/4099869

 

JCF(Java Collection Framework)에서 많이 사용되었던 Hashtable에 대해서 살펴보자.

Hashtable은 JDK 1.2이전부터 존재해 왔던 클래스이다. 많은 사람들이 의문인 것은 왜 HashTable이 아니냐는 것인데 여기서는 그냥 넘어가자. 클래스명이야 소스 개발자의 몫이니까.

Hashtable의 중요 메소드를 살펴보면,

  • void clear()
    • 모든 키와 값을 제거한다.
  • boolean contains(Object value), containsValue(Object value)
    • 주어진 객체에 대응하는 키 값이 있는지 알려준다.
  • boolean containsKey(Object key)
    • 주어진 키 값이 있는지 알려준다.
  • Enumeration keys()
    • 해시 테이블의 키 값들을 돌려준다.
  • Enumeration elements()
    • 해시 테이블에 저장된 객체들을 돌려준다.
  • Object get(Object key)
    • 주어진 키에 대응하는 값을 돌려준다.
  • Object put(Object key, Object value)
    • 키와 대응하는 값을 저장한다.
  • Object remove(Object key)
    • 키의 대응 관계(Mapping)을 제거한다.
  • int size()
    • 해시 테이블의 대응 관계(Mapping)의 개수를 돌려준다.

여기서 'Enumeration이 무엇인가?' 하는 궁금증이 있을 것이다. Enumeration은 단순히 객체들의 목록을 가지고 있는 구조체라고 생각하면 된다. Enumeration클래스의 메소드를 보면

  • boolean hasMoreElements()
    • 더 이상의 요소가 있는지 알려준다.
  • nextElements()
    • 다음 요소를 돌려준다. 만약 없다면, NoSuchElementException 예외를 발생한다.

따라서 위의 두 메소드를 사용하면 for문, while문을 이용하지 않고도 key값과 element값에 접근할 수 있다.

이제 관련 소스를 살펴보자.

public class HashtableExam2 {
    
    public static void main(String[] args) {
        Hashtable htable = new Hashtable(10);
        
        // put(Object key, Object value)메소드
        htable.put("java", "프로그래밍 언어");
        htable.put("bible", "성서, 성경, 성전");
        htable.put("star", "별, 항성");
        htable.put("moon", "달");
        
        // contains(Object value);
        if(htable.contains("프로그래밍 언어")) {
            System.out.println("value : \"프로그래밍 언어\" 존재");
        }else{
            System.out.println("value : \"프로그래밍 언어\" 존재하지 않음");
        }    
        
        if(htable.contains("음악")) {
            System.out.println("value : \"음악\" 존재");
        }else{
            System.out.println("value : \"음악\" 존재하지 않음");
        }
        
        // containsValue(Object value)
        if(htable.containsValue("프로그래밍 언어")) {
            System.out.println("value : \"프로그래밍 언어\" 존재");
        }else{
            System.out.println("value : \"프로그래밍 언어\" 존재하지 않음");
        }
        
        if(htable.containsValue("음악")) {
            System.out.println("value : \"음악\" 존재");
        }else{
            System.out.println("value : \"음악\" 존재하지 않음");
        }
        
        // containsKey(Object key)
        if(htable.containsKey("java")) {
            System.out.println("key : \"java\" 존재");
        }else{
            System.out.println("key : \"java\" 존재하지 않음");
        }
        
        if(htable.containsValue("ruby")) {
            System.out.println("key : \"ruby\" 존재");
        }else{
            System.out.println("key : \"ruby\" 존재하지 않음");
        }
        
        // get(Object key)
        String value = (String)htable.get("java");
        System.out.println("java(key) : " + value + "(value)");
        
        // remove(Object key)
        htable.remove("star");
        
        // Enumeration 사용
        Enumeration e = htable.keys();
        String key, val;
        while(e.hasMoreElements()) {
            key = (String)e.nextElement();
            val = (String)htable.get(key);
            System.out.println("[" + key + "] " + val);
        }
        
        // clear()
        htable.clear();
        
        // size()
        System.out.println("htable의 size : " + htable.size());
    }

}
저작자 표시
신고
posted by By훈트 2011.01.27 10:01

1. 자바 디자인 패턴 1 – Iterator

 

2. 자바 디자인 패턴 2 – Adapter

 

3. 자바 디자인 패턴 3 - Factory Method

 

4. 자바 디자인 패턴 4 - Template Method

 

5. 자바 디자인 패턴 5 – Singleton

 

6. 자바 디자인 패턴 6 – Strategy

 

7. 자바 디자인 패턴 7 – Composite

 

8. 자바 디자인 패턴 8 – Decorator

 

9. 자바 디자인 패턴 9 - Chain of Responsibility

 

10. 자바 디자인 패턴 10 – Facade


11. 자바 디자인 패턴 11 – Observer

 

12. 자바 디자인 패턴 12 – Prototype

 

13. 자바 디자인 패턴 13 – Flyweight

 

14. 자바 디자인 패턴 14 – Builder

 

15. 자바 디자인 패턴 15 – Mediator

 

16. 자바 디자인 패턴 16 – Visitor


출처 : http://iilii.egloos.com
저작자 표시
신고
posted by By훈트 2011.01.26 12:54
  Low density
ldpi (120)
Medium density
mdpi (160)
High density
hdpi (240)
Extra high density
xhdpi (320)
Small QVGA (240x320)      
Normal
WQVGA400 (240x400)
WQVGA432 (240x432)
 HVGA (320x480)
WVGA800 (480x800)
WVGA854 (480x854)
 
Large  
WVGA800 (480x800)
WVGA854 (480x854)
   
Extra Large        


저작자 표시
신고
posted by By훈트 2011.01.24 20:35


내 클래스 내 마음대로!

 

 

[Intro]

 

어플리케이션, 특히 UI 프로그래밍을 하다 보면 메세지 처리 라는 녀석을 피할 수 없습니다.

예를 들어, 로딩 중에 '로딩 중' 이라고 알려 주는 다이얼로그를 표시하고 싶다고 할 때,

단순히 스레드로만 처리를 하면 서로 엉켜서 다이얼로그가 제대로 표시 되지 않을 때가 많지요.

그래서 아래와 같이 살짝 딜레이를 주게 되면 엉키는 것을 조금이나마 완화 시킬 수 있습니다.

Handler handler = new Handler();
handler.postDelayed(rShowDialog, 1000);

Handler는 이 뿐 아니라 UI Thread 실행 기능도 가지고 있죠.

스레드 여러개 사용해 보신 분들은 금방 아실겁니다.

 

위에서 Handler에 대해 잠깐 언급 하긴 했지만,

제가 여기서 다루고자 하는 것은 Handler가 뭐하는 것인지, 어떻게 사용하는 것인지가 아닙니다.

일단 Handler를 사용해 보신 분들을 대상으로 설명을 해 나갈 것이니,

그 점 양해 부탁 드립니다.

 

 

[흘러가는 메세지는 잡을 수 없다!] 

 

일단 Handler는 내부적으로 MessageQueue를 가지고 있습니다.

그래서 Handler로 무조건 적으로 메세지를 보내게 되면,

 

뭐... 쌓이겠죠.

 

하지만 이것은 어디까지나 물리적인 측면에서 볼 때 쌓이는 것입니다.

논리적인 측면에서 볼 때는 충분히 메세지를 놓칠 수 있습니다.

 

예를 들어 봅시다.

onDown 이벤트가 발생 한 뒤에 onSingleTapUp 이벤트가 발생 하는 상황을 살펴 보겠습니다.

보통 onDown - onSingleTapUp 이벤트가 발생 했다는 것은 한번 터치 했을 경우입니다.

그렇다는 것은 터치가 되는 대상이 존재 한다는 말이겠죠.

그럼, 이제 여기서 좀 더 세부적으로 살펴 보겠습니다.

 

터치가 되는 대상이 어떤 하얀색 사각형 '들' 이라고 했을때,

onDown 이벤트 시에는 어떤 사각형이 선택이 되었는지 검색을 하며,

onSingleTapUp 이벤트 시에는 선택된 사각형을 빨간색으로 만드는 일을 할 것입니다.

연결 동작으로 보면 터치 했을 때 선택된 사각형이 빨간색으로 변하게 되겠죠?

 

그런데 만약에 onDown 이벤트 시에 어떤 사각형이 선택이 되었는지 미처 찾기도 전에

onSingleTapUp 이벤트가 발생한다면 어떻게 될까요?

네... 물론 아무것도 변하지 않겠죠.

그렇게 되면 결과적으로 onSingleTapUp 이벤트는 아무것도 안하고 그냥 흘러가는 메세지가 됩니다.

 

물리적으로는 onDown 이벤트와 onSingleTapUp 이벤트 둘 다 발생 했지만,

논리적으로 봤을땐... 하나는 실패한 이벤트입니다.

마치 하드웨어에서 인터럽트가 많아지면 나중에 들어온 인터럽트는 무시당하는 경우와 비슷하죠.

 

그렇다면 위와 같은 경우에는 흘러가는 메세지를 그냥 보내야만 할까요?

잡아둘 방법은 없을까요?

 

 

[메세지를 흘러가지 못하도록 막자!]

 

이제부터 본격적으로 제가 하고싶은 이야기를 할 것입니다.

그림이 없어서 복잡할지도 모르겠지만, 최대한 잘 설명해 보겠습니다.

위에서 언급했던 onDown - onSingleTapUp 이벤트를 다시 한번 봅시다.

 

선택된 사각형을 찾았을 때 onRectangleFounded 메소드가 호출 되며,

onSingleTapUp 이벤트 안에서 선택된 사각형의 색을 바꾼다고 합시다.

 

그렇다면, 동작이 원하는데로 이루어지기 위해서는 아래와 같은 순서로 진행 되어야 합니다.

onDown - onRectangleFounded - onSingleTapUp - (선택된 사각형의 색이 변함)

하지만 만약 사각형을 찾는 시간이 onSingleTapUp 이벤트 보다 느리다면,

onDown - onSingleTapUp (색을 바꿀 사각형이 없음) - onRectangleFounded - (변화 없음)

 

onSingleTapUp 보다는

onRectangleFounded의 이벤트를 먼저 처리 해야 한다는 것을 다시 한번더 정리해 봤습니다.

항상 첫 번째의 가장 좋은 경우가 일어나면 괜찮은데,

두 번째의 경우가 일어나지 말라는 법은 없습니다. 보장 할 수 없죠.

그래서 한번 생각해 봤습니다. Lock을 걸면 되지 않을까...?

 

Lock을 걸어본다면 흐름은 아마 아래와 같을 것입니다.

onDown with Lock - onSingleTapUp (Lock에 의해 메세지가 실행 대기 상태가 됨) 
- onRectangleFounded with Unlock (대기 하고 있던 onSingleTapUp 메세지가 실행 됨)

onSingleTapUp 메세지는 Lock에 의해 Queue에 저장이 되고,

Unlock시에 Queue에 쌓인 메세지들을 실행 하면 되겠거니 하고 생각해봤습니다.

 

 

[Extends Handler]

 

우선 Handler를 extends 하기 위해서는 반드시 아래의 메소드를 구현 해야 합니다.

public void handleMessage(Message msg)
Subclasses must implement this to receive messages.

하지만 Handler에게 메세지를 보낸다고 해서

모두 handleMessage() 메소드에 들어가는것은 아닙니다.

 

 

다음으로, Handler에게 메세지를 보내는 방법은 여러가지 방법이 있습니다.

 

sendEmptyMessage Family of Handler class

public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)

sendMessage Family of Handler class

public final boolean sendMessage(Message msg)
public final boolean sendMessageAtFrontOfQueue(Message msg)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)

sendEmptyMessage는 내용 없는 메세지만, sendMessage는 실제 Message를 보냅니다.

어떤 메세지인가? 혹은 메세지에 데이터가 필요한가?에 따라서

유용하게 사용 할 수 있을 것입니다.

 

Message.sendToTarget()

메세지를 보내는 또 한가지 방법은

Message 객체의 sendToTarget() 메소드를 호출 하는 방법입니다.

Message 객체 안에 설정된 Target Handler에게 Message 객체 자신이 직접 보내지는 것이죠.

 

(Handler를 많이 써오시던 분들은 post Family를 많이 사용하셨을 겁니다.

post Family는 실행 가능한 Runnable을 직접 Message Queue에 넣는일을 하기 때문에

일단 여기서는 제외 시켰습니다.)

 

 

sendMessage를 하던 sendToTarget으로 자신을 직접 보내던,

중요한것은 Message 객체를 생성하는 일입니다.

Constructor를 사용하여 직접 Message 객체를 생성해도 되지만,

Message.obtain() 메소드나 Handler.obtainMessage() 메소드의 사용을 권장하고 있습니다.

 

Constructor로 만드는것과 obtain 메소드를 사용하는 것의 큰 차이는

obtain 메소드를 사용하면 재활용 될 객체를 pool에서 뽑아온다는 것이

큰 차이점이라고 볼 수 있습니다. 새로 만드는 것 보단 낫겠죠?

 

Message.obtain()과 Handler.obtainMessage() 메소드의 차이점은,

Handler.obtainMessage()의 경우 자동으로 Target Handler가

호출 하는 Handler로 정해진다는 것이 차이점입니다.

즉, Message.obtain() 메소드를 사용하면 Target Handler를 지정해 줘야 한다는 말과도 같겠죠.

 

Handler.sendMessage() 메소드와 Message.sendToTarget() 메소드,

Handler.obtainMassage() 메소드와 Message.obtain() 메소드,

이 중에서 마음에 드는 것으로 사용 하시면 되겠습니다.

 

 

[Implements LockableMessageHandler]

 

이제 실제로 구현에 대해서 살펴 보겠습니다.

앞서 이야기 했던 extends 방법을 조합하면 대충 머릿속에 그려지지 않나요?

 

더 앞서 이야기 했듯이,

제가 하고 싶은 것은 Lock일 때 메세지를 쌓고, Unlock일 때 쌓인 메세지를 실행 하는 것입니다.

그럼 Lock 상태와 Unlock 상태가 필요 하고, 메세지를 쌓아 둘 Queue도 필요 합니다.

 

이 점을 유념해서 한번 만들어 보겠습니다.

맨 처음으로 Handler를 상속받은 클래스를 만들고, handleMessage() 메소드를 Override 해줍시다.

public final class LockableMessageHandler extends Handler {
    @Override
    public void handleMessage(Message msg) { ... }
}

이제 Lock과 Unlock에 대한 메세지를 만들겁니다.

@Override
public void handleMessage(Message msg) {
    if(msg.what == 1) {
        isLocked true;
    }
    else if(msg.what == 2) { 
        isLocked false;
        
        // RUN!!!
        while(mRunnableQueue.size() > 0) {
            Runnable runnable = mRunnableQueue.poll();
            this.post(runnable);
        }
    }
    ...

Message.what은 사용자 정의 메세지 코드이며, integer 값입니다.

그래서 지금은 편의상 1과 2로 나눠 놨습니다.

 

메세지 코드 1번은 Lock 동작으로, isLocked 필드에 true 값을 세팅하기만 하고,

메세지 코드 2번은 Unlock 동작으로, isLocked 필드에 false 값을 세팅하고,

mRunnableQueue에 쌓여있던 Runnable 들을 모두 빼내어

Handler.post() 메소드를 통해 모두 실행하게 합니다.

 

여기까지는 제가 원하는 Lock과 Unlock 동작이 간단하게 구현 되어있습니다.

그럼 이제 Queue에 쌓는 메소드만 만들면 되겠군요!

@Override
public void handleMessage(Message msg) {
    ...
    else if(msg.what == 3) {
        Runnable callback = (Runnable) msg.obj;
        if(callback != null) {
            try mRunnableQueue.put(callback); }
            catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
    ...
}

메세지 코드 3번은 매우 간단합니다.

Message.obj는 Message.obtain()을 통해 넘겨받은 Object 객체입니다. 

어떤 객체든 다 들어가지요~ 그래서 Message.obtain()을 통해

실행 하고자 하는 코드가 들어있는 Runnable 객체를 받은 뒤에,

무조건 mRunnableQueue에 쌓습니다.

 

이 코드는 지금 현재 Lock 여부에 상관 없이 무조건 쌓고,

Unlock 할 때 모조리 실행 되는 코드입니다.

근데 이런 동작은 뭔가... 좀 부족하죠~

 

그래서 좀 더 좋게 만들어 봤습니다!

@Override
public void handleMessage(Message msg) {
    ...
    else if(msg.what == 4) {
        Runnable callback = (Runnable) msg.obj;
        if(callback != null) {
            if(isLocked == false) { this.post(callback); }
            else {
                try mRunnableQueue.put(callback); }
                catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }
}

메세지 코드 4번도 매우 간단합니다.

Message.obtain()을 통해 실행 하고자 하는 코드가 들어있는 Runnable 객체를 받은 뒤에,

만약 Lock 상태라면 mRunnableQueue에 쌓고, 아니면 그냥 Handler.post()로 실행 시켜줍니다.

 

이것도 참 별것 없는 코드네요...

하지만 3번 보다는 좀 더 나은 것 같습니다.

 

 

[Use LockableMessageHandler]

 

만들었으니 써봅시다!

앞서 이야기 했던 Message.obtain() 메소드를 사용하여 사용 할 수 있습니다.

일단 만들고...

LockableMessageHandler mHandler = new LockableMessageHandler();

아까 Lock이 1번이었죠? 그렇다면 이렇게 사용 할 수 있습니다.

Message.obtain(mHandler, 1).sendToTarget();
    -> Message.obtain(Handler h, int what)

귀찮으니 한줄에... 그리고 쌓는 동작은 3번이었죠?

Message.obtain(mHandler, 3, new Runnable() { ... }).sendToTarget();
    -> Message.obtain(Handler h, int what, Object obj)

이렇게 사용 할 수 있습니다.

 

 

[Complete Source]

 

클래스 밖에서 Message.obtain()을 호출해서 사용 할 수 도 있지만,

사실 클래스 안에서도 Message.obtain()을 호출해서 사용 할 수 있습니다.

그렇게 되면 코드가 한결 더 간결해 지겠죠~

 

그리하여 정리해 본 전체 소스 입니다.

 

public final class LockableMessageHandler extends Handler {
    // Fields
    private boolean isLocked;
    private ArrayBlockingQueue<Runnable> mRunnableQueue;
    
    private final int CODE_LOCK          = 1;
    private final int CODE_UNLOCK        = 2;
    private final int CODE_PUT           = 3;
    private final int CODE_PUT_IF_LOCKED = 4;
    
    // Constructors
    public LockableMessageHandler() {
        isLocked false;
        mRunnableQueue new ArrayBlockingQueue<Runnable>(100);
    }

    // Override Methods
    @Override
    public void handleMessage(Message msg) {
        if(msg.what == CODE_LOCK) {
            isLocked true;
        }
        else if(msg.what == CODE_UNLOCK) {
            isLocked false;
        
            // RUN!!!
            while(mRunnableQueue.size() > 0) {
                Runnable runnable = mRunnableQueue.poll();
                this.post(runnable);
            }
        }
        else if(msg.what == CODE_PUT) {
            Runnable callback = (Runnable) msg.obj;
            if(callback != null) {
                try mRunnableQueue.put(callback); }
                catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
        else if(msg.what == CODE_PUT_IF_LOCKED) {
            Runnable callback = (Runnable) msg.obj;
            if(callback != null) {
                if(isLocked == false) { this.post(callback); }
                else {
                    try mRunnableQueue.put(callback); }
                    catch (InterruptedException e) { e.printStackTrace(); }
                }
            }
        }
    }

    // Handle Methods
    public void lock() {
        Message.obtain(thisCODE_LOCK).sendToTarget();
    }
    public void unlock() {
        Message.obtain(thisCODE_UNLOCK).sendToTarget();
    }  
    public void put(Runnable r) {
        Message.obtain(thisCODE_PUT, r).sendToTarget();
    }
    public void putIfLocked(Runnable r) {
        Message.obtain(thisCODE_PUT_IF_LOCKED, r).sendToTarget();
    }
}

깔끔하게 정리가 되었네요!

Handler를 extends 했기 때문에

평소에 쓰던 post(), postDelayed() 같은 메소드도 사용 가능합니다~

 

 

[Outro]

 

점점 내용도 난해해 지고 양도 많아지고 있습니다.

아무래도 기초지식 보다는 좀 Advance한 내용을 다루고자 하는 컨셉 때문인지도...

여튼 이 포스트에서 중점적으로 이야기 하고 싶은 것은

Handler를 원하는 대로 extends 하여 사용하는 방법입니다.

 

만약 Handler를 변경 하고자 하시는 분들에게 조금이나마 도움이 되었으면 합니다.

 

참고로 전 예전에 만들어 둔 AdvancedGestureDetectorWrapper와 함께

아주 잘~ 사용하고 있답니다. 으흐흐...

 

 

[Post Script]

 

이 내용에 대해 1월 부터 기획하고 있었는데 이제서야 마무리를 하게 됬습니다.

Nexus One이 튀어 나오는 바람에 리뷰를 한다고 설쳐대던 영향도 없지 않아 있었군요...

여튼 오래전 부터 쓰고 싶었던 포스팅을 마무리 하게 되어서 기분은 좋네요!

 

앞으로 더 난해한 내용들에 대해서 심도 있게 다루어 보고 싶은데...

시간이 별로 없군요...


[출처] 비즈페이님의 블로그

저작자 표시
신고
posted by By훈트 2011.01.24 18:59



가끔 귀찮다면 이렇게!

 

 

[Intro]

 

TextView를... 즉 글씨를 다루다 보면 폰트 설정 하는게 여간 귀찮은게 아닙니다.

14dip가 화면상에서 얼마나 큰지는 직접 찍어봐야 아는것이죠...

 

그냥 단순히 큰 글씨! 작은 글씨!를 보여주고 싶다~라고 할 때

이 방법을 사용해 보시죠!

 

 

[R.attr?]

 

이 녀석은 참 흥미로운 녀석입니다.

제가 다 뜯어 보진 않았지만... 뭔가 복잡한 속성 세팅을 한방에 해결해 주는 녀석이죠.

꼭 TextView에만 쓰이는건 아닙니다. ProgressBar를 만들 때도 쓰이더군요...

(사실 ProgressBar의 노란 그래디언트 부분은 이미지가 아니라

GradientDrawable에 R.attr의 값중 하나를 세팅한겁니다! 놀라워라...)

 

 

[R.attr.textAppearance Family]

 

그렇습니다... 그냥 쓰면 되는겁니다.

textAppearance
textAppearanceButton
textAppearanceInverse
textAppearanceLarge
textAppearanceLargeInverse
textAppearanceMedium
textAppearanceMediumInverse
textAppearanceSearchResultSubtitle (API Level : 5)
textAppearanceSearchResultTitle (API Level : 5)
textAppearanceSmall
textAppearanceSmallInverse

http://developer.android.com/reference/android/R.attr.html

 

위의 주소에서 좀 더 자세한 내용을 볼 수 있습니다.

그냥 변수 이름만 봐도 뭐하는지 딱 감이 잡힙니다.

 

그래서 한 번 직접 찍어 봤습니다!

 

 


 

 

Inverse 계열은 검은색 글씨라 배경을 빨갛게 칠해 봤습니다.

SearchResult 계열은 Level 5이고 나머지는 Level 1인데,

API설치 해 놓은게 Level 4 인지라 테스트는...

여튼! 뭐 그냥 이것만 보면 더 설명 할 게 없군요~

 

 

[Usage]

 

그럼 어떻게 사용하느냐! 아주 간단합니다.

TextView에 이런 Attribute 하나 넣어주면 됩니다.

android:textAppearance="?android:attr/textAppearanceSmall"

참 쉽죠?

폰트 설정하기 귀찮다면 이렇게...


[출처] 비즈페이님의 블로그

저작자 표시
신고