Android

Working with MVVM Architecture Design Pattern and DataBinding

Pinterest LinkedIn Tumblr

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.

3ViewModel 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

MVVM Login Example 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.

Write A Comment