RoboGuice와 더불어 안드로이드 코딩을 굉장히 심플하게 만들어주는 AndroidAnnotations(AA) 오픈소스 라이브러리는 @Click 이라는 아주 유용한 어노테이션을 제공합니다. 

뷰의 ID가 설정된 어노테이션을 메소드에 선언해주면 설정된 뷰에 click 이벤트가 발생할 때 해당 메소드가 실행됩니다. 

기존에는  view.setOnClickListener(new OnClickListener() { ...}) 와 같은 OnClickListener 익명 클래스로 도배된 지저분한 코딩이 필요했었죠.


@Click(R.id.myButton)  

void myButtonClicked() { 

 ... ... // R.id.myButton을 누르면 이 메소드가 호출됩니다.

}  


@Click

void anotherButton(View clickedView) {

 ... ... // R.id.anotherButton 버튼을 누르면 이 메소드가 호출됩니다.

}


@Click({R.id.myButton1, R.id.myButton2})  

void myButtonsClicked() { 

 ... ... // R.id.myButton1 또는 R.id.myButton2를 누르면 이 메소드가 호출됩니다.

}  


AA를 사용하지 않는(사용하기 싫은?) 환경을 위해 비슷한 기능을 제공하는 우리만의 @Click을 만들어 보겠습니다. 

먼저 Click 어노테이션을 작성합니다. value는 클릭 이벤트가 발생하는 뷰의 id 값입니다.


@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface Click {

    int[] value() default -1;

}


@Click 어노테이션을 선언하는 메소드는 아래의 제약사항을 준수해야 합니다(AA의 @Click도 마찬가지입니다).

- 리턴 타입은 void만 가능

- 파라미터는 없거나 View 타입의 1개만 가능

- Exception을 던지는 것이 가능하지만 메소드 caller한테는 throw되지 않음 (AA에서는 아예 checked Exception도 못던집니다).


그리고 공통 액티비티 클래스에서 android.app.Activity의 onContentChanged() 메소드를 오버라이딩합니다.


    /**

     * 어노테이션에 설정된 뷰에 대해 클릭 이벤트 발생시 해당 메소드가 실행되도록 리스너 등록.

     *

     */

    @Override

    protected void onContentChanged() {

        super.onContentChanged();

        processClick(this);

    }


    public static void processClick(final Activity activity) {

        Class clazz = activity.getClass();


        for (final Method m : clazz.getDeclaredMethods()) {

            Click click = m.getAnnotation(Click.class);//m.isAnnotationPresent(Click.class)

            if (click == null)

                continue;


            Class<?>[] params = m.getParameterTypes();

            final boolean hasViewParameter = (params.length > 0);


            int[] ids = click.value();

            for (int id : ids) {

                View view = activity.findViewById(id);

                if (view != null) {

                    view.setOnClickListener(new OnClickListener() {

                        public void onClick(View view) {

                            try {

                                if (hasViewParameter) {

                                    m.invoke(activity, view);

                                } else {

                                    m.invoke(activity);

                                }

                            } catch (IllegalAccessException e) {

                                throw new RuntimeException(e);

                            } catch (InvocationTargetException e) {

                                throw new RuntimeException(e);

                            }

                        }

                    });

                }

            }


        }

    }


다 됐습니다. 공통 액티비티를 상속받은 액티비티에서 AA의 그것과 동일한 기능을 수행하는 @Click 어노테이션을 사용할 수 있습니다. 간단하죠?


※ 단, AndroidAnnotations의 @Click과 차이점이 몇가지 있습니다.

- AA에서는 setContentView()에 @Click 처리코드가 작성되지만, 위에서는 onContentChanged()에서 @Click을 처리하도록 했습니다. 

- AA의 @Click은 뷰의 id가 주어지지 않은 경우 메소드 이름으로 뷰 id를 자동으로 찾지만 위 코드는 그러한 기능은 제공하지 않습니다.

- AA에서는 컴파일타임에 Java Annotation Processing에 의해 @Click 처리 코드가 생성되지만 위 코드는 런타임에 @Click이 붙은 메소드를 이벤트리스너를 등록합니다.



Posted by 에코지오
,