I have the current problem. Right now I'm trying to lazy inflate bunch of fragments for better performance, which are part of a ViewPager. The only solution I found was the usage of ViewStub. Here's where what I did so far:
abstract class ViewStubFragment: BaseFragment() {
private lateinit var binding: FragmentViewStubBinding
private var mSavedInstanceState: Bundle? = null
private var hasInflated = false
private var viewStub: ViewStub? = null
private var visible = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentViewStubBinding.inflate(inflater, container, false)
viewStub = binding.fragmentViewStub.viewStub
viewStub?.layoutResource = getViewStubLayoutResource()
mSavedInstanceState = savedInstanceState
if(visible && !hasInflated) {
val inflatedView = viewStub?.inflate()
if (inflatedView != null) {
onCreateViewAfterViewStubInflated(inflatedView, mSavedInstanceState)
}
}
return binding.root
}
protected abstract fun onCreateViewAfterViewStubInflated(
inflatedView: View,
savedInstanceState: Bundle?
)
@LayoutRes
protected abstract fun getViewStubLayoutResource(): Int
@CallSuper
protected fun afterViewStubInflated(originalViewContainerWithViewStub: View?) {
hasInflated = true
if (originalViewContainerWithViewStub != null) {
val progressBar = binding.progressBar
progressBar.visibility = View.GONE
}
}
override fun onResume() {
super.onResume()
visible = true
if (viewStub != null && !hasInflated) {
val inflatedView = viewStub!!.inflate()
onCreateViewAfterViewStubInflated(inflatedView, mSavedInstanceState)
afterViewStubInflated(view)
}
}
The corresponding layout:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progress_bar"
android:foregroundGravity="center"
android:layout_gravity="center"
android:indeterminate="true"
android:layout_width="48dp"
android:layout_height="48dp"/>
<ViewStub
android:id="@+id/fragment_view_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
</layout>
Part of the code from HomeFragment which I'm trying to lazily inflate:
class HomeFragment : ViewStubFragment() {
lateinit var binding: FragmentHomeBinding
........
override fun onCreateViewAfterViewStubInflated(
inflatedView: View,
savedInstanceState: Bundle?
) {
setDataBinding()
observeLiveData()
}
override fun getViewStubLayoutResource(): Int {
binding = FragmentHomeBinding.inflate(layoutInflater)
return binding.root.sourceLayoutResId
}
The problem is when setDataBinding()
is called, the binding for some reason is not applied, thus Views like RecyclerView are not showing on the screen or click events aren not working. I have searched for hours but couldn't find solution related to this issue.
After another hour of struggle I found a solution, not sure if it is the best possible.
if(visible && !hasInflated) {
inflatedViewBinding = binding.fragmentViewStub.binding
val inflatedView = viewStub?.inflate()
if (inflatedView != null) {
onCreateViewAfterViewStubInflated(inflatedView, mSavedInstanceState, inflatedViewBinding)
}
}
I passed the stored in ViewStubProxy
binding (associated with the inflated layout) as argument in onCreateViewAfterViewStubInflated()
and then assign the HomeFragment binding to it.
override fun onCreateViewAfterViewStubInflated(
inflatedView: View,
savedInstanceState: Bundle?,
binding: ViewDataBinding?
) {
this.binding = binding as FragmentHomeBinding
.......
}