[안드로이드 앱 개발] 6.이벤트를 알아보자
by 습작 | 13.10.28 01:26 | 5,175 hit

안드로이드에서 입력 이벤트는 키입력, 트랙볼, 포인터 등 크게 3가지 타입으로 구분한다.

GPS나 가속도 센서 같은 장치에서의 이벤트 입력도 있겠지만,
대부분의 어플에서 처리하는 이벤트는 UI(User Interface)를 기반으로 하며, View 클래스가 서비스를 담당한다.

어플에서 이벤트를 받는 방법은 
1. 안드로이드 프레임워크에 의해 호출되는 콜백 함수(Callback Function)를 확장 재정의하여(Override) 사용하는 방법과
2. View 클래스에서 제공하는 Callback함수로의 인터페이스인 Event listener를 사용하는 방법이 있는데 이벤트 리스너를 사용하는 방법이 보편적이다.

* 콜백 함수 Override 방법
콜백 함수를 사용할 때는 대상이 되는 View 내부에서 처리할 이벤트를 Override하여 자신 만의 코드를 기술한 다음,
다른 곳에 더이상의 이벤트 처리를 하지 않고 끝내고 싶다면 true를 리턴하고, 계속 진행시키려면 false를 리턴한다.
아래의 예제는 SDK TouchPaint.java 에 포함된 코드로 MyView 클래스 내에서 onTrackballEvent, onTouchEvent를 Override해서 사용하고 있음을 확인할 수 있다.
public class MyView extends View <
.......
        public MyView(Context c) <
            super(c);
......
        >
.......
        @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) <
            int curW = mBitmap != null ? mBitmap.getWidth() : 0;
            int curH = mBitmap != null ? mBitmap.getHeight() : 0;
            if (curW >= w && curH >= h) <
                return;
            >
            if (curW < w) curW = w;
            if (curH < h) curH = h;
            Bitmap newBitmap = Bitmap.createBitmap(curW, curH,
                                                   Bitmap.Config.RGB_565);
            Canvas newCanvas = new Canvas();
            newCanvas.setBitmap(newBitmap);
            if (mBitmap != null) <
                newCanvas.drawBitmap(mBitmap, 0, 0, null);
            >
            mBitmap = newBitmap;
            mCanvas = newCanvas;
            mFadeSteps = MAX_FADE_STEPS;
        >
        @Override protected void onDraw(Canvas canvas) <
            if (mBitmap != null) <
                canvas.drawBitmap(mBitmap, 0, 0, null);
            >
        >
        @Override public boolean onTrackballEvent(MotionEvent event) <
            int N = event.getHistorySize();
            final float scaleX = event.getXPrecision() * TRACKBALL_SCALE;
            final float scaleY = event.getYPrecision() * TRACKBALL_SCALE;
            for (int i=0; i                mCurX += event.getHistoricalX(i) * scaleX;
                mCurY += event.getHistoricalY(i) * scaleY;
                drawPoint(mCurX, mCurY, 1.0f, 16.0f);
            >
            mCurX += event.getX() * scaleX;
            mCurY += event.getY() * scaleY;
            drawPoint(mCurX, mCurY, 1.0f, 16.0f);
            return true;
        >
        @Override public boolean onTouchEvent(MotionEvent event) <
            int action = event.getActionMasked();
            if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL) <
                int N = event.getHistorySize();
                int P = event.getPointerCount();
                for (int i = 0; i < N; i++) <
                    for (int j = 0; j < P; j++) <
                        mCurX = event.getHistoricalX(j, i);
                        mCurY = event.getHistoricalY(j, i);
                        drawPoint(mCurX, mCurY,
                                event.getHistoricalPressure(j, i),
                                event.getHistoricalTouchMajor(j, i));
                    >
                >
                for (int j = 0; j < P; j++) <
                    mCurX = event.getX(j);
                    mCurY = event.getY(j);
                    drawPoint(mCurX, mCurY, event.getPressure(j), event.getTouchMajor(j));
                >
            >
            return true;
        >
위의 코드를 보면 onTrackballEvent, onTouchEvent 모두 Callback함수를 Override했고, 파라미터도 MotionEvent로 동일하지만
같은 방식으로 Override한 onSizeChanged, onDraw는 파라미터도 다르고 protected 되어 있는 차이가 있음을 통해 
입력 Event의 특성을 확인할 수 있다.
Override로 사용하는 입력 이벤트에는 아래와 같은 callback 함수들이 있다.
@Override public boolean onTouchEvent(MotionEvent event) <...
    float[] pt = < event.getX(), event.getY() >;
    int action = event.getActionMasked();
            if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL) <
                int N = event.getHistorySize();
                int P = event.getPointerCount();
                for (int i = 0; i < N; i++) <
                    for (int j = 0; j < P; j++) <
                        mCurX = event.getHistoricalX(j, i);
                        mCurY = event.getHistoricalY(j, i);
                        drawPoint(mCurX, mCurY,
                                event.getHistoricalPressure(j, i),
                                event.getHistoricalTouchMajor(j, i));
                    >
                >
@Override public boolean onTrackballEvent(MotionEvent event) <...
            int N = event.getHistorySize();
            final float scaleX = event.getXPrecision() * TRACKBALL_SCALE;
            final float scaleY = event.getYPrecision() * TRACKBALL_SCALE;
            for (int i=0; i                mCurX += event.getHistoricalX(i) * scaleX;
                mCurY += event.getHistoricalY(i) * scaleY;
                drawPoint(mCurX, mCurY, 1.0f, 16.0f);
            >
            for (int i=0; i                drawPoint(baseX+event.getHistoricalX(i)*scaleX,
                        baseY+event.getHistoricalY(i)*scaleY,
                        event.getHistoricalPressure(i),
                        event.getHistoricalSize(i));
            >
            drawPoint(baseX+event.getX()*scaleX, baseY+event.getY()*scaleY, event.getPressure(), event.getSize());
@Override public boolean onKeyDown(int keyCode, KeyEvent event) <...
        switch (keyCode) <
            case KeyEvent.KEYCODE_BACK:
                if (event.getRepeatCount() == 0 && mInputView != null) <
                    if (mInputView.handleBack()) <
                        return true;
                    >
                >
                break;
            case KeyEvent.KEYCODE_DEL:
                if (mComposing.length() > 0) <
                    onKey(Keyboard.KEYCODE_DELETE, null);
                    return true;
                >
                break;
            case KeyEvent.KEYCODE_ENTER:
                return false;
            default:
                if (PROCESS_HARD_KEYS) <
                    if (keyCode == KeyEvent.KEYCODE_SPACE
                            && (event.getMetaState()&KeyEvent.META_ALT_ON) != 0) <
                        InputConnection ic = getCurrentInputConnection();
                        if (ic != null) <
                            ic.clearMetaKeyStates(KeyEvent.META_ALT_ON);
                            keyDownUp(KeyEvent.KEYCODE_A);
                            keyDownUp(KeyEvent.KEYCODE_N);
                            keyDownUp(KeyEvent.KEYCODE_D);
                            keyDownUp(KeyEvent.KEYCODE_R);
                            keyDownUp(KeyEvent.KEYCODE_O);
                            keyDownUp(KeyEvent.KEYCODE_I);
                            keyDownUp(KeyEvent.KEYCODE_D);
                            return true;
                        >
                    >
                    if (mPredictionOn && translateKeyDown(keyCode, event)) <
                        return true;
                    >
                >
@Override public boolean onKeyUp(int keyCode, KeyEvent event) <...
@Override public boolean dispatchTouchEvent(MotionEvent ev) <...   Activity>ViewGroup에서 이벤트를 가로챌 수 있도록 한다.
@Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) <...   키보드 포커스의 변화 처리
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        if (focused) <
            switch (direction) <
                case View.FOCUS_DOWN:
                    mSelectedRow = 0;
                    break;
                case View.FOCUS_UP:
                    mSelectedRow = mNumRows - 1;
                    break;
                case View.FOCUS_LEFT:  // fall through
                case View.FOCUS_RIGHT:
                    // set the row that is closest to the rect
                    if (previouslyFocusedRect != null) <
                        int y = previouslyFocusedRect.top
                                + (previouslyFocusedRect.height() / 2);
                        int yPerRow = getHeight() / mNumRows;
                        mSelectedRow = y / yPerRow;
                    > else <
                        mSelectedRow = 0;
                    >
                    break;
                default:
                    // can't gleam any useful information about what internal
                    // selection should be...
                    return;
            >
            invalidate();
        >
* Event listener 사용법
Event listener를 사용하는 과정은 이벤트를 처리할 콜백 함수를 정의하는 것과 
해당 함수의 인스턴스를 set....Listener를 이용해서 등록하는 것으로 이루어진다.

크게 4가지 방법으로 이벤트 리스너의 정의와 등록을 할 수 있다.
1. Activity에 nested interface를 직접 기술하는 방법
   액티비티에 인터페이스를 기술하고 콜백 함수를 정의한다. set...Listener등록시에는 this를 넘겨준다.public class VoiceRecognition extends Activity implements OnClickListener <
......
    speakButton.setOnClickListener(this);
......
    public void onClick(View v) <
        if (v.getId() == R.id.btn_speak) <
            startVoiceRecognitionActivity();
        >
    >
2. 별도의 사용자 클래스를 인터페이스를 통해 정의하여 등록하는 방법
   implements 인터페이스(리스너이름)로 콜백이 포함된 클래스를 정의하고 해당하는 인스턴스를 등록한다.
    mButtonNext.setOnClickListener(new MyButtonListener());
    ......
    private class MyButtonListener implements OnClickListener <
        public void onClick(View v) <
            State player = mGameView.getCurrentPlayer();
            if (player == State.WIN) <
                GameActivity.this.finish();
            > else if (player == State.PLAYER1) <
                int cell = mGameView.getSelection();
                if (cell >= 0) <
                    mGameView.stopBlink();
                    mGameView.setCell(cell, player);
                    finishTurn();
                >
            >
        >
    >
3. Anonymous 클래스를 정의하여 등록하는 방법
   Anonymous 클래스를 통해 콜백 함수를 정의하고(mFadeListener), 해당 인스턴스를 등록한다.
public class Animation extends Activity <
    @Override
    protected void onCreate(Bundle savedInstanceState) <
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_animation);
        Button button = (Button)findViewById(R.id.fade_animation);
        button.setOnClickListener(mFadeListener);
        button = (Button)findViewById(R.id.zoom_animation);
        button.setOnClickListener(mZoomListener);
    >
    private OnClickListener mFadeListener = new OnClickListener() <
        public void onClick(View v) <
            startActivity(new Intent(Animation.this, Controls1.class));
            overridePendingTransition(R.anim.fade, R.anim.hold);
        >
    >;

4. Anonymous 클래스 정의와 등록을 한번에 수행하는 방법이 있다.
    set...Listener의 파라미터로 Anonymous 클래스를 직접 기술하는 방식        addButton.setOnClickListener(new View.OnClickListener() <
            public void onClick(View v) <
                Button newButton = new Button(LayoutAnimations.this);
                newButton.setText("Click to Delete " + (numButtons++));
                newButton.setOnClickListener(new View.OnClickListener() <
                    public void onClick(View v) <
                        container.removeView(v);
                    >
                >);
                container.addView(newButton, Math.min(1, container.getChildCount()));
            >
        >);
다음은 입력 이벤트를 포함한 대표적인 인터페이스와 콜백 함수를 매뉴얼에서 추려본것이다.view.View.OnClickListener
onClick(View v) : 뷰 클릭시 이벤트view.View.OnCreateContextMenuListener
onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) : 뷰를 long-touch하는 경우 나오는 컨텍스트메뉴 생성시 이벤트view.View.OnDragListener
onDrag(View v, DragEvent event) : 뷰에 드래그 발생시view.View.OnFocusChangeListener
onFocusChange(View v, boolean hasFocus) : 뷰의 포커스 상태 변동시view.View.OnKeyListener
onKey(View v, int keyCode, KeyEvent event) : 키 입력시 이벤트view.View.OnLongClickListener
onLongClick(View v) : 키를 길게 누를때view.View.OnTouchListener
onTouch(View v, MotionEvent event) : 터치 이벤트

위젯의 이벤트 리스너도 뷰의 리스너 사용과 유사하며 뷰의 리스너를 사용함과 더불어 아래와 같이 위젯 단위의 인터페이스와 콜백을 사용할 수 있다.widget.SeekBar.OnSeekBarChangeListener
onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) : 진행 상태 변동시
onStartTrackingTouch(SeekBar seekBar) : 트래킹 터치 시작
onStopTrackingTouch(SeekBar seekBar) : 트래킹 터치 종료widget.RadioGroup.OnCheckedChangeListener
onCheckedChanged(RadioGroup group, int checkedId) : 체크 상태 변동시widget.SearchView.OnQueryTextListener
onQueryTextChange(String newText) : 검색 텍스트 수정시 
onQueryTextSubmit(String query) : 검색 텍스트 입력 완료시widget.AdapterView.OnItemSelectedListener
onItemSelected(AdapterView<?> parent, View view, int position, long id) : 뷰내의 특정 아이템이 선택 되었을 때
onNothingSelected(AdapterView<?> parent) : 선택이 없을 때widget.TimePicker.OnTimeChangedListener
onTimeChanged(TimePicker view, int hourOfDay, int minute) : 시간 조정시widget.AdapterView.OnItemClickListener
onItemClick(AdapterView<?> parent, View view, int position, long id) :  뷰내 특정 아이템 클릭시widget.AbsListView.OnScrollListener
onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) : 리스트나 그리드 스크롤 완료후
onScrollStateChanged(AbsListView view, int scrollState) : 리스트나 그리드 스크롤 진행 시widget.RatingBar.OnRatingBarChangeListener
onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) : 평점 변경 시widget.TextView.OnEditorActionListener
onEditorAction(TextView v, int actionId, KeyEvent event) : 지정 작업 수행시widget.AdapterView.OnItemLongClickListener
onItemLongClick(AdapterView<?> parent, View view, int position, long id) : 뷰내 특정 아이템을 길게 클릭한 경우
참조문서 : http://developer.android.com/guide/topics/ui/ui-events.html
추천 1

댓글 11

벽하거사 2013.11.14 16:50
좋은 정보를 읽고 많은 도움이 되었습니다.
감사합니다.
한여름날의꿈 2013.10.29 11:08
잘보고 갑니다 ㅎ
천방지축 2013.10.29 10:39
 
감사합니다....
사막의장미 2013.10.29 10:36
쉽지 않네요~ㅋㅋㅋ
세계를품다 2013.10.29 08:12
감쏴
Oollalla 2013.10.29 00:24
좋은 정보 감사합니다.
라떼로주세요 2013.10.28 17:44
헐 어려워요 ^^;;;
길태 2013.10.28 15:53
어렵다 ...프로그래머...
서현서진서율아빠 2013.10.28 15:35
대체 뭔 내용인지...
알람방구 2013.10.28 13:46
감사합니다.
뽀대 2013.10.28 13:42
훌륭한 말들이네요~ ㅋ

이거슨 꿀팁 다른 게시글

게시물 더보기

이거슨 꿀팁 인기 게시글

  1. 알뜰폰 쓸 때 인터넷과 결합하려면2,639
  2. 해외여행 갈 때 데이터로밍 간편하고 싸게 쓰…2,506
  3. 기프티콘은 컬쳐랜드 쿠폰거래소에서 이용하…2,392
  4. 클리오 루즈힐 블룸 다이아 립스틱 5종 홈쇼…2,493
  5. 데이터 10GB+1Mbps 무제한 6,500원부터 쓸수…2,353
  6. 3월 알뜰폰 가성비 평생요금제 2가지 6GB 6천…2,098
  7. 3Mbps 속도 데이터무제한 최저가 검색2,100
  8. 컬쳐랜드에 쿠폰거래소가 새롭게 생겼어요2,062
  9. 해외여행 데이터로밍 일본 태국 대만 최저가…2,042
  10. 아싸컴에서 천만원 이벤트 하는 거 찾았다2,011

2024.05.16 21:00 기준