액티비티 화면은 TextView, EditText, ImageView, Button 등의 뷰로 구성된다. 이 뷰들을 사용자가 터치했을 때의 이벤트는 터치 이벤트를 직접 처리하지 않고, 각 뷰에서 제공하는 별도의 이벤트 처리 방식을 사용한다.
뷰의 이벤트도 터치 이벤트로 처리할 수 있지만 그렇게 하면 프로그래밍이 복잡해진다.
위 사진을 예로 들어보자.
이 화면에서 사용자가 하트 버튼을 누르면 발생하는 이벤트는 터치 이벤트로 처리할 수 있다.
즉, 액티비티에 onTouchEvent()를 선언하면 된다. 하지만 화면에 뷰가 최소 4개이므로 어느 뷰를 터치했는지 알아야 한다.
즉, onTouchEvent() 매개변수를 이용해 사용자가 터치한 지점의 좌표를 얻어서 처리를 해야하는데, 뷰가 많을 때는 프로그래밍이 복잡해진다.
그러나 각 뷰가 제공하는 별도의 이벤트, 예를 들면 버튼은 ClickEvent, 체크박스는 CheckedChangeEvent, 리스트는 ItemClickEvent로 처리하면 더 간단명료해진다.
따라서 대부분의 뷰는 해당 뷰에 맞는 이벤트를 따로 제공하며 이를 사용한다.
뷰 이벤트는 이전 장에서 배운 onTouchEvent, onKeyDown과 같은 이벤트 콜백 함수만 선언해서는 처리할 수 없다.
뷰 이벤트 처리는 이벤트 소스와 이벤트 핸들러로 역할이 나뉘며, 이 둘을 리스너로 연결해야 이벤트를 처리할 수 있다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.checkbox.setOnCheckedChangeListener(object : CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
Log.d("ryan", "체크박스 클릭: $isChecked")
}
})
}
}
앱이 시작되면 체크박스가 화면 중앙에 표시된다.
사용자가 체크박스를 클릭하면 setOnCheckedChangeListener가 호출된다.
리스너 내에서 Log.d를 사용하여 체크박스의 상태를 로그로 출력한다.
인터페이스로 구현한 object 클래스를 이벤트 핸들러로 만들었지만, 액티비티 자체에서 인터페이스를 구현할 수도 있다.
또한 이벤트 핸들러를 별도의 클래스를 만들어 처리할 수도 있으며 코틀린의 SAM기법을 사용할 수도 있다.
이 방법은 액티비티 클래스 자체가 이벤트 리스너 인터페이스를 구현하는 방식이다.
class MainActivity : AppCompatActivity(), CompoundButton.OnCheckedChangeListener {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.checkbox.setOnCheckedChangeListener(this)
}
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
Log.d("MyApp", "체크박스 상태: $isChecked")
}
}
장점:
단점:
class CheckboxEventHandler : CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
Log.d("MyApp", "체크박스 상태: $isChecked")
}
}
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.checkbox.setOnCheckedChangeListener(CheckboxEventHandler())
}
}
장점:
단점:
SAM 변환은 단일 추상 메서드를 가진 인터페이스를 간단한 람다식으로 표현할 수 있게 해주는 코틀린의 기능이다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.checkbox.setOnCheckedChangeListener(this::onCheckboxChanged)
}
private fun onCheckboxChanged(buttonView: CompoundButton, isChecked: Boolean) {
Log.d("MyApp", "체크박스 상태: $isChecked")
}
}
장점:
단점:
각 방법은 상황에 따라 장단점이 있다:
안드로이드는 앱의 화면을 구성하는 데 필요한 다양한 뷰를 제공하며 대부분 뷰에서 자체 이벤트를 제공한다. 그런데 뷰가 아무리 많아도 이벤트 처리 구조는 동일하다.
그러므로 이벤트 소스와 이벤트 핸들러를 리스너로 연결하는 구조만 이해한다면 어떤 뷰 이벤트라도 쉽게 처리할 수 있다.
ClickEvent는 OnClickListener를 구현한 객체를 이벤트 핸들러로 등록해야 한다.
LongClickEvent는 OnLongClickListener를 구현한 객체를 이벤트 핸들러로 등록해야 한다.
class MainActivity : AppCompatActivity() {
private lateinit var button: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button = findViewById(R.id.button)
// Click 이벤트 처리
button.setOnClickListener {
// 클릭했을 때 수행할 작업
Toast.makeText(this, "Button Clicked!", Toast.LENGTH_SHORT).show()
}
// Long Click 이벤트 처리
button.setOnLongClickListener {
// 길게 클릭했을 때 수행할 작업
Toast.makeText(this, "Button Long Clicked!", Toast.LENGTH_SHORT).show()
true // true를 반환하면 롱 클릭 이벤트가 처리되었음을 의미
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click or Long Click Me"
android:layout_centerInParent="true"/>
</RelativeLayout>
만약 버튼을 클릭하면 토스트 메시지에 Button Clicked가 뜬다.
중요한 점은 이벤트 소스, 리스너, 이벤트 핸들러가 무엇인지 파악하는 것이다.
이벤트 소스: button (버튼 객체)
리스너: OnClickListener (클릭 이벤트를 처리), OnLongClickListener (롱 클릭 이벤트를 처리)
이벤트 핸들러: 클릭 및 롱 클릭 이벤트가 발생했을 때 실행되는 Toast 메시지를 표시하는 코드 블록