Chris.Koo
Developer 쿠의 개발이야기
Chris.Koo
전체 방문자
오늘
어제
  • 분류 전체보기 (17)
    • Python (2)
    • Android (5)
    • iOS (2)
    • 알고리즘 (0)
    • Git (1)
    • 생각 (1)
    • Web (4)
    • Blockchain (1)
    • 자동차 (1)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록

공지사항

인기 글

태그

  • NavigationLink
  • rootView
  • rxjava
  • feedelegation
  • kaikas
  • firebase
  • BaseActivity
  • rxbus
  • ios
  • imap_unordered
  • basefragment
  • Klaytn
  • Blockchain
  • Python
  • SSR
  • viewtoroot
  • react
  • Android
  • swiftUI
  • viewBinding
  • DynamicList
  • fetchedresults
  • multiprocessing
  • Kotlin
  • appversion
  • Klay
  • nextjs
  • isdetaillink
  • rxKotlin
  • RemoteConfig

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Chris.Koo

Developer 쿠의 개발이야기

Android

Activity / Fragment ViewBinding Boilerplate Class

2021. 12. 5. 03:39
현업에서 다양한 개발자와 명세를 맞추어 개발하는 것은 매우 중요하다. 파편화된 코드 스타일은 곧 가독성을 해치고, 버그가 발생하거나 기능을 추가할 때 자칫하면 개발 효율성을 크게 저하시킬 수 있다.

 

MVVM 패턴을 사용할때 다음은 ViewBinding을 강제하는 Activity, Fragment BoilerPlate Class이다. Generic을 활용하기 때문에 모든 하위 클래스는 이를 상속받아 보다 쉽게 ViewBinding을 강제한 코드를 빠르게 구현할 수 있다.

뷰 바인딩
https://developer.android.com/topic/libraries/view-binding?hl=ko
 

뷰 결합  |  Android 개발자  |  Android Developers

뷰 결합 뷰 결합 기능을 사용하면 뷰와 상호작용하는 코드를 쉽게 작성할 수 있습니다. 모듈에서 사용 설정된 뷰 결합은 모듈에 있는 각 XML 레이아웃 파일의 결합 클래스를 생성합니다. 바인딩

developer.android.com

BaseActivity

abstract class BaseBindActivity<V : ViewDataBinding>(
    @LayoutRes private val layoutResId: Int
) : AppCompatActivity() {

    protected lateinit var binding: V

    lateinit var toolbar: Toolbar
    protected abstract val layoutToolbarID: Int

    val realm: Realm = Realm.getDefaultInstance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, layoutResId)
        binding.lifecycleOwner = this

        if (layoutToolbarID != 0) {
            toolbar = findViewById(layoutToolbarID)
            setSupportActionBar(toolbar)

            supportActionBar?.setDisplayHomeAsUpEnabled(true)
            supportActionBar?.title = ""
        }

        setStatusBarTransparent()
        initLayoutAttributes()
    }

    abstract fun initLayoutAttributes()

    override fun onDestroy() {
        super.onDestroy()
        if (!realm.isClosed) realm.close()
    }
}

Realm 이나 RxJava 사용 시에도 boilerplate내에서 lifecycle을 관리하면 개발자가 실수할 확률도 줄어든다.

사용 시에는 다음과 같이 상속받아 사용한다.

class ExampleActivity : BaseBindActivity<ActivityExampleBinding>(R.layout.activity_example) {

    override val layoutToolbarID: Int
        get() = R.id.toolbar_center_title // 사용하지 않으면 0

    override fun initLayoutAttributes() {
        // 레이아웃 관련 초기화
    }
}

BaseFragment

Fragment는 ViewBindingHolder를 활용한다.

abstract class BaseBindFragment<V : ViewDataBinding>(
    @LayoutRes private val layoutResId: Int
) : Fragment(),
    ViewBindingHolder<V> by ViewBindingHolderImpl() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ) = initBinding(DataBindingUtil.inflate(inflater, layoutResId, null, false), this) {}

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

    protected abstract fun initLayoutAttributes()
}

ViewBindingHolder

interface ViewBindingHolder<T : ViewBinding> {

    val binding: T?

    fun initBinding(binding: T, fragment: Fragment, onBound: (T.() -> Unit)?): View

    fun requireBinding(block: (T.() -> Unit)? = null): T
}

class ViewBindingHolderImpl<T : ViewBinding> : ViewBindingHolder<T>, LifecycleObserver {

    override var binding: T? = null
    var lifecycle: Lifecycle? = null

    private lateinit var fragmentName: String

    override fun requireBinding(block: (T.() -> Unit)?) =
        binding?.apply { block?.invoke(this) } ?: throw IllegalStateException("Accessing binding outside of Fragment lifecycle: $fragmentName")

    override fun initBinding(binding: T, fragment: Fragment, onBound: (T.() -> Unit)?): View {
        this.binding = binding
        lifecycle = fragment.viewLifecycleOwner.lifecycle
        lifecycle?.addObserver(this)
        fragmentName = fragment::class.simpleName ?: "N/A"
        onBound?.invoke(binding)
        return binding.root
    }
}

마찬가지로 다음과 같이 상속받아 사용한다.

class ExampleFragment : BaseBindFragment<FragmentExampleBinding>(R.layout.fragment_example) {

    override fun initLayoutAttributes() {
        // 레이아웃 관련 초기화
    }

}

매우 간결하게 사용할 수 있다. 나머지는 부모 클래스가 다~ 알아서 해준다.

'Android' 카테고리의 다른 글

RxBus - Rxjava로 전역 callback으로 구현하기  (0) 2021.12.08
[Android] Firebase Remote Config 사용하기  (0) 2021.12.06
Google Admob 보상형 광고 적용 및 최적화 하기  (0) 2020.05.04
Google Admob 광고 사용하기  (0) 2020.05.04
    'Android' 카테고리의 다른 글
    • RxBus - Rxjava로 전역 callback으로 구현하기
    • [Android] Firebase Remote Config 사용하기
    • Google Admob 보상형 광고 적용 및 최적화 하기
    • Google Admob 광고 사용하기
    Chris.Koo
    Chris.Koo
    개발하며 얻은 지식과 방법론 및 일화에 대해 남기고자 만든 블로그 입니다

    티스토리툴바