BaseFragment with viewbinding
To use viewbinding
in an android app I am basically creating base classes for Activity
& Fragment
to remove boilerplate of everytime writing inflating code.
ACTIVITY:
BaseActivity
with viewbinding
:
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = getViewBinding()
}
abstract fun getViewBinding(): VB
}
MainActivity
:
class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
//we can directly use binding now and it works fine inside activity
//binding.view.doSomething()
}
override fun getViewBinding(): ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)
}
FRAGMENTS :
BaseFragment
:
abstract class BaseFragment<VB : ViewBinding> : Fragment() {
var binding: VB? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = getViewBinding(view)
}
abstract fun getViewBinding(view: View): VB
}
DemoFragment
:
class DemoFragment : BaseFragment<DemoFragmentBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//problem is here
binding.txtData.text="Something"
}
override fun getViewBinding(view: View): DemoFragmentBinding = DemoFragmentBinding.bind(view)
}
demo_fragment.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.fragments.DemoFragment">
<TextView
android:id="@+id/txt_data"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello" />
</FrameLayout>
Problem : Unable to access views using binding inside Demofragment. I don't know why it works with activity and not with fragment.
2nd way that I don't want todo:
implementation 'androidx.fragment:fragment-ktx:1.3.1'
class DemoFragment : Fragment(R.layout.demo_fragment) {
lateinit var binding: DemoFragmentBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = DemoFragmentBinding.bind(view).apply {
txtData.text = "Hello World"
}
}
}
You need to override onCreateView
in BaseFragment and initialize the viewbinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = getViewBinding()
return binding.root
}
Then change this line
override fun getViewBinding(view: View): DemoFragmentBinding = DemoFragmentBinding.bind(view)
with
override fun getViewBinding() = DemoFragmentBinding.inflate(layoutInflater)
BaseFragment:
abstract class BaseFragment<VB : ViewBinding> : Fragment() {
private var _binding: VB? = null
val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = getViewBinding()
return binding.root
}
abstract fun getViewBinding(): VB
}
DemoFragment:
class DemoFragment : BaseFragment<DemoFragmentBinding>() {
override fun getViewBinding() = DemoFragmentBinding.inflate(layoutInflater)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.apply {
txtData.text = "Something"
}
}
}
Here is another way to implement this factory abstraction with ViewBinding
. I am sharing the implementation code below. I am using genrics
here. If anyone needs further explanation, I am here for that. Make sure you have enabled viewbinding
feature already into the build.gradle
file. Then use the following BaseFragment.kt
as your fragment abstraction.
BaseFragment:
typealias Inflate<T> = (LayoutInflater, ViewGroup?, Boolean) -> T
abstract class BaseFragment<V: ViewBinding>(
private val inflate: Inflate<V>
) : Fragment() {
private lateinit var _binding: V
val binding get() = _binding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = inflate(inflater, container, false)
return binding.root
}
}
N:B: Know more about typealias.
HomeFragment:
// Implement the BaseFragment like below
class HomeFragment : BaseFragment<FragmentHomeBinding>(FragmentHomeBinding::inflate) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// usages by calling public variable 'binding' from base class
binding.message.text = "update $value"
}
}