[Android] ViewBinding 위임하기

uuranus·2024년 3월 25일
post-thumbnail

Delegation

프로퍼티를 세팅해주는 코드를 다른 클래스에게 위임하는 디자인 패턴

val delegate by Delegate()

이런 식으로 선언하고 delegate 변수를 위임 프로퍼티, Delegate() 객체를 위임 객체라고 부름

by 연산자

kotlin convention 중에 하나

by 뒤에 오는 위임 객체가 getValue(), setValue()를 구현하면 위임 객체로서의 기능을 할 수 있음

Convention

언어의 특정 기능을 특정 이름의 함수와 연결해주는 기능

ex. plus라는 이름의 메서드를 만들면 + 연산자를 plus라는 함수와 연결해줘서 a.plus(b)를 a+b로 사용할 수 있음

Binding 위임하기

Activity의 기능을 무엇인가?

  • view를 inflate하고 생명주기에 맞춰 사용자와 상호작용

여기서 view를 inflate하는 부분이 Activity마다 비슷하기 때문에 by 연산자로 만들어 수 있지 않을까 생각했다.

최종 코드

DataBinding for Activity

class DataBindingActivityDelegate<out T : ViewBinding>(
    @LayoutRes private val layoutRes: Int,
) {
    private var _binding: T? = null

    operator fun getValue(thisRef: AppCompatActivity, property: KProperty<*>): T {
        _binding = _binding ?: DataBindingUtil.setContentView(thisRef, layoutRes)
        return _binding!!
    }
}

사용할 때는

private val binding: ActivityRegisterBinding by DataBindingActivityDelegate<ActivityRegisterBinding>(
        R.layout.activity_register
    )

ViewBinding for Activity

class ViewBindingActivityDelegate<out T : ViewBinding>(
    private val inflater: (LayoutInflater) -> T,
) {
    private var _binding: T? = null

    operator fun getValue(thisRef: AppCompatActivity, property: KProperty<*>): T {
        _binding = _binding ?: inflater(thisRef.layoutInflater)
        return _binding!!
    }
}

사용할 때는

override val binding: ActivityDetailBinding by ViewBindingActivityDelegate {
        ActivityDetailBinding.inflate(it)
    }

Fragment

  • Fragment는 onDestroyView할 때 null로 세팅해주기 위해 setValue를 구현해야하는데 그러면 리턴값이 nullable 해져서 사용할 때 계속 null 체크를 해줘야 해서 backing property도 써야 한다.
  • 그럴거면 그냥 onCreateView에서 바로 생성해주는 게 더 나을 것 같지만 그래도 한 번 만들어 봤다.
class DataBindingFragmentDelegate<T : ViewBinding>(
    @LayoutRes private val layoutRes: Int,
) {
    private var _binding: T? = null

    operator fun getValue(thisRef: Fragment, property: KProperty<*>): T? {
        _binding = _binding ?: DataBindingUtil.inflate(
            thisRef.requireActivity().layoutInflater,
            layoutRes,
            thisRef.view?.parent as? ViewGroup,
            false
        )
        return _binding
    }
    
    operator fun setValue(thisRef: Fragment, property: KProperty<*>, value: T?) {
        _binding = value
    }
}

사용할 땐

class HomeFragment : GoBongFragment<FragmentHomeBinding, HomeViewModel>() {

    private var _binding: FragmentHomeBinding? by DataBindingFragmentDelegate(R.layout.fragment_home)
    override val binding: FragmentHomeBinding get() = _binding!!
    
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        return binding.root
    }
    
     override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
    
}
profile
Frontend Developer

0개의 댓글