[Android] 사용자 정의 이벤트 추가

자바에서 사용자 정의 이벤트를 추가하는 방법에 대한 글(자바에서 사용자정의 이벤트 추가하기)을 남긴 적이 있습니다. 그 글에서 소개한 사용자 정의 이벤트 추가 방식은 안드로이드에서는 추천하지 않는다는 글로 정리를 했습니다.

안드로이드에서는 어떤 식으로 사용자 정의 이벤트를 남기면 좋을까에 대한 것이 이 글의 주제입니다. 일반적인 자바에서 사용자 정의 이벤트는 이벤트 리스트에 여러개의 이벤트를 등록하게 됩니다. 예를 들어서 특정한 뷰에 대한 마우스 클릭에 대한 이벤트라면.. 여러개의 이벤트를 등록하게 하는 방식입니다. 하지만 안드로이드라는 환경은 한정된 리소스를 가지고 있음으로 해서.. 예로 든 특정 뷰에 대한 마우스 클릭에 대한 이벤트에 대해 ‘하나’만을 등록하는 방식이 더 적당하다고 할 수 있습니다. 물론… 필요하다면 자바에서 일반적인 사용자 정의 이벤트를 추가하는 방식을 안드로이드에서 사용해도 됩니다. 그럼.. 안드로이드에서 적합한 사용자 정의 이벤트를 추가하는 방식에 대해 정리해 보겠습니다.

먼저 새롭게 추가할 이벤트가 무엇인지 확인해 봅니다. 예를 들어 맵엔진 개발에 있어서 맵의 축척이 변경되었을때 발생하는 이벤트를 추가하는 예를 통해 살펴보겠습니다. 1개의 클래스와 또 다른 한개의 인터페이스를 추가해야 하는데.. 새롭게 추가할 클래스는 이벤트 객체에 대한 클래스이며 새롭게 추가할 인터페이스는 이벤트 리스너입니다. 이 둘은 일반적인 자바에서의 사용자 정의 이벤트에 대해서 동일한 내용입니다. 다른게 없습니다.

아래는 축척 변경 이벤트에 대한 이벤트 객체 클래스 소스입니다.

package geoservice.blackpoint.events;

import geoservice.blackpoint.XrMap;
import java.util.EventObject;

public class MapEvent extends EventObject {
    public MapEvent(Object source) {   
        super(source);   
    }   
 
    public XrMap getMap() {
        XrMap map = (XrMap)getSource(); 
        return map;
    }
}

이벤트 객체는 이벤트를 발생시킨 주체(getSource 매서드를 통해 반환됨)를 기본적인 내용으로 하며 그외 더 필요한 정보를 담을 수 있습니다. 다음으로 이벤트 리스너에 대한 인터페이스입니다.

package geoservice.blackpoint.events;

import java.util.EventListener;

public interface OnMapScaleChangedEventListener extends EventListener {
    void onMapScaleChanged(MapEvent event);
}

이벤트 리스너 인터페이스에는 여러개의 이벤트를 넣을 수 있는데.. 이 경우 맵축척 변경에 대한 이벤트만이 있습니다. 이제 축척 변경에 대한 이벤트에 필요한 클래스와 인터페이스가 준비되었습니다.

그럼 맵엔진 단에서 맵 축척이 변경되었을때 이벤트를 발생시키는 것에 대해 코드를 통해 살펴보겠습니다.

class XrMap {
    // 이벤트 코드에만 집중하자

    public OnTapUpEventListener onTapUpEventListener = null;    
    public void setOnTapUpListener(OnTapUpEventListener listener) {
        onTapUpEventListener = listener;
    }

    private void __맵축척을변경시키는부분__ {
        if(onMapScaleChangedEventListener != null) {
            MapEvent event = new MapEvent(this);
            onMapScaleChangedEventListener.onMapScaleChanged(event);
        }
    }

    // 이벤트 코드에만 집중하자
}

보시는 것처럼.. 이벤트 리스너에 대한 변수와 이 이벤트 리스너를 할당시키기 위한 매서드 끝으로 실제 이벤트가 발생될때 이벤트 리스너를 실행해 주는 코드로 구성되어 있습니다. 여기까지가 일반적으로 안드로이드에서 사용자 정의 이벤트를 만들어 주는 방식입니다. 그럼.. 이 맵축척이 변경되었을때 발생하는 이벤트에서 원하는 코드를 작성해보는 샘플 코드는 어떻게 될까.. 아래 코드를 통해 살펴보시기 바랍니다.

map.setOnMapScaleChangedListener(
    new OnMapScaleChangedEventListener() {
        public void onMapScaleChanged(MapEvent event) {
            Toast.makeText(
                XrMapTest.this, 
                "SCALE CHANGED", 
                Toast.LENGTH_SHORT).show();
       }
    }
);

map은 앞서 XrMap 클래스에 대한 변수입니다. 축척이 변경될때마다 화면에 SCALE CHANGED라는 메세지를 토스트를 통해 띠웁니다.. 끝으로 안드로이드에서 버튼을 클릭했을 때 이벤트를 할당하는 코드를 살펴보고.. 위의 맵축척 변경 이벤트 할당 코드와 비교해 보시기 바랍니다.

Button btnro= (Button)findViewById(R.id.ro);
btnro.setOnClickListener(
    new Button.OnClickListener() {
        public void onClick(View v) {
            CoordMapper cm = map.getRendererManager().getCoordMapper();
            cm.rotate(15);
            map.update();
        }
    }
);

보시는 것처럼.. 이벤트를 할당하는 두개의 구조가 모두 동일함을 알 수 있습니다. 이상으로 안드로이드에서 사용자 정의 이벤트를 할당하는 방법에 대한 정리를 마치도록 하겠습니다.

[Java] 사용자 정의 이벤트 추가하기

컴포넌트는 시스템을 구성하는 하나의 부품으로 자신을 운용하는 시스템이나.. 또는 자신과 관계를 가지는 다른 컴포넌트와 상호 통신을 위한 효율적인 방안으로 이벤트라는 개념을 가지고 있습니다. 자바(Java)에서 어떤 컴포넌트를 개발하였고.. 이 컴포넌트에 사용자 정의 이벤트를 추가하는 방법에 대한 설명입니다. 이벤트를 추가하기 위해 새롭게 정의해야 할 클래스는 총 2가지입니다.

  • 이벤트에 대한 정보를 담고 있으며 EventObject를 상속받는 클래스
  • 이벤트에 대해 실행해야할 코드를 정의할 수 있는 방법을 제공하며 EventListener를 확장하는 인터페이스

먼저 EventObject를 상속받으며.. 이벤트에 대한 정보(이벤트를 발생시킨 객체 등)를 담고 있는 클래스에 대한 예를 들어보겠습니다.

import java.util.EventObject;

public class CustomEvent extends EventObject {
    public CustomEvent(Object source) {
        super(source);
    }
}

이벤트를 발생시킨 객체에 대한 정보를 위해 생성자에서 Object 타입의 source를 인자로 받습니다. 만약 이벤트에 대한 다른 정보가 필요하다면 이 클래스에 추가하면 됩니다.

다음으로 EventListener 인터페이스를 확장하며 이벤트에 대해 실행해야할 코드를 정의할 수 있는 길을 열어주는 인터페이스에 대한 예를 들어 보겠습니다.

import java.util.EventListener;

public interface CustomEventListener extends EventListener {
    void customEvent(CustomEvent event);
}

이 인터페이스의 매서드인 customEvent에 이벤트가 발생했을때 어떤 코드를 실행할지를 정의하게 됩니다. 이렇게 사용자 정의 이벤트를 추가하기 위해 필요한 2개의 클래스를 정의했으므로 이제.. 컴포넌트에 이벤트를 코드를 살펴보겠습니다. 컴포넌트의 클래스는 CustomComponent이라고 하고 이벤트에 관련된 코드만을 살펴보면 다음과 같습니다.

public class CustomComponent {
    .... 이벤트에 관련된 코드만 봅시다!

    protected EventListenerList listenerList = new EventListenerList();
 
    public void addCustomEventListener(CustomEventListener listener) {
        listenerList.add(CustomEventListener.class, listener);
    }
 
    public void removeCustomEventListener(CustomEventListener listener) {
        listenerList.remove(CustomEventListener.class, listener);
    }
 
    private void fireCustomEvent(CustomEvent event) {
        Object[] listeners = listenerList.getListenerList();

        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i] == CustomEventListener.class) {
                ((CustomEventListener)listeners[i+1]).customEvent(event);
            }
        }
    }
 
    .... 이벤트에 관련된 코드만 봅시다!
}

자바는 하나의 이벤트에 대해서도 여러개의 이벤트 처리 코드(핸들러)를 담을 수 있습니다. 이 이벤트 핸들러를 담을 수 있는 컨테이너가 필요한데.. 그 컨테이너가 바로 4번 코드에서 EventListenerList 타입의 listenerList 변수입니다. 그리고 가장 앞서 언급한 새로운 이벤트를 추가하고 제거 하는 매서드가 6번과 10번 코드의 addCustomEventListener과 removeCustomEventListener입니다. 그리고 14번 코드인 fireCustomEvent 매서드는 이벤트가 발생해야할 적당한 시점에서 이벤트를 발생을 위해 ‘호출’ 해주는 매서드입니다. 이 매서드가 private로 선언된 이유는 이벤트가 발생하는 시점은 컴포넌트 안에서 정의하는 것이고 외부에서 그 시점을 컨트롤 해서는 않되기 때문입니다.

이상으로 자바에서 사용자 정의 이벤트(Custom Event)를 정의하는 방법에 대해 설명드렸습니다. 하지만 여기서 EventListenerList 클래스는 자바의 swing 패키지에 정의된 클래스로 swing은 안드로이드에서는 제공하지 않는 패키지입니다. 해서 안드로이드에서는 EventListenerList 클래스를 직접 개발자가 정의해 주어야 합니다. EventListenerList 클래스는 아래 다운로드를 통해 받으시기 바랍니다.

왜 안드로이드에서 EventListenerList를 제공하지 않는지에 대한 고민해 해보시기 바랍니다. 만약 제가 안드로이드에서 어떤 컴포넌트를 개발할때 새로운 이벤트를 정의해야할 필요가 있다면.. EventListenerList를 사용하지 않을 겁니다. 이에 대한 이야기는 다음 기회에 말씀드리로 하겠습니다.