Android/Study

[Android] abstract class를 이용하여 ViewBinding을 쉽게 사용하기

Tenacity_Dev 2024. 2. 29. 15:43
728x90
지난 프로젝트에 대한 회상 겸 정리할 겸 작성을 해보았다. (물론 너무 오랜 시간이 지나긴했지만...)

 

프로젝트를 진행하였을 때 ViewBinding을 일일히 Activity마다 혹은 Fragment마다 설정하는 것이 너무나도 귀찮았다.

그래서 추상화 클래스를 이용하여 ViewBindingActivity 그리고 ViewBindingFragment을 개발하였다. 

결과적으로 효율적으로 ViewBinding을 사용할 수 있었으며, 문제가 생긴다면 위 2 클래스를 확인하면 되는 효율적임이 생겼다.

 

우선 ViewBinding이란

아래 포스팅 링크를 남기겠다.

https://superohinsung.tistory.com/239

 

[Android] ViewBinding(뷰 바인딩) 정리

ViewBinding(뷰 바인딩) 이란 레이아웃 XML 파일에 선언한 뷰 객체를 코드에서 쉽게 이용하는 방법이다. 레이아웃 XML 파일에 등록한 뷰는 findViewById() 함수로 얻어서 사용해야한다. 그런데 이러한 과

superohinsung.tistory.com

 

ViewBindingActivity

import android.os.Bundle
import android.view.LayoutInflater
import androidx.annotation.CallSuper
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding

abstract class ViewBindingActivity<VB : ViewBinding> : AppCompatActivity() {

    abstract val bindingInflater: (LayoutInflater) -> VB

    private var _binding: VB? = null
    protected val binding: VB get() = requireNotNull(_binding)

    @CallSuper
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        _binding = bindingInflater.invoke(layoutInflater)
        setContentView(binding.root)
    }

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}

 

ViewBindingFragment

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding

abstract class ViewBindingFragment<VB : ViewBinding> : Fragment() {

    protected abstract val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> VB

    private var _binding: VB? = null
    protected val binding: VB get() = requireNotNull(_binding)

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = bindingInflater.invoke(inflater, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

 

 

실제로 적용한 모습

@AndroidEntryPoint
class HomeActivity : ViewBindingActivity<ActivityHomeBinding>() {

    override val bindingInflater: (LayoutInflater) -> ActivityHomeBinding
        get() = ActivityHomeBinding::inflate

    private lateinit var appBarConfiguration: AppBarConfiguration

    private val viewModel: HomeViewModel by viewModels()

    companion object {
        fun getIntent(context: Context): Intent {
            return Intent(context, HomeActivity::class.java)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        WindowCompat.setDecorFitsSystemWindows(window, true)
        super.onCreate(savedInstanceState)

        setSupportActionBar(binding.toolbar)

        initNavigationView()
        initBottomNavigationView()
    }
// (생략)
}

 

class StudyFragment : ViewBindingFragment<FragmentPostBinding>() {

    override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentPostBinding
        get() = FragmentPostBinding::inflate

    private val viewModel: StudyViewModel by activityViewModels()

    private var launcher: ActivityResultLauncher<Intent>? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val extendedFab = requireActivity().findViewById<ExtendedFloatingActionButton>(R.id.fab)
        val adapter = PostAdapter(::onClickPost)

        initRecyclerView(adapter)
        initExtendedFloatingActionButton(extendedFab)

        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    updateUi(it, adapter)
                }
            }
        }

        launcher = registerForActivityResult(RefreshStateContract()) {
            if (it != null) {
                adapter.refresh()
                it.message?.let { message -> showSnackBar(message) }
            }
        }
    }
// (생략)
}
728x90