[Android] 맨날 까먹는 버튼 클릭 이벤트 핸들러 코드

맨날 까먹어 책 찾아 보고.. 인터넷 뒤져보고.. 해서 이 기회에 버튼에 대한 클릭 이벤트 핸들러 코드를 작성하는 것에 대해 정리를 해 놔야겠습니다. 머리가 나쁘니.. 손이 좀 고생을 해야겠지요..

레이아웃에 두개의 버튼이 있다고 가정하겠습니다. id는 각각 viewMode, editMode라고 하면.. 클릭 이벤트에 대한 핸들러 코드를 작성하는 방법에는 2가지가 있습니다. 물론 따져보면 둘다 동일한 방식이기는 하지만 코드 모냥새가 다르므로 다르다고 치겠습니다.

첫번째 방식입니다. 다수의 버튼들에 대한 이벤트 코드를 한자리에 가족같은 분위기로 다스리는 치국평천하 방식이라고 할 수 있겠습니다..

@Override
public void onCreate(Bundle savedInstanceState) {
    findViewById(R.id.viewMode).setOnClickListener(btnClickListener);
    findViewById(R.id.editMode).setOnClickListener(btnClickListener);
}

private Button.OnClickListener btnClickListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        switch(v.getId()) {
            case R.id.viewMode:
                map.setMouseMode(MouseMode.MapViewMode);
                break;

            case R.id.editMode:
                map.setMouseMode(MouseMode.EditMode);
                break;
        }
    }
};

두번째 방식입니다. 이 방식은 각 버튼마다 이벤트 처리 코드를 따라 분리해 두는 방식입니다.

@Override
public void onCreate(Bundle savedInstanceState) {
    ....
    
    findViewById(R.id.viewMode).setOnClickListener(
        new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                map.setMouseMode(MouseMode.MapViewMode);
            }
        }
    );

    findViewById(R.id.editMode).setOnClickListener(
        new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                map.setMouseMode(MouseMode.EditMode);
            }
        }
    );
    
    ....
}

앞서도 말씀드렸지만.. 첫번째나 두번째나 결국 똑 같은 방식입니다..

세번째 방식은 상당히 직관적인 것으로 생각되는데요. 레이아웃을 정의하는 XML에서 터치 이벤트 함수명을 지정하고 간단히 소스코드에서 해당 이벤트 함수를 추가해 주기만 하면 됩니다. 예를 들어서 레이아웃을 정의하는 XML 중 버튼 부분만을 보면..

위의 버튼에 대한 터치 이벤트 함수인 onClickButton은 아래처럼, 해당 뷰를 사용하는 엑티비트의 구현부에 추가하면 됩니다.

public void onClickButton(View v) {
    // 직관적이닷!
}

[Android] Spinner 또는 ListView에 Adapter 지정 후 바로 setSelection 호출 제대로 하기

제목도 참 길어 거시기 합니다. 안드로이드에서 Spinner나 ListView에 항목에 대한 목록을 지정하기 위해서는 Adapter 객체를 생성 및 구성해서 setAdapter 함수를 호출하여 지정합니다. 이렇게 지정하고 난 뒤에 바로 n번째 항목을 선택하도록 setSelection(n-1)과 같은 함수를 호출하게 됩니다. 예를 들어 아래와 같은 코드처럼 말입니다.

Spinner spRi = (Spinner)findViewById(R.id.spRi);
ArrayAdapter adp = new ArrayAdapter( ... );

...

spRi.setAdapter(adpr);

int n = ...;

...

spRi.setSelection(n-1);

그러나 이렇게 하면 n번째 항목은 선택되지 않고 항상 첫번째 항목이 선택되어 있습니다. 사용자 인터페이스(UI)에 대한 표현은 다른 연산보다 가장 나중에 처리되는 OS 정책 때문인데요. 이럴때는 아래와 같은 코드로 대신해야 합니다.

Spinner spRi = (Spinner)findViewById(R.id.spRi);
ArrayAdapter adp = new ArrayAdapter( ... );

...

spRi.setAdapter(adpr);

new Handler().postDelayed(new Runnable() {        
    public void run() {
        int n = ...;

        ...

        spRi.setSelection(n-1);
    }
}, 100);

즉, UI의 표현이 될때까지 기다렸다가 n-1 번째 항목을 선택하라는 것인데요.. 기다린다는 것이 100ms라는 애매한 시간으로 지정했다는 것이 걸리지만 잘 작동합니다. 않되면 이 애매한 시간을 더 늘려주세요. 개인적으로 Delay나 Sleep와 같은 기능을 하는 함수 호출을 싫어하지만… 근데 어디선가 지금 바로 UI를 업데이트 하라는 함수를 본 것 같은데 기억이 않난단 말입니다. 메모를 해 뒀어야 했는데 말입니다.