[Android] ListView 클릭이 안돼요

핑구·2023년 4월 23일
0

Android

목록 보기
1/8
post-thumbnail

문제 상황

OnItemClickListener
adapterView가 클릭되었을 때 사용되어지는 인터페이스이다.
Single Abstract Method이기 때문에 람다로 바로 사용 가능하다.

listView.OnItemClickListener { 
	parent: AdapterView, view: View!, position: Int, id: Long ->
	val item = parent.getItemAtPosition(position) as Movie
	val intent = Intent(this, MovieDetail::class.java)
	intent.putExtra(MOVIE_KEY, item)
	startActivity(intent)
}

위와 같이 작성했다.
리스트뷰의 position번째에 있는 item을 클릭하면 해당 item을 intent로 다른 액티비티로 넘기는 작업이었다.
하지만 동작하지 않았다..
로그로 찍어봤는데 아예 이 함수 자체가 실행되지않는 상태였다.
(리스트뷰를 클릭해도 함수가 실행되지 않았다.)

  • listView가 가지는 view 중에 button이 존재한다.

해결 과정

검색해보니 두 가지를 찾아볼 수 있었다.

자식들이 focus를 못 갖게 하여 리스트뷰 자체를 클릭하게끔 만든다.

해당 리스트뷰가 아닌 그 안쪽에 있는 각각의 뷰들을 클릭하는 것으로 인식하고 있다.
android:descendantFocusability="blocksDescendants”
item 레이아웃의 루트에 위의 코드 추가하여 리스트뷰에 focus를 맞춘다.

descendantFocusability


focus를 가질 View를 찾을 때 ViewGroup과 그 자식들 사이의 관계를 정의한다.
1. afterDescendants: 자식들이 focus를 원하지 않을 때만 ViewGroup이 focus를 가진다. = 자식들이 먼저 focus를 가진다.
2. beforeDescendants: 자식들보다 먼저 focus를 가진다.
3. blocksDescendants: 자식들이 focus를 가지지 못하게 한다.

blocksDescendants를 주어 리스트뷰가 가진 여러 view들이 focus를 가지지 못하게 하여 리스트뷰 자체가 focus를 가지게 된다.

클릭 가능한 위젯이 있으면 리스트뷰가 클릭이 되지 않는다.

나의 경우 버튼이 있어서 되지 않았다.
android:focusable="false"
위의 코드를 버튼에 추가해주었더니 동작했다.

focus란?

두 가지 해결방법 모두 focus와 관련이 되어있어서 찾아보았다.
focusable의 속성이 true로 되어 있는 뷰가 사용자와 상호작용하게 된다면 해당 뷰가 focus를 가졌다고 한다.
예를 들어 EditText를 사용자가 클릭하여 글을 입력할 수 있게 되면 그 EditText가 focus를 가진다.

focus를 가질 수 있는 뷰가 여러개 있다면 가장 첫 번째 뷰가 focus를 갖게 된다.
EditText나 Button같이 디폴트로 focusable한 뷰도 있지만 그렇지 않은 뷰나 레이아웃에 focus를 주고 싶다면 임의로 코드를 작성해주어야 한다.
android:focusable="true" android:focusableInTouchMode="true" 를 추가해주면 focus를 줄 수 있다.
1. focusable: 말 그대로 focus를 가질 수 있냐 못가지냐의 속성을 의미하고
2. focusableInTouchMode: 사용자의 클릭으로 focus를 가질 수 있느냐를 의미한다.

어쨌튼 이와 같이 리스트뷰가 가진 하위 뷰들 중 focusable한 Button이 있었기 때문에 OnItemClickListener가 동작하지 않았던 것이다!

남겨진 문제

버튼을 클릭하면 액티비티를 띄우고 싶은데 리스트뷰 전체에 클릭이 들어가는 상황이다.
SetOnItemClickListener를 사용하려고 한 이유가 adapter 내부에서 모든 버튼마다 미리 클릭 이벤트를 등록해주고 싶지 않았기 때문이었다.
adapter 외부에서 클릭 이벤트를 처리해 주어 Event 감지 후 실행되는 콜백 등록하려 하였다.
하지만 애초에 이 인터페이스가 adapterView 전체에 반응하는 아이였기 때문에 내가 원하는대로 할 수 없었다.

해결

결국 크루의 도움을 받아 해결하게 되었다.
결론적으로 SetOnItemClickListener는 사용하지 않았다ㅎㅎㅎ
→ adapter 생성자에 리스너 혹은 함수를 넘겨주어 adapter 내부에서 클릭했을 시 해당 리스너(함수)를 사용하도록 한다.
매번 모든 버튼에 클릭 기능을 등록해주지 않고 클릭했을 때 해당 리스너(함수)를 통해 클릭 이벤트가 실행된다.

val movieAdapter = MovieAdapter(
            setMovieData(),
            object : MovieAdapter.OnClickItem {
                override fun onClick(movie: MovieUIModel) {
                    val intent = Intent(this@MovieListActivity, MovieDetailActivity::class.java)
                    intent.putExtra(MOVIE_KEY, movie)
                    startActivity(intent)
                }
            },
            ::clickAdvertisement
        )

참고
https://paulaner80.tistory.com/42
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=twotoedsloth&logNo=220015258451
https://satisfactoryplace.tistory.com/9

profile
발전중

0개의 댓글