[ Android ] ViewTreeObserver - View 의 크기 구하기
1. 문제상황
View 의 크기를 구해서 특정 작업을 진행하려는데 계속 view의 크기가 0 이라는 답을 얻었다. 문제를 확인해보니 View가 그려지기 전에 그 크기에 접근했기 때문이라고 한다. 실제로 View 는 onResume 에서 그려지기 때문에 이를 위해서 ViewTreeObserver 를 사용했다.
2. ViewTreeObserver
이름 그대로 ViewTree에 대한 옵저버 역할을 하며 옵저버의 리스너를 등록하면 ViewTree 의 변화를 감지할 수 있다.
리스너로는 아래와 같은 리스너를 선택하여 변화를 감지 할 수 있다.

https://developer.android.com/reference/android/view/ViewTreeObserver.OnGlobalLayoutListener
ViewTreeObserver.OnGlobalLayoutListener | Android Developers
android.net.wifi.hotspot2.omadm
developer.android.com
3. 주의
ViewTreeObserver.XX 의 리스너를 생성하고 등록한뒤에는 반드시 remove 해주는 것 이 필요하다.
제거해주시 않으면 어떤 리스너를 생성하던 끊임 없이 호출되어 메모리를 낭비할 수 있다.
그리고 이때 OnDrawListener 를 채택하여 사용하였는데 draw 리스너는 리스너의 삭제를 오버라이딩 한 공간에서 할 수 없는지라 대체로 OnGlobalLayoutListener 를 선택 해서 사용했다.
이에 대한 정확한 이유는 찾지 못했다.....
removeOnXX 리스너 함수를 제공하고 있습니다.
4. 사용 예제
1. 리스너를 생성합니다.
val viewDrawingListener = object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
Log.d(TAG, binding.smileImageButton.width.toString())
binding.smileImageButton.layoutParams.height = binding.smileImageButton.width
binding.smileImageButton.requestLayout()
binding.smileImageButton.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
2. 리스너를 옵저버에 등록해줍니다.
binding.smileImageButton.viewTreeObserver.addOnGlobalLayoutListener (viewDrawingListener )
3. 전체 코드
package com.example.kotlin_cardgame
import android.app.ActionBar
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.example.kotlin_cardgame.databinding.ActivityMainBinding
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setImageButtonSize()
}
private fun setImageButtonSize() {
val viewDrawingListener = object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
Log.d(TAG, binding.smileImageButton.width.toString())
binding.smileImageButton.layoutParams.height = binding.smileImageButton.width
binding.smileImageButton.requestLayout()
binding.smileImageButton.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
binding.smileImageButton.viewTreeObserver.addOnGlobalLayoutListener (viewDrawingListener )
}
}