In Android, the MVP and MVVM architecture design patterns are very popular. In MVVM, most importantly, the children don’t have a direct reference to the parent, they only have the reference by observables.
If you’re unfamiliar with the DataBinding. Feel free to skip this tutorial and click here to read the DataBinding tutorial first. Why DataBinding? Because It enhances the MVVM architecture design pattern and makes it even better.
MVVM stands for Model-View-ViewModel design pattern. Challenges that developers face by writing the coding structure
- Writing Clean Code
- Flexible architecture
- Unit-Testing
- User-Experience
- Fast development
Software developers use MVVM design patterns to solve these problems.
1- The Model represents the data and the business logic. It consists of the business logic+local+remote data sources, model classes, and repositories.
2– The View consists of the UI (activity, fragment, adapter, etc). It sends the user action to the ViewModel but does not get the response back. For getting the response back, it has to subscribe to the observables which ViewModel exposes to it.
3– ViewModel is a bridge between view and model. ViewModel does not have a direct reference of View. ViewModel interacts with the model and exposes the observable that can be observed by the View.
Demo
1- Open the Android Studio, create a new project, and select a blank template
2- Enable the DataBinding feature and add the below dependencies in the project build.gradle.xml file. Add the below code and sync the Gradle.
plugins {
id 'com.android.application'
}
android {
compileSdk 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.mvvmexample"
minSdk 21
targetSdk 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildFeatures {
dataBinding true
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
Note: Change the application id if different.
3- Open the strings.xml file and add the below code
<resources>
<string name="app_name">MVVM Example</string>
<string name="str_login">Login</string>
<string name="hint_username">Enter username</string>
<string name="hint_password">Enter password</string>
</resources>
4- Add the below code in the colors.xml file.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<!-- App colors -->
<color name="color_bg">#121212</color>
<color name="bg_line">#00FFFF</color>
<color name="color_dark_black">#1D1D1D</color>
</resources>
5- Create the drawable resources files that will use in the design for EditText, Button, and background, etc.
Create a new drawable resource file called bg_edt_field.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp" />
<solid android:color="@color/white"/>
</shape>
Drawable resource file for draw the line called bg_line.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/bg_line"/>
</shape>
Background drawable resource file for Button called btn_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_dark_black"/>
<corners android:radius="50dp" />
</shape>
6- Create a new Java class called UserM.java. UserM.java class has two properties username and password. This class will bind with the activity_login.xml file using the variable tag. We already discussed this in the DataBinding tutorial.
package com.example.mvvmexample;
public class UserM {
private String userName;
private String pwd;
public UserM(String userName, String pwd) {
this.userName = userName;
this.pwd = pwd;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
7– Now create a new layout file called activity_login.xml. I used ConstraintLayout to design the Login screen. This will help, how you can create a design using ConstraintLayout and ConstraintHelper? Add the below code
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools">
<data>
<variable
name="user"
type="com.example.mvvmexample.LoginViewModal" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_bg">
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="24sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imgLogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:src="@drawable/app_logo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvTitle" />
<TextView
android:id="@+id/tvLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/str_login"
android:textColor="@color/white"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="@+id/leftGuideline"
app:layout_constraintTop_toBottomOf="@+id/imgLogo" />
<View
android:id="@+id/viewLine"
android:layout_width="0dp"
android:layout_height="5dp"
android:layout_marginTop="5dp"
android:background="@color/bg_line"
app:layout_constraintEnd_toEndOf="@+id/tvLogin"
app:layout_constraintStart_toStartOf="@+id/tvLogin"
app:layout_constraintTop_toBottomOf="@+id/tvLogin" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/inputUsername"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toEndOf="@+id/rightGuideline"
app:layout_constraintStart_toStartOf="@+id/leftGuideline"
app:layout_constraintTop_toBottomOf="@+id/viewLine">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/edtUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_edt_field"
android:hint="@string/hint_username"
android:text="@={user.userName}"
android:imeOptions="actionNext"
android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/inputPwd"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:passwordToggleEnabled="true"
app:layout_constraintEnd_toEndOf="@+id/rightGuideline"
app:layout_constraintStart_toStartOf="@+id/leftGuideline"
app:layout_constraintTop_toBottomOf="@+id/inputUsername">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/edtPwd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_edt_field"
android:hint="@string/hint_password"
android:text="@={user.password}"
android:imeOptions="actionDone"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnLogin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:background="@drawable/btn_bg"
android:text="@string/str_login"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="22sp"
android:onClick="@{()->user.doLogin()}"
bind:msgForDisplay="@{user.msgForDisplay}"
app:layout_constraintEnd_toEndOf="@+id/rightGuideline"
app:layout_constraintStart_toStartOf="@+id/leftGuideline"
app:layout_constraintTop_toBottomOf="@+id/inputPwd" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/leftGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="20dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/rightGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="20dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
8- Create a Java class called LoginViewModal.java and extends the BaseObservable class in it. Add the below code
package com.example.mvvmexample;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
public class LoginViewModal extends BaseObservable {
private String successMsg = "Login successfully done";
private String errorMsg = "Email or password is invalid";
private UserM userM;
LoginViewModal() {
userM = new UserM("","");
}
@Bindable
private String msgForDisplay = null;
public String getMsgForDisplay() {
return msgForDisplay;
}
public void setMsgForDisplay(String msgForDisplay) {
this.msgForDisplay = msgForDisplay;
notifyPropertyChanged(BR.msgForDisplay);
}
@Bindable
public void setUserName(String username) {
userM.setUserName(username);
notifyPropertyChanged(BR.userName);
}
@Bindable
public String getUserName() {
return userM.getUserName();
}
@Bindable
public void setPassword(String password) {
userM.setPwd(password);
notifyPropertyChanged(BR.password);
}
@Bindable
public String getPassword() {
return userM.getPwd();
}
public void doLogin() {
if (validateUserInfo())
setMsgForDisplay(successMsg);
else
setMsgForDisplay(errorMsg);
}
/**
* this method is just for validating the user input field info.
* You can change according to your logic and can return message according to your requirement
*
* @return
*/
private boolean validateUserInfo() {
if (userM.getUserName().trim().isEmpty())
return false;
if (userM.getUserName().trim().length() < 5)
return false;
if (userM.getPwd().trim().isEmpty())
return false;
return userM.getPwd().trim().length() >= 5;
}
}
Why I used BR and BaseObservable class?
BR class is generated by DataBinding for storing all reactive values to handle. Observable pattern I explained it in my second example of DataBinding tutorial. Observable class which is able to be observed by other classes. Observer classes react and notify if observe any changes in the Observable class.
Binding Adapters are using to set the custom values to the view. You set the attribute in XML using bind:customvaluename and DataBinding library will look for this binding adapter to set the property to your view. Íf the value of this property will change, it will reflect automatically to the views.
Make the below changes in the ActivityLogin.java class.
package com.example.mvvmexample;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.BindingAdapter;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.example.mvvmexample.databinding.ActivityLoginBinding;
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityLoginBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_login);
binding.setUser(new LoginViewModal()); // bind the LoginViewModal with XML using variable tag
}
@BindingAdapter({"msgForDisplay"}) // this is the static method which can use with xml generate attribute. bind:msgForDisplay like this
public static void displayMsg(View view, String msg){
if (msg != null)
Toast.makeText(view.getContext(), msg, Toast.LENGTH_SHORT).show();
}
}
Download Android Source Code
Thanks for reading the tutorial. Subscribe to my YouTube channel. Like and Share my Facebook page with your friends.