240104 TIL #286 Android #9 Event

김춘복·2024년 1월 4일
0

TIL : Today I Learned

목록 보기
286/571

Today I Learned

오늘은 이어서 이벤트 처리 방법에 대해 공부했다.


Event

사용자의 동작은 이벤트를 발생시키고, 앱은 화면에서 발생하는 다양한 사용자 이벤트를 처리해 상호작용한다.

Touch Event

손가락으로 화면을 잠시 눌렀다가 떼는 행위.

터치 이벤트 처리

  • activity 클래스 안에서 원하는 대로 터치 이벤트를 처리하기 위해선, 아래와 같이 onTouchEvent() 콜백 함수를 선언해 재정의(Override)해야 한다.
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        return super.onTouchEvent(event)
    }
  • 위처럼 콜백함수 선언해두면, 유저가 액티비티 화면을 터치하는 순산 onTouchEvent() 함수가 자동으로 호출된다.

  • 매개변수인 MotionEvent 객체에는 터치의 종류와 좌표값이 담긴다.

  • 터치 이벤트 종류

    1. Action_Down : 화면을 손가락으로 누른 순간의 이벤트
    2. Action_Move : 손가락으로 화면을 누른 채 이동하는 순간의 이벤트
    3. Action_UP : 화면에서 손가락을 떼는 순간의 이벤트
  • 터치 발생 좌표 얻기

    x: 이벤트가 발생한 뷰 안의 좌상단 기준 x좌표
    y : 이벤트가 발생한 뷰 안의 좌상단 기준 y좌표
    rawX : 디바이스 전체 화면에서의 x좌표
    rawY : 디바이스 전체 화면에서의 y좌표

  • MotionEvent?의 action을 해당 이벤트로 판별해 동작 처리.

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when(event?.action){
            MotionEvent.ACTION_DOWN -> {
                Log.d("event","터치 좌표는 x : ${event.x}, rawX : ${event.rawX}")
            }
            MotionEvent.ACTION_UP -> {
                Log.d("event", "UP")
            }
        }
        return super.onTouchEvent(event)
    }

Key Event

사용자가 폰의 키를 누르는 순간 발생하는 이벤트

키이벤트의 종류

  1. onKeyDown : 키를 누른 순간의 이벤트
  2. onKeyLongPress : 키를 오래 누르는 순간의 이벤트
  3. onKeyUp : 키를 떼는 순간의 이벤트
  • 여기서 의미하는 키에서 텍스트 입력시 화면 하단에서 나오는 소프트 키보드를 이용해 입력한 키는 제외된다. 즉, 소프트 키보드는 키 이벤트 X.

  • 폰 하드웨어 버튼인 전원, 볼륨UP&DOWN, 그리고 하단 네비게이션 바의 뒤로가기, , 오버뷰(최근 사용한 목록)의 키가 키이벤트에 포함된다.

  • 하지만 전원, 홈, 오버뷰 버튼은 액티비티의 onKeyDown() 함수로 앱에서 이벤트를 처리할 수 없는 버튼이다.
    즉, 뒤로가기, 볼륨업, 볼륨다운은 처리 가능하다.

  • 뒤로가기 버튼은 앱에서 이벤트 처리하지 않으면 이전 액티비티로 이동한다.

처리

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        when(keyCode) {
            KeyEvent.KEYCODE_BACK -> Log.d("event", "뒤로가기 버튼")
            KeyEvent.KEYCODE_VOLUME_UP -> Log.d("event", "볼륨 업")
            KeyEvent.KEYCODE_VOLUME_DOWN -> Log.d("event", "볼륨 다운")
        }

        return super.onKeyDown(keyCode, event)
    }

View Event

TextView, ImageView 같은 뷰를 사용자가 터치했을때의 이벤트

  • 뷰 이벤트는 터치와 키이벤트로 처리하면 복잡해지므로, 각 뷰에서 별도로 제공하는 이벤트를 사용한다.

처리 구조

터치, 키 이벤트는 콜백함수인 onTouchEvent(), onKeyDown() 함수만 액티비티에 선언해도 이벤트를 처리할 수 있다.
하지만, 뷰 이벤트는 콜백함수의 선언만으로는 처리할 수 없다.

  • 구성요소
    이벤트 소스 : event가 발생한 객체
    이벤트 핸들러 : event 발생 시 실행할 로직이 구현된 객체
    리스너 : 이벤트 소스와 핸들러를 연결하는 함수

  • 이벤트 소스에 리스너로 이벤트 핸들러를 등록하면 이벤트 발생 시 실행되는 구조.

  • 대부분 이벤트 핸들러의 형식은OnXXXListener이다.

  • 기본 구조 예시
    checkbox1이 이벤트소스, setOnCheckedChangeListener가 리스너, object객체가 이벤트 핸들러이다.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.checkbox1.setOnCheckedChangeListener(object: CompoundButton.OnCheckedChangeListener{
            override fun onCheckedChanged(p0: CompoundButton?, p1: Boolean) {
                Log.d("event","체크박스 클릭")
            }
        })
    }

이벤트 핸들러 구현 방법

어느 방법이든 지정된 인터페이스를 구현한 객체를 이벤트 핸들러로 등록한다.

// 1. 액티비티에서 인터페이스 구현
class MainActivity1 : AppCompatActivity(), CompoundButton.OnCheckedChangeListener {
	override fun onCheckedChanged(p0: CompoundButton?, p1: Boolean) {...}
    //...
    
    binding.checkbox1.setOnCheckedChangeListener(this)
}    

// 2. 이벤트 핸들러를 별도의 클래스로 구현
class MyEventHandler : CompoundButton.OnCheckedChangeListener {
	override fun onCheckedChanged(p0: CompoundButton?, p1: Boolean) {...}
}
//...
	binding.checkbox1.setOnCheckedChangeListener(MyEventHandler())

// 3. 람다(SAM 기법)로 구현
class MainActivity1 : AppCompatActivity() {
	// ...
	binding.checkbox1.setOnCheckedChangeListener { p0, p1 -> Log.d("event", "체크박스 클릭") 
}

ClickEvent / LongClickEvent

뷰의 최상위 클래스인 View에 정의된 이벤트로 가장 기초적이면서 많이 사용되는 이벤트

  • ClickEvent : 뷰를 짧게 클릭할 때 발생하는 이벤트
    LongClickEvent : 뷰를 길게 클릭할 때 발생하는 이벤트

  • 두 이벤트의 핸들러

open fun setOnClickListenr(l: View.onClickLister?): Unit
open fun setOnLongClickListenr(l: View.onLongClickLister?): Unit
  • SAM 기법 처리 예시
    추상함수 하나를 포함한 인터페이스를 람다기법으로 처리
binding.btn.setOnClickLister {
	//...
}
  • 예시 / 버튼 이벤트 처리
        binding.button1.setOnClickListener {
            Log.d("event", "버튼 클릭")
        }
        
        // LongClick은 콜백함수의 반환값이 Boolean이라 마지막줄 true가 필요하다.
        binding.button1.setOnLongClickListener {
            Log.d("event", "롱버튼 클릭")
            true
        }

실습 - 스톱워치

  • MainActivity.kt
package com.example.ch8_event

import android.os.Bundle
import android.os.SystemClock
import android.view.KeyEvent
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.ch8_event.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    // 뒤로가기 버튼 누른 시각을 저장하는 속성
    var initTime = 0L

    // 멈춘 시각을 저장하는 속성
    var pauseTime = 0L

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // start 버튼 클릭 이벤트 처리
        binding.startButton.setOnClickListener {
            binding.chronometer.base = SystemClock.elapsedRealtime() + pauseTime
            binding.chronometer.start()
            // 버튼 표시 조정
            binding.startButton.isEnabled = false
            binding.stopButton.isEnabled = true
            binding.resetButton.isEnabled = true
        }
        // stop 버튼 클릭 이벤트 처리
        binding.stopButton.setOnClickListener {
            pauseTime = binding.chronometer.base - SystemClock.elapsedRealtime()
            binding.chronometer.stop()
            binding.stopButton.isEnabled = false
            binding.startButton.isEnabled = true
            binding.resetButton.isEnabled = true
        }
        // reset 버튼 클릭 이벤트 처리
        binding.resetButton.setOnClickListener {
            pauseTime = 0L
            binding.chronometer.base = SystemClock.elapsedRealtime()
            binding.chronometer.stop()
            binding.stopButton.isEnabled = false
            binding.resetButton.isEnabled = false
            binding.startButton.isEnabled = true
        }

    }

    // 뒤로가기 버튼 이벤트 핸들러
    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        // 누른 버튼이 뒤로가기 버튼일 때
        if (keyCode === KeyEvent.KEYCODE_BACK) {
            // 뒤로가기 버튼을 처음 눌렀거나, 누른지 3초가 넘었을 때
            if (System.currentTimeMillis() - initTime > 3000){
                Toast.makeText(this, "종료하려면 한번 더 누르세요!", Toast.LENGTH_SHORT).show()
                initTime = System.currentTimeMillis()
                return true
            }
        }

        return super.onKeyDown(keyCode, event)
    }

}
  • 실행 화면
profile
Backend Dev / Data Engineer

0개의 댓글