Android View 이벤트

timothy jeong·2021년 10월 31일
0

Android with Kotlin

목록 보기
5/69

이상 살펴본 터치 이벤트, 키 이벤트와는 별개로 각 View 들은 고유의 이벤트를 가지고 있다.

뷰 이벤트 처리 구조

뷰 이벤트 처리는 이벤트 소스, 이벤트 핸들러로 역할이 나뉘며 둘을 리스너 로 연결해야 이벤트를 처리할 수 있다.

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

구조가 이렇게 되어있지만, 코드로는 이벤트 소스에 리스너로 이벤트 핸들러를 등록하는 형태이다. checkBox 이벤트를 조금 살펴보자면 CompoundButton 클래스에는 다음과 같은 인터페이스가 정의되어 있다.

 public static interface OnCheckedChangeListener {
        void onCheckedChanged(CompoundButton buttonView, boolean isChecked);
    }

이 인터페이스에 정의된 인터페이스가 이벤트 핸들러이다. 이벤트 핸들러 내부에 정의된 함수 onCheckedChanged(CompoundButton buttonView, boolean isChecked) 를 정의하여 체크된 상태에서 어떻게 행동할 것인지 구성하면 된다.

이제는 코틀린에서 해당 핸들러와 이벤트 소스를 연결하는 리스너를 정의하는 방법을 알아보자

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.checkbox.setOnCheckedChangeListener { buttonView, isChecked ->
            if (isChecked) {
                Log.d("info", "Checked")
            } else {
                Log.d("info", "Not Checked")
            }
        }
    }

binding.checkbox 이 이벤트 소스이며, 해당 view 에서 호출된 setOnCheckedChangeListener {buttonView, isChecked -> ...} 함수가 리스너로서 역할을 수행한다. 여기서는 SAM 기능을 이용하였다.

이상의 구조를 보면 알 수 있겠지만, Activity 자체에서 CompoundButton.OnCheckedChangeListener 를 구현하고, 관련된 함수를 override 하는 방법도 사용 가능하다. 혹은 아예 별도의 클래스를 만들어서 처리할 수도 있을 것이다.

class MainActivity : AppCompatActivity(), CompoundButton.OnCheckedChangeListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.checkbox.setOnCheckedChangeListener(this)
    }

    override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
        Log.d("info", "called")
    }
}

클릭과 롱클릭 이벤트 처리

안드로이드에는 다양한 View 가 존재하고, 각 View 마다 고유의 이벤트가 존재한다. 하지만 모든 View 이벤트는 처리 구조가 동일하기 때문에 이벤트를 처리하는데 어려움은 없을 것이다. 추가로 클릭, 롱클릭 이벤트 처리를 보자.

두 이벤트의 리스너의 구조는 다음과 같다. 각각 View 에 정의된 OnClickListener, OnLongClickListener 핸들러를 인자로 받게 되어있다.

open fun setOnClickListener(l: View.OnClickListener?): Unit
open fun setOnLongClickListener(l: View.OnLongClickListener?): Unit

각각 핸들러 인터페이스는 하나의 추상 메서드만 있으므로 SAM 을 충족한다.

public interface OnClickListener {
        void onClick(View v);
    }

public interface OnLongClickListener {
        boolean onLongClick(View v);
    }

이제 직접 리스너를 구현한 코드를 보자, LongClick 은 SAM 방식으로, click 은 그냥 인터페이스를 직접 구현하는 방식으로 구현하였다.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.myButton.setOnClickListener(View.OnClickListener { Log.d("info", "just tap") })
        binding.myButton.setOnLongClickListener {
            Log.d("info","Long tap")
            true
        }
    }
}

SAM (Single Abstract Method)

오로지 하나의 메서드만 정의되어 있는 인터페이스를 functional interface 혹은 SAM 인터페이스라고 부른다. 이들이 몇개의 구현된 메서드를 가지고 있든, 구현되지 않은 메서드를 하나만 가지고 있다면 조건이 충족된다.

Kotlin 에서는 다음과 같이 정의한다. interface 앞에 fun 키워드가 붙은게 특징이다.

fun interface IntPredicate {
   fun accept(i: Int): Boolean
}

sam 을 lambda 식으로 바꿔주는 기능을 사용하지 않는다면 직접 선언과 구현을 해줘야하는데,

val isEven = object : IntPredicate {
   override fun accept(i: Int): Boolean {
       return i % 2 == 0
   }
}

sam 에 대해서는 lambda 표현식으로 바꿔주기 때문에 다음과 같이 한줄로 처리가 가능하다.

val isEven = IntPredicate { it % 2 == 0 } // SAM lambda 표현식은 맨 마지막 줄을 return 문으로 인식하게끔 되어있다.

체크박스 핸들러를 정의할때로 해당 객체가 SAM 이기 때문에 lambda 표현식이 적용됐음을 알 수 있다.

profile
개발자

0개의 댓글