How to make a Grocery List App?

How to make a Grocery List App?
How to make a Grocery List App?

A grocery list app has a very high throughput as it is downloaded by a very high number of consumers globally.

There are many pre-existing grocery list apps, but all of them don’t get worthy organic traffic due to the complicated user-interface. A broad age group operates such apps; therefore the user interface must be very preliminary and basic. Such apps are downloaded by the user for convenience, so we must not add too many security layers, as we know that security comes at the cost of convenience.

In our grocery list app, we are going to store data in an SQLite database. The objective of this app is to remind the user of his requirements. Hence, a serverless database is preferred as all the CRUD operations are carried out on the host device itself. SQLite comes in very handy for the developer as well as the user, as it a zero-configuration database and the memory occupancy is minimal; this implies there is no pre-required setup or administration needed. SQLite does not call for any segregated server for data manipulation or storage or any system to operate (serverless).

Phase 1: Designing of a Grocery List App

Keeping in mind that the app should be really very user-friendly, we create two screens only; Screen one which displays the list of all the elements present in the list and Screen two which is used for adding elements to the Grocery list.

The user-interface is very preliminary and easy to operate. As soon as the user enters the app, he/she can view the Grocery list. In case the user wants to add any element, he/she can click on the floating action button with the plus symbol, the button is painted in orange colour so that it is distinctly visible.

When the user enters the second screen, he/she can enter the name of the required quantity and click on the Save button. Post-clicking the user is navigated back to the home screen of the app. You can refer to the below-mentioned XML codes for creating the UI design.

XML Code 1: The Grocery List

For the recycler view, we use a constraint layout, whereas for the parent layout we use the co-ordinator layout. Be very particular, that you include the layout of the list item in your recycler view. This is going to be the template that your application will recycle for elongating the list.

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.android.example.roomwordssample.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <android.support.constraint.ConstraintLayout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorScreenBackground"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="com.android.example.roomwordssample.MainActivity"
        tools:showIn="@layout/activity_main">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            tools:listitem="@layout/recyclerview_item" />
    </android.support.constraint.ConstraintLayout>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@drawable/ic_add_black_24dp"
        app:backgroundTint="@color/colorAccent" />

</android.support.design.widget.CoordinatorLayout>
Recycler View showing the items fetched from the SQLite Database
Image Source: Android Emulator

XML Code 2: List Item

This code is used for designing the layout of each item on the list. You need to create only one template and automatically the recycler view can use it redundantly by setting the text value according to the position of the item in the SQLite database. The number of clones of this template is equal to the number of items in the corresponding column of the SQLite database.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <TextView
        android:id="@+id/textView"
        style="@style/text_view_style"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:elevation="8dp"
        tools:text="placeholder text" />
</LinearLayout>

XML Code 3: Add Item Screen

The design for this screen is very elementary and distinct. There are only two main components- an edit text and a button embraced within a linear layout.

<?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:background="@color/colorScreenBackground"
    android:orientation="vertical"
    android:padding="24dp">
    <EditText
        android:id="@+id/edit_word"
        style="@style/text_view_style"
        android:hint="@string/hint_word"
        android:inputType="textAutoComplete" />

    <Button
        android:id="@+id/button_save"
        style="@style/button_style"
        android:text="@string/button_save" />
</LinearLayout>
The second screen is used for adding new entries in the SQLite database.
Image Source: Android Emulator

FAQs in the above-mentioned UI Design

  1. What is the difference between the recycler view and the list view?

With the ingress of Android Lollipop, RecyclerView was announced officially. The RecyclerView is more extensive, flexible and customisable than the list view.

blog banner 1
  • ViewHolder Pattern: In a ListView, it was recommended to use the ViewHolder pattern but in the case of RecyclerView, it is mandatory to use the RecyclerView.ViewHolder class.
  • LayoutManager: In a ListView, the only type of view available, that is the vertical ListView. In contrast, a RecyclerView comes with LinearLayoutManager, StaggeredLayoutManager and GridLayoutManager.
  • Item Animator: The RecyclerView brings an entirely contemporary dimension to animations. Using the RecyclerView.ItemAnimator class, animating the views turns out to be simple and intuitive.
  • OnItemTouchListener: The RecyclerView is much more customisable than the ListView and provides many options to its developers.
  • Performance: RecyclerView has the advantage of preparing behind the visible entries, which is highly efficient if you intend to prepare bitmaps in the background. The obsolete ListView has no method for pre-calculating or caching the size of entries in the list, which creates complications when scrolling or performing layout.
Image showing the difference between a list view and a recycler view.
Image Source: Medium

2. What is the difference between the co-ordinator layout and the constraint layout?

A CoordinatorLayout is an extended FrameLayout, whereas a ConstraintLayout is an extended ViewGroup analogous to the RelativeLayout.

By default, if the developer added more than one child to a FrameLayout, they overlapped each other. To overcome this limitation of the FrameLayout, the CoordinatorLayout was introduced. The CoordinatorLayout is able to hold multiple children and it co-ordinates the animations and transitions of the compositae ViewGroups.

A ConstraintLayout is analogous to RelativeLayout, but it is way more flexible than RelativeLayout. ConstraintLayout gives a flat view hierarchy by eliminating nested view groups. It assists the developers in creating large and complex layouts efficiently. It is analogous to RelativeLayout as all components in the view group are aligned in coherence with the relationship among the sibling views and the parent layout. For implementing ConstraintLayout you don’t require other ViewGroups such as RelativeLayout, LinearLayout, or FrameLayout.

3. What is the advantage of using the elevation attribute in XML code?

With the advent of Material design, elevation for UI elements came into the picture. The Elevation property basically elevates the view on the z-axis to a dimension equal to the entered value. As elevation as first introduced in Android Lollipop, developers used shadow to give a similar effect in the previous versions.

The elevation any UI view is depicted by its Z property. Elevation affects the visual appearance of a shadow, that is, views with a greater Z value casts larger and softer shadows. Shadows are the virtue of the parent of the elevated view; therefore, they might be subjected to view clipping. The elevation is also employed for creating animations where widgets temporarily rise above the view plane to give a visual effect.

Image Source: developers.google

4. Why do we set the text input type as auto-complete by adding this code snippet-android:inputType=”textAutoComplete” ?

AutoCompleteTextView is an editable text view; it is unique as it displays a list of suggestions while the user is typing. For implementing auto-complete the developers have to declare the adaptor from which the text suggestions are to be fetched.

The developer can explicitly declare static array adaptors or link the auto-complete text view with any dynamic database adaptor. The suggestions are fetched from the corresponding adaptor and displayed in a drop-down menu just below the edit text. The user can pick an item from the drop-down menu and it will replace the contents of the edit text. If the user finds no relevant suggestions, he can dismiss the suggestions by pressing the back key.

Phase 2: Java code for the SQLite Database

You have to create a list of strings for storing words and then add them to the items column of the SQLite database created. After the item gets added to the database, it can be easily fetched and set to the recycler view adapter by the column number and the recycler view position. You can delete any one item of the list by sliding the item and you can clear the entire list by clicking on the “Clear all data” option from the menu.

Code 1: Creating the database

WordRoomDatabase class includes code to create the database. After the app creates the database, all further interactions with it happen through the WordViewModel.

package com.android.example.roomwordssample;

import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.room.Database;
import android.arch.persistence.room.Room;
import android.arch.persistence.room.RoomDatabase;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.NonNull;


@Database(entities = {Word.class}, version = 1, exportSchema = false)
public abstract class WordRoomDatabase extends RoomDatabase {

    public abstract WordDao wordDao();

    private static WordRoomDatabase INSTANCE;

    public static WordRoomDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (WordRoomDatabase.class) {
                if (INSTANCE == null) {
                    // Create database here
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            WordRoomDatabase.class, "word_database")
                            // Wipes and rebuilds instead of migrating if no Migration object.
                            // Migration is not part of this practical.
                            .fallbackToDestructiveMigration()
                            .addCallback(sRoomDatabaseCallback)
                            .build();

                }
            }
        }
        return INSTANCE;
    }

    // This callback is called when the database has opened.
    // In this case, use PopulateDbAsync to populate the database
    // with the initial data set if the database has no entries.
    private static RoomDatabase.Callback sRoomDatabaseCallback =
            new RoomDatabase.Callback(){

                @Override
                public void onOpen (@NonNull SupportSQLiteDatabase db){
                    super.onOpen(db);
                    new PopulateDbAsync(INSTANCE).execute();
                }
            };

    // Populate the database with the initial data set
    // only if the database has no entries.
    private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {

        private final WordDao mDao;

        // Initial data set
        private static String [] words = {"dolphin", "crocodile", "cobra", "elephant", "goldfish",
                "tiger", "snake"};

        PopulateDbAsync(WordRoomDatabase db) {
            mDao = db.wordDao();
        }

        @Override
        protected Void doInBackground(final Void... params) {
            // If we have no words, then create the initial list of words
            if (mDao.getAnyWord().length < 1) {
                for (int i = 0; i <= words.length - 1; i++) {
                    Word word = new Word(words[i]);
                    mDao.insert(word);
                }
            }
            return null;
        }
    }
}

Code 2: Interacting with the database

This class holds the implementation code for the methods that interact with the database. Using a repository allows us to group the implementation methods together, and allows the WordViewModel to be a clean interface between the rest of the app and the database.

For insert, update and delete, and longer-running queries, you must run the database interaction methods in the background. Typically, all you need to do to implement a database method is to call it on the data access object (DAO), in the background if applicable.

package com.android.example.roomwordssample;

import android.app.Application;
import android.arch.lifecycle.LiveData;
import android.os.AsyncTask;
import java.util.List;

public class WordRepository {

    private WordDao mWordDao;
    private LiveData<List<Word>> mAllWords;

    WordRepository(Application application) {
        WordRoomDatabase db = WordRoomDatabase.getDatabase(application);
        mWordDao = db.wordDao();
        mAllWords = mWordDao.getAllWords();
    }

    LiveData<List<Word>> getAllWords() {
        return mAllWords;
    }

    public void insert(Word word) {
        new insertAsyncTask(mWordDao).execute(word);
    }

    public void deleteAll() {
        new deleteAllWordsAsyncTask(mWordDao).execute();
    }

    // Need to run off main thread
    public void deleteWord(Word word) {
        new deleteWordAsyncTask(mWordDao).execute(word);
    }

    // Static inner classes below here to run database interactions
    // in the background.

    /**
     * Insert a word into the database.
     */
    private static class insertAsyncTask extends AsyncTask<Word, Void, Void> {

        private WordDao mAsyncTaskDao;

        insertAsyncTask(WordDao dao) {
            mAsyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(final Word... params) {
            mAsyncTaskDao.insert(params[0]);
            return null;
        }
    }

    /**
     * Delete all words from the database (does not delete the table)
     */
    private static class deleteAllWordsAsyncTask extends AsyncTask<Void, Void, Void> {
        private WordDao mAsyncTaskDao;

        deleteAllWordsAsyncTask(WordDao dao) {
            mAsyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            mAsyncTaskDao.deleteAll();
            return null;
        }
    }

    /**
     *  Delete a single word from the database.
     */
    private static class deleteWordAsyncTask extends AsyncTask<Word, Void, Void> {
        private WordDao mAsyncTaskDao;

        deleteWordAsyncTask(WordDao dao) {
            mAsyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(final Word... params) {
            mAsyncTaskDao.deleteWord(params[0]);
            return null;
        }
    }
}
You can clear all the items by selecting the option from menu.
Image Source: Android emulator

The basic structure of the app is very elementary; you just need to be very cautious while handling the database. In this project we have used the serverless SQLite database; if you want this app to be collaborative you can use SQL or firebase.

We have covered the delete single item and delete all items functionalities. You can extend this to delete selected items; this will assist you in exploring Android Studio and SQLite database. However, if you stumble across anything you can refer to my GitHub repository.

Now that you have learned to make a grocery list app, why don’t you learn to make a calculator.

By Vanshika Singolia