2019년 10월 22일 화요일

안드로이드 이벤트 처리





1. 터치 이벤트, 제스처 이벤트

터치 이벤트는 화면을 손가락으로 누를 때 발생하는 이벤트들을 처리할 수 있다.
제스처 이벤트는 터치 이벤트 중에서 일정한 패턴, 스크롤과 같은 이벤트를 처리할 수가 있다.
제스처 이벤트는 터치 이벤트를 받은 후에 추가적인 확인을 거쳐 만들어진다.




package org.techtown.sampleevent;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView textView;
    GestureDetector detector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);

        View view = findViewById(R.id.view);
        /**
         * 뷰에 OnTouchListener 를 등록한다.
         */
        view.setOnTouchListener(new View.OnTouchListener(){
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent){
                // 액션의 상태 값을 가져오는 메소드
                int action = motionEvent.getAction();
                // 현재 X, Y 좌표의 위치를 가져옴
                float curX = motionEvent.getX();
                float curY = motionEvent.getY();

                if(action == MotionEvent.ACTION_DOWN) {
                    println("손가락 눌림 : "+ curX +" , " + curY);
                }else if( action == MotionEvent.ACTION_MOVE) {
                    println("손가락 움직임 : " + curX + " , " +  curY);
                }else if(action == MotionEvent.ACTION_UP) {
                    println("손가락 뗌 : " + curX + " , " + curY);
                }
                return true;
            }
        });

        //제스쳐 이벤트를 생성
        detector = new GestureDetector(this, new GestureDetector.OnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                println("onDown() 호출됨.");
                return true;
            }

            @Override
            public void onShowPress(MotionEvent e) {
                println("onShowPress() 호출됨.");
            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                println("onSingleTapUp() 호출됨.");
                return true;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                println("onScroll() 호출됨 : "+ distanceX + " , " + distanceY);
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                println("onLongPress() 호출됨.");
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                println("onFling() 호출됨 : "+ velocityX + ", " + velocityY);
                return true;
            }
        });
        /**
         * 두번째 뷰에다 제스쳐이벤트를 설정
         */
        View view2 = findViewById(R.id.view2);
        view2.setOnTouchListener(new View.OnTouchListener(){
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent ){
                    // 뷰를 터치했을때 발생하는 터치이벤트를 제스터 디렉터로 전달한다.
                    detector.onTouchEvent(motionEvent);
                    return true;
                }
        });
    }

    public void println(String data) {
        textView.append(data + "\n");
    }

}



스크롤뷰 -> 리니어레이아웃 -> 텍스트뷰에서 이벤트 정보를 찍어보기로 한다.


파란 부분이 첫번째 뷰, 주황 부분이 두번째 뷰이다.
이벤트가 발생할때마다 아래의 스크롤뷰에 수직으로 데이터들이 쌓인다.





2. 키 이벤트 처리하기

키 입력은 시스템 버튼이 눌렸거나, 문자열 키가 입력된 경우에 관하여 이벤트 처리를 할 수 있다.


- 시스템 [back] 버튼 눌렸을 때 토스트 메시지 생성하기
시스템 버튼인 back은 onKeyDown 메서드를 재정의 하여 간단히 이벤트 처리할 수 있다.


MainActivity.java 에서 우클릭 한 후 [Generate] 선택
[Override Methods] 선택


[onKeyDown] 메소드를 선택하고 [OK] 하면 메소드가 자동으로 생성된다.





@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        
        if(keyCode == KeyEvent.KEYCODE_BACK){
            Toast.makeText(this, "시스템 [back] 버튼이 눌렸습니다.",
                    Toast.LENGTH_LONG).show();
            return true;
        }
        return false;
    }





3. 단말 방향 전환 시 이벤트 처리

단말의 방향 전환 시에는 앱화면에 대하여 가로, 세로 각각 XML 레이아웃을 만들 필요가 있다.
단말의 방향이 바뀌었을 때는 액티비티는 메모리에서 없어졌다가 다시 만들어지게 된다.


/app/res/layout/ : 단말 세로 방향
/app/res/layout-land/ : 단말 가로 방향


[res] 폴더에서 우클릭하여 [New] -> [Android Resource Directory] 클릭


Directory name 을 layout-land 로 입력하고 OK.


Project Files 구조로 볼때만 layout-land 폴더가 보이게 된다.
생성된 것을 확인.


layout 폴더와 layout-land 는 같은 역할을 하지만 단말이 가로방향으로 보일 때는 layout-land 폴더 안에 있는 XML 레이아웃 파일이 사용된다.
layout-land 폴더가 없다면 어떤 방향이든 layout 폴더의 XML 파일을 디폴트로 사용하게 된다.


단말의 방향이 바뀌었을 때는 액티비티는 메모리에서 없어졌다가 다시 만들어지게 된다면, 이전에 선언해 두었던 변수 값이 사라지므로 이를 유지하도록 해야한다.

이를 위해 onSaveInstanceState 콜백 메서드가 제공된다.
이 메서드는 액티비티가 종료되기 전의 상태를 저장한다.
이때 저장한 상태는 onCreate() 메서드가 호출될 때 전달되는 번들 객체로 복원할 수 있다.




public class MainActivity extends AppCompatActivity {
    String name;

    EditText editText;

    /**
     * onCreate의 파라미터를 보면 Bundle 객체인 savedInstanceState를 인자로 받게 되어 있다.
     * 이 객체에서 데이터를 가져와서 name 변수에 다시 할당하게 되어 복구되는 것이다.
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        showToast("onCreate 호출됨.");

        editText = findViewById(R.id.editText);

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view){
                // 버튼을 클릭했을 때 사용자가 입력한 값을 name 변수에 저장
                name = editText.getText().toString();
                showToast("입력된 값을 변수에 저장하였습니다 : "+ name);
            }
        });
        // 이 화면이 초기화 될 때 name 변수값 복원
        // ex) 단말 방향 전환시 액티비티가 메모리에서 제거되는 상황
        if(savedInstanceState != null){
            name = savedInstanceState.getString("name");
            showToast("값을 복원하였습니다 : "+name);
        }

    }
    /**
     * onSaveInstanceState 메소드는
     * 액티비티가 종료되기 전의 상태를 저장한다.
     * @param outState
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("name", name);
    }

    @Override
    protected void onStart() {
        super.onStart();
        showToast("onStart 호출됨.");
    }

    @Override
    protected void onStop() {
        super.onStop();
        showToast("onStop 호출됨.");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        showToast("onDestroy 호출됨.");
    }

    public void showToast(String data){
        Toast.makeText(this, data, Toast.LENGTH_LONG).show();
    }

}





4. 단말 방향이 바뀔 때 액티비티 유지하는 방법

- /app/manifests/AndroidManifest.xml 수정

아래와 같이 빨간네모상자의 속성을 설정하면, 시스템은 단말의 방향이 바뀌는 시점에 액티비티에게 상태 변화를 알려줄 수 있게 된다. 
keyboardHidden 값은 키패드가 자동으로 나타나지 않도록 하는 설정이다.


activity 태그가 액티비티를 등록할 때 사용하는 태그이다.
configChanges 속성을 위와 같이 추가한다.





public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /**
     * 액티비티에서 방향 전환을 인식할 수 있게 되어,
     * 단말의 방향이 바뀌는 시점에 자동으로 이 메서드가 호출된다.
     * @param newConfig
     */
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            showToast("가로 방향: ORIENTATION_LANDSCAPE");
        }else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
            showToast("세로 방향: ORIENTATION_PORTRAIT");
        }
    }
    public void showToast(String data){
        Toast.makeText(this, data, Toast.LENGTH_LONG).show();
    }

}




5. 단말 방향을 고정시키기

- /app/manifests/AndroidManifest.xml 수정


screenOrientation 속성에서 landscape로 설정하면 화면이 가로로 고정된다.




댓글 없음:

댓글 쓰기