refactor ViewBinding lazy initialization
This commit is contained in:
parent
9b85bcf562
commit
60d2b9d766
|
|
@ -1,13 +1,13 @@
|
|||
package ru.touchin.template.base.fragment
|
||||
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import ru.touchin.template.base.viewmodel.BaseController
|
||||
import ru.touchin.template.base.viewmodel.BaseViewModel
|
||||
import ru.touchin.template.di.SharedComponent
|
||||
import ru.touchin.template.di.getSharedModule
|
||||
import ru.touchin.template.navigation.backpress.OnBackPressedListener
|
||||
|
||||
abstract class BaseFragment<T : ViewModel> : Fragment(), OnBackPressedListener {
|
||||
abstract class BaseFragment<T : BaseViewModel>(@LayoutRes layoutId: Int) : Fragment(layoutId), OnBackPressedListener {
|
||||
|
||||
protected val viewModel: T by lazy { createViewModelLazy().value }
|
||||
|
||||
|
|
@ -18,6 +18,6 @@ abstract class BaseFragment<T : ViewModel> : Fragment(), OnBackPressedListener {
|
|||
protected fun getSharedComponent(): SharedComponent = getSharedModule()
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
return (viewModel as BaseController).onBackClicked()
|
||||
return viewModel.onBackClicked()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package ru.touchin.template.base.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
abstract class BaseViewModel : ViewModel(), BaseController
|
||||
|
|
@ -1,16 +1,13 @@
|
|||
package ru.touchin.template.di.auth
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import ru.touchin.template.feature.second.SecondRepository
|
||||
import ru.touchin.template.feature.second.SecondRepositoryImpl
|
||||
|
||||
@Module
|
||||
class AuthModule {
|
||||
|
||||
@Provides
|
||||
@AuthScope
|
||||
internal fun providesSecondRepository(): SecondRepository {
|
||||
return SecondRepositoryImpl()
|
||||
}
|
||||
// @Provides
|
||||
// @AuthScope
|
||||
// internal fun providesSecondRepository(): SecondRepository {
|
||||
// return SecondRepositoryImpl()
|
||||
// }
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import com.github.terrakok.cicerone.NavigatorHolder
|
|||
import com.github.terrakok.cicerone.Router
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Singleton
|
||||
import ru.touchin.template.di.AppScope
|
||||
|
||||
@Module
|
||||
class NavigationModule {
|
||||
|
|
@ -13,13 +13,13 @@ class NavigationModule {
|
|||
private val cicerone: Cicerone<Router> = Cicerone.create()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@AppScope
|
||||
fun provideRouter(): Router {
|
||||
return cicerone.router
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@AppScope
|
||||
fun provideNavigatorHolder(): NavigatorHolder {
|
||||
return cicerone.getNavigatorHolder()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,19 @@
|
|||
package ru.touchin.template.feature.first
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import ru.touchin.template.R
|
||||
import ru.touchin.template.base.fragment.BaseFragment
|
||||
import ru.touchin.template.databinding.FragmentFirstBinding
|
||||
import ru.touchin.template.utils.binding.viewBinding
|
||||
|
||||
class FirstFragment : BaseFragment<FirstViewModel>() {
|
||||
class FirstFragment : BaseFragment<FirstViewModel>(R.layout.fragment_first) {
|
||||
|
||||
private var _binding: FragmentFirstBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val binding by viewBinding { FragmentFirstBinding.bind(it) }
|
||||
|
||||
override fun createViewModelLazy() = viewModels<FirstViewModel> { viewModelFactory }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = FragmentFirstBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
|
@ -32,10 +26,4 @@ class FirstFragment : BaseFragment<FirstViewModel>() {
|
|||
textView.text = "First Fragment"
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,16 @@
|
|||
package ru.touchin.template.feature.first
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import javax.inject.Inject
|
||||
import ru.touchin.template.base.viewmodel.BaseViewModel
|
||||
import ru.touchin.template.navigation.Screens
|
||||
|
||||
class FirstViewModel @Inject constructor(
|
||||
private val router: Router
|
||||
) : ViewModel() {
|
||||
) : BaseViewModel() {
|
||||
|
||||
fun onNextButtonClicked(from: String) {
|
||||
router.navigateTo(Screens.Second(from))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
package ru.touchin.template.feature.second
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.touchin.template.R
|
||||
import ru.touchin.template.base.fragment.BaseFragment
|
||||
import ru.touchin.template.databinding.FragmentSecondBinding
|
||||
import ru.touchin.template.di.viewmodel.assistedViewModel
|
||||
import ru.touchin.template.utils.binding.viewBinding
|
||||
|
||||
class SecondFragment : BaseFragment<SecondViewModel>() {
|
||||
class SecondFragment : BaseFragment<SecondViewModel>(R.layout.fragment_second) {
|
||||
|
||||
companion object {
|
||||
private const val FROM_KEY = "FROM"
|
||||
|
|
@ -31,8 +31,7 @@ class SecondFragment : BaseFragment<SecondViewModel>() {
|
|||
}
|
||||
}
|
||||
|
||||
private var _binding: FragmentSecondBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val binding by viewBinding { FragmentSecondBinding.bind(it) }
|
||||
|
||||
private val secondViewModelFactory by lazy { getSharedComponent().secondScreenViewModelFactory() }
|
||||
|
||||
|
|
@ -43,11 +42,6 @@ class SecondFragment : BaseFragment<SecondViewModel>() {
|
|||
)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = FragmentSecondBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
|
@ -67,9 +61,4 @@ class SecondFragment : BaseFragment<SecondViewModel>() {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package ru.touchin.template.feature.second
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -8,13 +7,13 @@ import dagger.assisted.AssistedInject
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import ru.touchin.template.base.viewmodel.BaseController
|
||||
import ru.touchin.template.base.viewmodel.BaseViewModel
|
||||
|
||||
class SecondViewModel @AssistedInject constructor(
|
||||
@Assisted("from") from: String,
|
||||
@Assisted("screenName") screenName: String,
|
||||
private val rootRouter: Router
|
||||
) : ViewModel(), BaseController {
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val _state = MutableStateFlow("$from to $screenName")
|
||||
val state: StateFlow<String> = _state.asStateFlow()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package ru.touchin.template.utils.binding
|
||||
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
typealias ViewBindingFactory<T> = (View) -> T
|
||||
|
||||
class FragmentViewBindingDelegate<T : ViewBinding>(
|
||||
val fragment: Fragment,
|
||||
val viewBindingFactory: ViewBindingFactory<T>,
|
||||
) : ReadOnlyProperty<Fragment, T> {
|
||||
private var binding: T? = null
|
||||
|
||||
init {
|
||||
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
fragment.viewLifecycleOwnerLiveData.observe(fragment) { lifecylelOwner ->
|
||||
lifecylelOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
binding = null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
|
||||
val binding = this.binding
|
||||
|
||||
if (binding != null) return binding
|
||||
|
||||
val lifecycle = fragment.viewLifecycleOwner.lifecycle
|
||||
|
||||
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
|
||||
throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
|
||||
}
|
||||
|
||||
return viewBindingFactory(thisRef.requireView()).also { this.binding = it }
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: ViewBindingFactory<T>) =
|
||||
FragmentViewBindingDelegate(this, viewBindingFactory)
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/biometric_error_color">
|
||||
android:background="@color/colorPrimaryDark">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/activityScreensContainer"
|
||||
|
|
|
|||
Loading…
Reference in New Issue