MVVM is current default pattern that created when we build new project in Android Studio. With MVVM we easily manage our service especialy for maintaining even though on the development we need to prepare more files.
In the development process, we often meet with a common view that will appear in some place. For a small project base, its OK if you create it manualy in each parent view, but for a huge scale project i think that is a bad practice. What is the solution? Yap, using a Custom View. Making a custom view is not a dificult case i think, but how to create a custom view that contain a view model for fetching data requirement?
Here we will learning together about how to Create a Custom View with ViewModel as Datasource, but i assump that you are already familiar with MVVM pattern
First
Let’s create a simple view for our custom view. Here we will make a view that will showing a data from your endpoint, so we just create a textview. Create a xml file in yoir project and named with layout_custom_view.xml
1 2 3 4 5 |
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text_user_name" android:layout_width="wrap_content" android:layout_height="wrap_content" /> |
Oke, so we just create a textview widget for an example and give the textview id with text_user_name that will showing user name from the endpoint.
Second
Create a custom class that will be used on parent view on next step. Here i give the file class name with CustomView.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
@AndroidEntryPoint class CustomView( context: Context, attrs: AttributeSet ) : LinearLayout(context, attrs) { // Declaring your viewModel first as a lazy variable private val viewModel by lazy { findViewTreeViewModelStoreOwner()?.let { ViewModelProvider(it)[YourViewModelClass::class.java] } } // Let's prepare for viewbinding for the view private var bind = LayoutCustomViewBinding.inflate(LayoutInflater.from(context)) init { // You can use this setting if you want your view width match parrent to the parrent view val view = bind.root view.layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ) addView(view) } override fun onAttachedToWindow() { super.onAttachedToWindow() // Setup listener for your view model findViewTreeLifecycleOwner()?.let { viewModel?.yourApiListener?.observe(it, ::setupView) } // Call your function in view model to fetch data here viewModel?.getUserName() } private fun setupView(result: YourResultModel) { // do your stuff here // I assump that you are already prepare the viewModel data val data = result.data // this is based on your data with(bind) { textUserName.text = data?.user_name // Exampling to set username to your view } } } |
I think in the code above is already clear with the comments that already added. So we define viewModel as a lazy variable and init view on init methode and change YourViewModelClass with your view model. Then we setup our listener on onAttachedToWindow and setup the view on function setupView that passing data from our view model.
Last Step
After we create layout and the custom view class, now we can use our custom view inside the parent view xml. For example is like that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> ... <CustomView android:layout_width="match_parent" android:layout_height="wrap_content"/> ... </LinearLayout> |
So we just including our custom view like code above. Simple isn’t it?
Thanks for reading this article. If you have another best practice, please add to the comments block.