Open In App

How to Build a Grocery Android App using MVVM and Room Database?

Last Updated : 27 Dec, 2021
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we are going to build a grocery application in android using android studio. Many times we forget to purchase things that we want to buy, after all, we can’t remember all the items, so with the help of this app, you can note down your grocery items that you are going to purchase, by doing this you can’t forget any items that you want to purchase. A sample image is given below to get an idea about what we are going to do in this article. Note that we are going to implement this project using the Kotlin language. 

Build a Grocery Android App

In this project, we are using MVVM (Model View ViewModel) for architectural patterns, Room for database, Coroutines and RecyclerView to display the list of items. Before jumping to the project let’s understand these terms.

MVVM (Model View ViewModel)

MVVM architecture in android is used to give structure to the project’s code and understand code easily. MVVM is an architectural design pattern in android. MVVM treat Activity classes and XML files as View. This design pattern completely separate UI from its logic. Here is an image to quickly understand MVVM.  

MVVM(Model View ViewModel)

After seeing this image if you don’t understand how it will work then don’t worry as we will write the code we understand it completely.

ROOM DataBase

Room persistence library is a database management library and it is used to store the data of apps like grocery item name, grocery item quantity, and grocery item price. Room is a cover layer on SQLite which helps to perform the operation on the database easily.

RecycleView

RecyclerView is a container and it is used to display the collection of data in a large amount of data set that can be scrolled very effectively by maintaining a limited number of views.

Coroutines

Coroutines are a lightweight thread, we use a coroutine to perform an operation on other threads, by this our main thread doesn’t block and our app doesn’t crash. 

Step by Step Implementation

Step 1: Create a New Project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Kotlin as the programming language.

Step 2: Before going to the coding section first you have to do some pre-task

Before going to the coding part first add these libraries in your gradle file and also apply the plugin as ‘kotlin-kapt’. To add these library go to Gradle Scripts > build.gradle(Module:app).

def room_version = “2.2.1”

   def lifecycle_version = “2.0.0”

   // Room and Architectural Components

   implementation “androidx.room:room-runtime:$room_version”

   implementation “androidx.legacy:legacy-support-v4:1.0.0”

   implementation ‘androidx.lifecycle:lifecycle-extensions:2.1.0’

   implementation ‘androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0’

   implementation “androidx.room:room-ktx:2.2.1”

   kapt “androidx.room:room-compiler:$room_version”

   // Coroutines

   implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0’

   implementation “org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0”

   // New Material Design

   implementation “com.google.android.material:material:1.0.0”

   // ViewModel

   implementation “androidx.lifecycle:lifecycle-extensions:$lifecycle_version”

   implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version”

   kapt “androidx.lifecycle:lifecycle-compiler:$lifecycle_version”

Below is the complete code for the build.gradle(:app) file.

Kotlin




apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
 
android {
    compileSdkVersion 29
    buildToolsVersion "30.0.3"
 
    defaultConfig {
        applicationId "com.example.grocerylist"
        minSdkVersion 16
        targetSdkVersion 29
        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 = 1.8
        targetCompatibility = 1.8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
}
 
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
 
    def room_version = "2.2.1"
    def lifecycle_version = "2.0.0"
 
    // Room and Architectural Components
    implementation "androidx.room:room-runtime:$room_version"
    implementation "androidx.legacy:legacy-support-v4:1.0.0"
    implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
    implementation "androidx.room:room-ktx:2.2.1"
    kapt "androidx.room:room-compiler:$room_version"
 
    // Coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0'
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
 
    // New Material Design
    implementation "com.google.android.material:material:1.0.0"
 
    // ViewModel
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
 
}


 
 Below is the code for the strings.xml file. Here we have added the necessary strings that we are going to use in our project. 

XML




<resources>
    <string name="app_name">Fresh Basket</string>
 
    <!-- TODO: Remove or change this placeholder text -->
    <string name="hello_blank_fragment">Hello blank fragment</string>
    <string name="itemName">Banana</string>
    <string name="itemQuantity">35</string>
    <string name="itemPrice">250Rs</string>
    <string name="totalCost">20</string>
    <string name="totalCostTitle">Total Cost</string>
    <string name="title">Add Items to your cart</string>
    <string name="etItem">Item</string>
    <string name="etQuantity">Quantity</string>
    <string name="etPrice">Price</string>
    <string name="save">Save</string>
    <string name="cancel">Cancel</string>
     
</resources>


 
Below is the code for the colors.xml file. Here we have added the necessary colors that we are going to use in our project. 

XML




<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#0AD042</color>
    <color name="colorPrimaryDark">#03551A</color>
    <color name="colorAccent">#03DAC5</color>
    <color name="black">#000000</color>
    <color name="white">#ffffff</color>
</resources>


 
Step 3: Implement room database

a) Entities class 

The entities class contains all the columns in the database and it should be annotated with @Entity(tablename = “Name of table”). Entity class is a data class. And @Column info annotation is used to enter column variable name and datatype. We will also add Primary Key for auto-increment. Go to app > java > com.example.application-name. Right-click on com.example.application-name go to new and create Kotlin file/class and name the file as GroceryEntities. See the code below to completely understand and implement.

Kotlin




package com.example.grocerylist.Database.Entity
 
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
 
// This is a data class which store data.
// Entities class create a table in database,
// in our database we will create three column
 
@Entity(tableName = "grocery_items")
 
data class GroceryItems(
 
    // create itemName variable to
    // store grocery items.
    @ColumnInfo(name = "itemName")
    var itemName: String,
 
    // create itemQuantity variable
    // to store grocery quantity.
    @ColumnInfo(name = "itemQuantity")
    var itemQuantity: Int,
 
    // create itemPrice variable to
    // store grocery price.
    @ColumnInfo(name = "itemPrice")
    var itemPrice: Int
) {
    // Primary key is a unique key
    // for different database.
    @PrimaryKey(autoGenerate = true)
    var id: Int? = null
}


 b) Dao Interface 

The Dao is an interface in which we create all the functions that we want to implement on the database. This interface also annotated with @Dao. Now we will create a function using suspend function which is a coroutines function. Here we create three functions, First is the insert function to insert items in the database and annotated with @Insert, Second is for deleting items from the database annotated with @Delete and Third is for getting all items annotated with @Query. Go to the app > java  > com.example.application-name . Right-click on com.example.application-name go to new and create Kotlin file/class and name the file as GroceryDao. See the code below to implement.

Kotlin




package com.example.grocerylist.Database
 
import androidx.lifecycle.LiveData
import androidx.room.*
import com.example.grocerylist.Database.Entity.GroceryItems
 
// This class is used to create
// function for database.
@Dao
interface GroceryDao {
 
    // Insert function is used to
    // insert data in database.
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(item: GroceryItems)
 
    // Delete function is used to
    // delete data in database.
    @Delete
    suspend fun delete(item: GroceryItems)
 
    // getAllGroceryItems function is used to get
    // all the data of database.
    @Query("SELECT * FROM grocery_items")
    fun getAllGroceryItems(): LiveData<List<GroceryItems>>
}


c) Database class

Database class annotated with @Database(entities = [Name of Entity class.class], version = 1) these entities are the entities array list all the data entities associating with the database and version shows the current version of the database. This database class inherits from the Room Database class. In GroceryDatabase class we will make an abstract method to get an instance of DAO and further use this method from the DAO instance to interact with the database. See the below code to implement. Go to the app > java > com.example.application-name. Right-click on com.example.application-name go to new and create Kotlin file/class as GroceryDatabase. See the code below to implement.

Kotlin




package com.example.grocerylist.Database
 
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.example.grocerylist.Database.Entity.GroceryItems
 
@Database(entities = [GroceryItems::class], version = 1)
abstract class GroceryDatabase : RoomDatabase() {
 
    abstract fun getGroceryDao(): GroceryDao
 
    companion object {
        @Volatile
        private var instance: GroceryDatabase? = null
        private val LOCK = Any()
 
        operator fun invoke(context: Context) = instance ?: synchronized(LOCK) {
            instance ?: createDatabase(context).also {
                instance = it
            }
        }
 
        private fun createDatabase(context: Context) =
            Room.databaseBuilder(context.applicationContext, GroceryDatabase::class.java, "GroceryDatabase.db").build()
    }
}


Step 4: Now we will implement the architectural structure in the app 

a) Repository class

The repository is one of the design structures. The repository class gives the data to the ViewModel class and then the ViewModel class uses that data for Views. The repository will choose the appropriate data locally or on the network. Here in our Grocery Repository class data fetch locally from the Room database. We will add constructor value by creating an instance of the database and stored in the db variable in the Grocery Repository class. Go to the app > java > com.example.application-name. Right-click on com.example.application-name go to new and create Kotlin file/class as GroceryRepository. See the code below to implement.

Kotlin




package com.example.grocerylist.Database
 
import com.example.grocerylist.Database.Entity.GroceryItems
 
class GroceryRepository(private val db: GroceryDatabase) {
 
    suspend fun insert(item: GroceryItems) = db.getGroceryDao().insert(item)
    suspend fun delete(item: GroceryItems) = db.getGroceryDao().delete(item)
 
    fun allGroceryItems() = db.getGroceryDao().getAllGroceryItems()
}


Go to app > java > com.example.application-name. Right-click on com.example.application-name go to new and create a new Package called UI and then right-click on UI package and create a Kotlin file/class. See the code below to implement.

b) ViewModel class 

ViewModel class used as an interface between View and Data. Grocery View Model class inherit from View Model class and we will pass constructor value by creating instance variable of Repository class and stored in repository variable. As we pass the constructor in View Model we have to create another class which is a Factory View Model class. Go to app > java > com.example.application-name > UI. Right-click on the UI package and create a Kotlin file/class and name the file as GroceryViewModel. See the below code.

Kotlin




package com.example.grocerylist.UI
 
import androidx.lifecycle.ViewModel
import com.example.grocerylist.Database.Entity.GroceryItems
import com.example.grocerylist.Database.GroceryRepository
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
 
class GroceryViewModel(private val repository: GroceryRepository) : ViewModel() {
 
    // In coroutines thread insert item in insert function.
    fun insert(item: GroceryItems) = GlobalScope.launch {
        repository.insert(item)
    }
 
    // In coroutines thread delete item in delete function.
    fun delete(item: GroceryItems) = GlobalScope.launch {
        repository.delete(item)
    }
 
    //Here we initialized allGroceryItems function with repository
    fun allGroceryItems() = repository.allGroceryItems()
 
}


 c) Factory ViewModel class  

We will inherit the Grocery ViewModel Factory class from ViewModelProvider.NewInstanceFactory and again pass constructor value by creating instance variable of Grocery Repository and return GroceryViewModel(repository). Go to the app > java > com.example.application-name > UI. Right-click on the UI package and create a Kotlin file/class name it GroceryViewModelFactory. See the below code to understand.

Kotlin




package com.example.grocerylist.UI
 
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.grocerylist.Database.GroceryRepository
 
class GroceryViewModelFactory(private val repository: GroceryRepository):ViewModelProvider.NewInstanceFactory() {
 
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return GroceryViewModel(repository) as T
    }
}


 
 Step 5: Now let’s jump into the UI part 

In the activity_main.xml file, we will add two ImageView, RecyclerView, and Button after clicking this button a DialogBox open and in that dialog box user can enter the item name, item quantity, and item price. Refer to the following code.

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:orientation="vertical"
    tools:context=".UI.MainActivity">
 
    <!-- To create a app bar with logo image. -->
    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/logo"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0" />
 
    <!-- In this image view we will add a title image -->
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="35dp"
        android:src="@drawable/title"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView2"
        app:layout_constraintVertical_bias="0.0" />
 
    <!-- Recycler View to display list -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvList"
        android:layout_width="match_parent"
        android:layout_height="470dp"
        android:background="@color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        app:layout_constraintVertical_bias="1.0">
         
    </androidx.recyclerview.widget.RecyclerView>
 
    <!-- This button is used to open dialog box in
         which user can enter grocery items -->
    <Button
        android:id="@+id/btnAdd"
        android:layout_width="60dp"
        android:layout_height="wrap_content"
        android:background="@drawable/button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.954"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        app:layout_constraintVertical_bias="0.969" />
     
</androidx.constraintlayout.widget.ConstraintLayout>


 Output of XML Code:

Step 6: Let’s implement RecyclerView. Now we will code the UI part of the row in the list. Go to app > res > layout. Right-click on layout, go to new, and then add a Layout Resource File and name it as groceryadapter. See the XML code of the groceryadapter.xml file.

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="125dp"
    android:background="@drawable/adapter1">
 
    <!-- To display item name in recycler view -->
    <TextView
        android:id="@+id/txtItemName"
        android:layout_width="0dp"
        android:layout_height="53dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginBottom="17dp"
        android:fontFamily="@font/rokkitt"
        android:text="@string/itemName"
        android:textColor="@color/white"
        android:textSize="35sp"
        app:layout_constraintBottom_toTopOf="@+id/txtTotalCostTitle"
        app:layout_constraintEnd_toStartOf="@+id/txtItemQuantity"
        app:layout_constraintStart_toEndOf="@+id/cbItemCheck"
        app:layout_constraintTop_toTopOf="parent" />
 
    <!-- To display item quantity -->
    <TextView
        android:id="@+id/txtItemQuantity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="34dp"
        android:layout_marginRight="34dp"
        android:layout_marginBottom="9dp"
        android:text="@string/itemQuantity"
        android:textColor="@color/white"
        android:textSize="25sp"
        app:layout_constraintBottom_toBottomOf="@+id/txtItemName"
        app:layout_constraintEnd_toStartOf="@+id/txtItemPrice"
        app:layout_constraintStart_toEndOf="@+id/txtItemName"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0" />
 
    <!-- To display item price -->
    <TextView
        android:id="@+id/txtItemPrice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="26dp"
        android:layout_marginEnd="22dp"
        android:layout_marginRight="22dp"
        android:layout_marginBottom="26dp"
        android:text="@string/itemPrice"
        android:textColor="@color/white"
        android:textSize="25sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@+id/txtItemTotalCost"
        app:layout_constraintEnd_toStartOf="@+id/ibDelete"
        app:layout_constraintStart_toEndOf="@+id/txtItemQuantity"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0" />
 
    <CheckBox
        android:id="@+id/cbItemCheck"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="29dp"
        android:layout_marginLeft="29dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:background="@color/white"
        android:shadowColor="@color/black"
        app:layout_constraintBaseline_toBaselineOf="@+id/txtItemName"
        app:layout_constraintEnd_toStartOf="@+id/txtItemName"
        app:layout_constraintStart_toStartOf="parent" />
 
    <!-- This button is used to delete grocery item  -->
    <ImageButton
        android:id="@+id/ibDelete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="26dp"
        android:layout_marginRight="26dp"
        android:background="@color/black"
        android:src="@drawable/ic_action_delete"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/txtItemPrice"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.257" />
 
    <!-- To display total cost of grocery items -->
    <TextView
        android:id="@+id/txtItemTotalCost"
        android:layout_width="100dp"
        android:layout_height="60dp"
        android:background="@drawable/adapter2"
        android:padding="8dp"
        android:paddingLeft="12dp"
        android:text="@string/totalCost"
        android:textSize="30sp"
        android:textStyle="bold"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/txtTotalCostTitle"
        app:layout_constraintTop_toTopOf="@+id/txtTotalCostTitle"
        app:layout_constraintVertical_bias="0.875" />
 
    <!-- This text view is used to add statement for total cost  -->
    <TextView
        android:id="@+id/txtTotalCostTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="77dp"
        android:layout_marginLeft="77dp"
        android:layout_marginEnd="149dp"
        android:layout_marginRight="149dp"
        android:layout_marginBottom="16dp"
        android:text="@string/totalCostTitle"
        android:textColor="@color/white"
        android:textSize="20dp"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/txtItemTotalCost"
        app:layout_constraintStart_toStartOf="parent" />
     
</androidx.constraintlayout.widget.ConstraintLayout>


 Output of XML Code:

 We will code adapter class for recycler view. In the Grocery Adapter class, we will add constructor value by storing entities class as a list in list variable and create an instance of the view model. In Grocery Adapter we will override three functions: onCreateViewHolder, getItemCount, and onbindViewHolder, we will also create an inner class called grocery view holder. Go to the app > java > com.example.application-name. Right-click on com.example.application-name go to new and create a new Package called Adapter and then right-click on Adapter package and create a Kotlin file/class name it GroceryAdapter. See the below code.

Kotlin




package com.example.grocerylist.Adapter
 
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.grocerylist.Database.Entity.GroceryItems
import com.example.grocerylist.R
import com.example.grocerylist.UI.GroceryViewModel
import kotlinx.android.synthetic.main.groceryadapter.view.*
 
class GroceryAdapter(var list: List<GroceryItems>, val viewModel: GroceryViewModel) :
    RecyclerView.Adapter<GroceryAdapter.GroceryViewHolder>() {
 
    // In this function we will add our groceryadapter.xml to kotlin class
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroceryViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.groceryadapter, parent, false)
        return GroceryViewHolder(view)
    }
 
    // This function is used to return total number of size of list.
    override fun getItemCount(): Int {
        return list.size
    }
 
    // In onBindViewHolder we will bind our itemViews with adapter
    override fun onBindViewHolder(holder: GroceryViewHolder, position: Int) {
        var currentPosition = list[position]
        holder.itemView.txtItemName.text = currentPosition.itemName
        holder.itemView.txtItemPrice.text = "${currentPosition.itemPrice}"
        holder.itemView.txtItemQuantity.text = "${currentPosition.itemQuantity}"
        holder.itemView.ibDelete.setOnClickListener {
            viewModel.delete(currentPosition)
        }
 
        // To get total cost
        if (position == list.size - 1) {
            var totalCost = 0
            for (i in 0 until list.size) {
                totalCost += list[i].itemPrice
            }
            holder.itemView.txtItemTotalCost.visibility = View.VISIBLE
            holder.itemView.txtTotalCostTitle.visibility = View.VISIBLE
            holder.itemView.txtItemTotalCost.text = "$totalCost"
        }
    }
    // Inner class for viewHolder
    inner class GroceryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}


 Step 7: To enter grocery item, quantity, and price from the user we have to create an interface. To implement this interface we will use DialogBox. First create UI of dialog box. In this dialog box we will add three edit text and two text view. Three edit text to enter grocery item name, quantity and price. Two text view one for save and other for cancel. After clicking the save text all data saved into the database and by clicking on the cancel text dialog box closes. Go to the app > res > layout. Right-click on layout, go to new and then add a Layout Resource File and name it as grocerydialog. See xml code of grocerydialog.xml file.

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="310dp"
    android:layout_height="250dp"
    android:background="@drawable/rectangle">
 
    <!-- To display title-->
    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_marginEnd="5dp"
        android:layout_marginRight="5dp"
        android:layout_marginBottom="26dp"
        android:text="@string/title"
        android:textColor="@color/black"
        android:textSize="30dp"
        app:layout_constraintBottom_toTopOf="@+id/linearLayout"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
    <!-- Linear Layout is used to give equal
         weight sum to edit text-->
    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="66dp"
        android:weightSum="3"
        app:layout_constraintBottom_toTopOf="@+id/tvSave"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvTitle">
 
        <!-- Edit Text is used to Enter Grocery
             Item Name by user-->
        <EditText
            android:id="@+id/etItemName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="0dp"
            android:layout_weight="1"
            android:hint="@string/etItem"
            android:textSize="30dp"
            app:layout_constraintBottom_toTopOf="@+id/tvCancel"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.069"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.661" />
 
        <!-- Edit Text is used to Enter Grocery
             Item Quantity by user-->
        <EditText
            android:id="@+id/etItemQuantity"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="0dp"
            android:layout_weight="1"
            android:hint="@string/etQuantity"
            android:inputType="number"
            android:textSize="30dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/etItemPrice"
            app:layout_constraintHorizontal_bias="0.461"
            app:layout_constraintStart_toEndOf="@+id/etItemName"
            app:layout_constraintTop_toBottomOf="@+id/tvTitle"
            app:layout_constraintVertical_bias="0.276" />
 
        <!-- Edit Text is used to Enter Grocery
             Item Price by user-->
        <EditText
            android:id="@+id/etItemPrice"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="0dp"
            android:layout_weight="1"
            android:hint="@string/etPrice"
            android:inputType="number"
            android:textSize="30dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.861"
            app:layout_constraintStart_toEndOf="@+id/etItemName"
            app:layout_constraintTop_toTopOf="parent" />
 
    </LinearLayout>
 
    <!-- Text view is used as save button to save
         all details in database by user-->
    <TextView
        android:id="@+id/tvSave"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="14dp"
        android:text="@string/save"
        android:textColor="@color/black"
        android:textSize="20dp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/tvCancel"
        app:layout_constraintTop_toBottomOf="@+id/linearLayout" />
 
    <!-- Text View is used to close dialog box-->
    <TextView
        android:id="@+id/tvCancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="28dp"
        android:layout_marginLeft="28dp"
        android:layout_marginEnd="154dp"
        android:layout_marginRight="154dp"
        android:layout_marginBottom="14dp"
        android:text="@string/cancel"
        android:textColor="@color/black"
        android:textSize="20dp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/tvSave"
        app:layout_constraintStart_toStartOf="parent" />
 
</androidx.constraintlayout.widget.ConstraintLayout>


 Output of XML Code:

To add a clicklistener on save text we have to create an interface first in which we create a function. Go to the app > java > com.example.application-name > UI. Right-click on the UI package and create a Kotlin file/class and create an interface name it as DialogListener.  See the code of the DialogListener.kt file.

Kotlin




package com.example.grocerylist.UI
 
import com.example.grocerylist.Database.Entity.GroceryItems
 
interface DialogListener {
   
    // Create a function to add items
      // in GroceryItems on clicking
    fun onAddButtonClicked(item:GroceryItems)
}


Now we will create grocery dialog class in which we save all input in different variable and then insert in database.  Go to the app > java > com.example.application-name > UI. Right-click on UI package and create a Kotlin file/class and create an class name it GroceryItemDialog. See the below code.

Kotlin




package com.example.grocerylist.UI
 
import android.content.Context
import android.os.Bundle
import android.view.Window
import android.widget.Toast
import androidx.appcompat.app.AppCompatDialog
import com.example.grocerylist.Database.Entity.GroceryItems
import com.example.grocerylist.R
import kotlinx.android.synthetic.main.grocerydialog.*
 
class GroceryItemDialog(context: Context, var dialogListener: DialogListener) : AppCompatDialog(context) {
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
        setContentView(R.layout.grocerydialog)
 
        // Click listener on Save button
        // to save all data.
        tvSave.setOnClickListener {
 
            // Take all three inputs in different variables from user
            // and add it in Grocery Items database
            val name = etItemName.text.toString()
            val quantity = etItemQuantity.text.toString().toInt()
            val price = etItemPrice.text.toString().toInt()
 
            // Toast to display enter items in edit text
            if (name.isEmpty()) {
                Toast.makeText(context, "Please Enter Item Name", Toast.LENGTH_SHORT).show()
            }
 
            val item = GroceryItems(name, quantity, price)
            dialogListener.onAddButtonClicked(item)
            dismiss()
        }
 
        // On click listener on cancel text to close dialog box
        tvCancel.setOnClickListener {
            cancel()
        }
    }
}


Step 8: In this step finally we will code in our MainActivity. In our main activity, we have to set up the recycler view and add click listener on add button to open the dialog box. Go to the MainActivity.kt file and refer to the following code. Below is the code for the MainActivity.kt file. Comments are added inside the code to understand the code in more detail.

Kotlin




package com.example.grocerylist.UI
 
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.grocerylist.Adapter.GroceryAdapter
import com.example.grocerylist.Database.Entity.GroceryItems
import com.example.grocerylist.Database.GroceryDatabase
import com.example.grocerylist.Database.GroceryRepository
import com.example.grocerylist.R
import kotlinx.android.synthetic.main.activity_main.*
 
class MainActivity : AppCompatActivity() {
     
    lateinit var ViewModel: GroceryViewModel
    lateinit var list: List<GroceryItems>
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
         
        val groceryRepository = GroceryRepository(GroceryDatabase(this))
        val factory = GroceryViewModelFactory(groceryRepository)
 
        // Initialised View Model
        ViewModel = ViewModelProvider(this, factory).get(GroceryViewModel::class.java)
        val groceryAdapter = GroceryAdapter(listOf(), ViewModel)
        rvList.layoutManager = LinearLayoutManager(this)
        rvList.adapter = groceryAdapter
         
        // To display all items in recycler view
        ViewModel.allGroceryItems().observe(this, Observer {
            groceryAdapter.list = it
            groceryAdapter.notifyDataSetChanged()
        })
         
        // on ClickListener on button to open dialog box
        btnAdd.setOnClickListener {
            GroceryItemDialog(this, object : DialogListener {
                override fun onAddButtonClicked(item: GroceryItems) {
                    ViewModel.insert(item)
                }
            }).show()
        }
    }
}


 
 This is how the complete project structure looks like.
 

Output:

 

Github Link: https://github.com/arpit-288/Fresh_Basket



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads