Developing a Counter in Android with Java

Index:

  1. About the Project
  2. Create an Empty Project
  3. Requirements & Resources
  4. Designing the User Interface
  5. Creating the logic

Prerequisites:

  1. Basics of Java Programming Language
  2. Basics of Object-Oriented Programming in Java
  3. Basic understanding of Android Studio (Optional)
  4. Basic understanding of the Android SDK (Optional)

About the Project

We will be developing a simple counter application in Android Studio using the Java Programming Language. A simple counter allows a user to increase, decrease and reset the counter. Here is a video example of what we will be building.

Create an Empty Project

Run Android Studio and on the Welcome Screen click on Start a new Android Studio Project

In the Choose your project section, select the Empty Activity template

Enter the following details in the Configure your project window

NameCounter
Package Namecom.example.counter
Save LocationYour Choice
LanguageJava
Minimum API levelAPI 15: Android 4.0.3 (IceCreamSandwich)

For the Save location option, you can click on the folder icon at the end in the Save location field to browse and select a location to save your project.

We are selecting the minimum API Level 15 (Ice Cream Sandwich) which covers approximately 100% devices running the Android Operating System. Click on the Finish button for the project to be created.

Let’s run the app by clicking on the green play icon in the top-right corner menu bar. There are two ways to run your application, either using the Android Virtual Device (Emulator) or with a Physical Device. Both options are preferable. When your app runs, you will see the Hello, World text centred in the screen which means your app successfully ran on the emulator/physical device. Now we have an empty project. Let’s understand the concerning files currently in the Project Tab.

In Android Studio, the Project Tab has files related to the Project we’ve currently created. An Activity in Android Studio is created by the following 3 things:

  1. A Layout file
  2. A Java Class
  3. Notifying the Android Manifest (AndroidManifest.xml) about the Activity.

The Project Tab is always located on the left bar in Android Studio. By default, Android Scope is always selected when a new project is created. The Android scope limits unnecessary files and displays the usable files to the developers. Let’s go through each file/folder and its usage:

app – The app folder contains multiple folders which are limited to a developer’s best usage because of the Android scope. If you wish to look at all the files then click on the drop-down icon next to Android and Select Project.

AndroidManifest.xml – Android Manifest is an XML file which contains data regarding the whole app. It contains data regarding packages, along with the components of the application such as activities, services, broadcast receivers, content providers etc. The AndroidManifest.xml file is placed in the manifests directory (app β†’ manifests β†’ AndroidManifest.xml).

MainActivity.java – The MainActivity.java is a Java file which contains logic which we will be discussing below. A Java class is required when creating activity as discussed above. Every Java file is placed in the java directory which comprises of packages (folders in Java) as the MainActivity.java file is placed in a package com.example.counter (app β†’ javaβ†’ com β†’ example β†’ counter β†’ MainActivity.java).

drawable – The drawable directory contains all the images related to the project. It is placed in the res (resources) directory. We will be importing images in the project which will help understand how we can import images in Android Studio.

layouts – The layout directory contains XML files as layout. We will dive deep into layouts later.

strings.xml – The strings.xml file helps us write down strings in XML format and reference them in our Java code.

build.gradle – The build.gradle file is a Gradle file. Gradle is an advanced build toolkit for Android which manages dependencies and allows to tinker and define custom build logic. We will be writing dependencies which will assist us in developing the Counter app. There are 2 build.gradle files, one for the whole project and the other one for the app only. All the dependencies are placed in the build.gradle (Module: app) which is located in the Gradle Scripts β†’ build.gradle (Module: app).

Let’s go through the MainActivity.java code which is generated by default when we created the project using the Empty Project Template:

package com.example.counter;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
package com.example.counter

Packages are folders in Java. The package keyword requires the path to the Java file separated with dots. The compiler understands when building the app in which directory/package to look for. So, com.example.counter can be understood easily as com/example/counter.

import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

The import keyword is used to import Java classes which have a usage in the current class. The import keyword looks for a class by writing down the path where the Java class is stored. As we can see, we have called the Bundle class from the android.os package and the AppCompatActivity class from the androidx.appcompat.app package. The amazing news is that Android Studio handles imports itself rather than writing them manually.

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Our current class MainActivity extends the AppCompatActivity class. The AppCompatActivity class has lifecycle methods on which an Activity executes and so many more functions which can be accessed by selecting Code in the top menu bar and choosing Override Methods. By default Android Studio generates the onCreate function which is a lifecycle function. Activity Lifecycle functions execute during runtime so we use them by adding our own instructions within them. The onCreate function takes a Bundle as a parameter, which is then called within the super.onCreate(savedInstanceState). The super keyword in Java is a reference variable which is used to refer immediate parent class object, which in easy words can be said as telling the onCreate function to perform what we it came for. The setContentView(R.layout.activity_main) is a pre-defined method within AppCompatActivity Java class which helps in setting the content view of the Activity. The setContentView() method takes in a layout resource id. To refer to a Resource in Android we use the R keyword and then the concerning directory and the file name. R.layout.activity_main goes in the res/layout/activity_main.xml and sets it as the default layout of the MainActivity.

Let’s move further by gathering all the requirements and resources for our counter application.

Requirements & Resources

As we are developing a simple counter application, we can write down the requirements into steps.

  • User can increase the counter value
  • User can decrease the counter value
  • User can reset the counter value

According to our requirements, we need 2 buttons, one for the increase event and the other for the decrease event. We will be using a Floating Action Button (fab) which is a spherical widget with an icon inside. It acts as a button which allows users to click and execute instructions. And for the reset counter value event, we will use a simple Button. For the increasing Floating Action Button, we can use a plus sign icon and for decreasing we can use a minus sign icon. Let’s import the images in our current Project by right-clicking on the res folder and selecting New β†’ Vector Asset.

In the Configure Vector Asset, press the Button next to Clip Art.

The Select icon window opens. Write add in the top left corner search bar and look for the icon with a plus, for example:

Select OK and when you return back to Configure Vector Asset window, change the name to ic_increase, then click on the bar next to Color. Move the brightness scale to the top which is a red arrow on the right vertical line with Black and White color and click Choose.

Press the Next button and then the Finish button to import the icon to the Drawable directory. Now, repeat the step and when at the Select icon window, write down remove and select the minus looking icon and change its name to ic_decrease. Both icons will be uploaded to the Drawable directory.

Let’s add some strings which we will be using later on when designing the user interface. Navigate to app β†’ res β†’ values β†’ strings.xml. Add the following code:

<resources>
    <string name="app_name">Counter</string>
    <string name="counter_value">0</string>
    <string name="below_zero_err">Value cannot decrease than 0</string>
    <string name="reset">RESET</string>
    <string name="already_at_zero">Already at Zero</string>
</resources>

In Android, we can either reference a string or hardcode it in the layout by directly adding it to the attribute value which is a very bad practice. Even Android Studio will throw a warning to store all the hardcoded string values in the strings.xml file. To reference a string in an XML file, we can write the @ sign and then strings/ and then the name of the string. The syntax for adding a string in strings.xml starts with the opening string tag and a required name attribute. The name attribute is used to refer the string in other places. After the opening tag the value of the String and then the closing tag. We have added the necessary strings we can use in developing our Counter application. Here is a table with a clear understanding of why these strings are added early:

String NameString ValueUsage
app_nameCounterName of the app
counter_value0The initial value of the counter
below_zero_errValue cannot decrease than 0Error text which will be displayed when a user tries to decrease value from 0
resetRESETText for the reset button
already_at_zeroAlready at ZeroError text which will be displayed when the counter value is 0 and a user tries to reset value again

One last thing that is left to ensure, that we have gathered all the resources is to add the dependency in the build.gradle file for accessing the Floating Action Button widget. Navigate to Gradle Scripts β†’ build.gradle (Module: app) and in the dependencies section add the following line:

implementation 'com.google.android.material:material:1.1.0'

We are adding the Material Library from Google in our project which will give us access to Floating Action Button and many more widgets and features.

Designing the User Interface

Navigate to app β†’ res β†’ layout and double click on activity_main.xml to open the layout file. Now there are two options of designing the layout. One is to use the tools in Design Panel and the other is to write the XML code manually in the Text Panel. Both the panels are available at the bottom alongside each other. We will be writing the XML code manually. Following code is generated by Android Studio when an Empty Project Template is used:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

XML stands for eXtensible Markup Language. It is used to describe data. The XML standard is a flexible way to create information formats and share structured data. XML is written using Tags. There can either be an Opening and Closing Tag or just a Self Closing Tag. XML also consists of Attributes which we will use to define data regarding a view. Here is a graphical representation to understand XML:

Let’s start designing the user interface and for more information on any attribute, copy the attribute/value and paste it on Google to understand the usage of it or refer to the documentation. Let’s note down views we require for our counter app:

ViewUsageTagIcon
Text ViewCounter Value<TextView />Not Required
Floating Action ButtonIncreasing the counter value<FloatingActionButton />Plus Sign
Floating Action ButtonDecreasing the counter value<FloatingActionButton />Minus Sign
ButtonFor Resetting the counter value<Button />Not Required

Here is the code after adding all the views from the table above:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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=".MainActivity">

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/increase_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="64dp"
        android:layout_marginRight="64dp"
        android:layout_marginBottom="32dp"
        android:clickable="true"
        android:focusable="true"
        android:onClick="IncreaseValue"
        app:backgroundTint="#01579B"
        app:elevation="1dp"
        app:fabSize="normal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:rippleColor="#FFFFFF"
        app:srcCompat="@drawable/ic_increase"
        app:useCompatPadding="true" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/decrease_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="64dp"
        android:layout_marginLeft="64dp"
        android:layout_marginBottom="32dp"
        android:clickable="true"
        android:focusable="true"
        android:onClick="DecreaseValue"
        app:backgroundTint="#880E4F"
        app:elevation="1dp"
        app:fabSize="normal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:rippleColor="#FFFFFF"
        app:srcCompat="@drawable/ic_decrease"
        app:useCompatPadding="true" />

    <TextView
        android:id="@+id/counting_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/counter_value"
        android:textAppearance="@style/TextAppearance.AppCompat.Display4"
        android:textColor="@android:color/black"
        android:textStyle="bold"
        app:fontFamily="monospace"
        app:layout_constraintBottom_toTopOf="@+id/increase_button"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/reset_button"
        style="@style/Widget.AppCompat.Button.Borderless"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="ResetValue"
        android:text="@string/reset"
        android:textColor="@android:color/darker_gray"
        app:layout_constraintBottom_toTopOf="@+id/decrease_button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/counting_value" />

</androidx.constraintlayout.widget.ConstraintLayout>

Let’s understand the XML code step by step:

<?xml version="1.0" encoding="utf-8"?>
This line sets the version of XML to 1.0 and encoding to utf-8.

Root Layout: <ConstraintLayout></ConstraintLayout>

The following table includes the attributes, their values and usage used within the Root Layout

AttributeValueUsage
xmlns:androidhttp://schemas.android.com/apk/res/androidNamespace which allows android attributes
xmlns:apphttp://schemas.android.com/apk/res-autNamespace which allows app attributes
xmlns:toolshttp://schemas.android.com/toolsNamespace which allows tools attributes
android:layout_widthmatch_parentSets the width to cover the view from left to right
android:layout_heightmatch_parentSets the height to cover the view from top to bottom
tools:context.MainActivityConnects the layout with the MainActivity java class.
Increase Button: <com.google.android.material.floatingactionbutton.FloatingActionButton />

The following table includes the attributes, their values and usage used within the Increase Button

AttributeValueUsage
android:id@id+/increase_buttonSets the id of the view through which it can either be connected to other views or referenced in the Context (MainActivity.java)
android:layout_widthwrap_contentWraps the view according to its content based on width
android:layout_heightwrap_contentWraps the view according to its content based on height
android:layout_marginEnd64dpAdds margin (space) at the end (right) side of the view based on the value provided. The attribute supports APIs >= 17
android:layout_marginRight64dpAdds margin (space) at the right side of the view based on the value provided. The attribute supports APIs < 17
android:layout_marginBottom32dpAdds margin (space) at the bottom of the view based on the value provided
android:clickabletrueDefines whether the view reacts to click events.
android:focusabletrueControls whether a view can take focus.
android:onClickIncreaseValueName of the method in this View’s context (MainActivity) to invoke when the view is clicked.
app:backgroundTint#01579BSets the background color of the view
app:elevation1dpDefine the shadow (base z depth) of the view
app:fabSizenormalSets the size of the FloatingActionButton (fab)
app:layout_constraintBottom_toBottomOfparentSet the view’s bottom constraint to the parent’s bottom side
app:layout_constraintEnd_toEndOfparentSet the view’s end (right) constraint to the parent’s end (right) side
app:rippleColor#FFFFFFSet the ripple’s color when the FloatingActionButton is pressed
app:srcCompat@drawable/ic_increaseSet the icon by accessing the drawable directory and finding the icon by its name
app:useCompatPaddingtrueSet whether FloatingActionButton should add inner padding to ensure consistent dimensions on all platforms. The attribute is limited to APIs >= 21
Decrease Button: <com.google.android.material.floatingactionbutton.FloatingActionButton />

The following table includes the attributes, their values and usage used within the Decrease Button

AttributeValueUsage
android:id@id+/decrease_buttonSets the id of the view through which it can either be connected to other views or referenced in the Context (MainActivity.java).
android:layout_widthwrap_contentWraps the view according to its content based on width
android:layout_heightwrap_contentWraps the view according to its content based on height
android:layout_marginStart64dpAdds margin (space) at the start (left) side of the view based on the value provided. The attribute supports APIs >= 17
android:layout_marginLeft64dpAdds margin (space) at the left side of the view based on the value provided. The attribute supports APIs < 17
android:layout_marginBottom32dpAdds margin (space) at the bottom of the view based on the value provided
android:clickabletrueDefines whether the view reacts to click events.
android:focusabletrueControls whether a view can take focus.
android:onClickDecreaseValueName of the method in this View’s context (MainActivity) to invoke when the view is clicked.
app:backgroundTint#880E4FSets the background color of the view
app:elevation1dpDefine the shadow (base z depth) of the view
app:fabSizenormalSets the size of the FloatingActionButton (fab)
app:layout_constraintBottom_toBottomOfparentSet the view’s bottom constraint to the parent’s bottom side
app:layout_constraintStart_toStartOfparentSet the view’s start (left) constraint to the parent’s start (left) side
app:rippleColor#FFFFFFSet the ripple’s color when the FloatingActionButton is pressed
app:srcCompat@drawable/ic_decreaseSet the icon by accessing the drawable directory and finding the icon by its name
app:useCompatPaddingtrueSet whether FloatingActionButton should add inner padding to ensure consistent dimensions on all platforms. The attribute is limited to APIs >= 21
Counter Text: <TextView />

The following table includes the attributes, their values and usage used within the Counter Text

AttributeValueUsage
android:id@+id/counting_valueSets the id of the view through which it can either be connected to other views or referenced in the Context (MainActivity.java).
android:layout_widthwrap_contentWraps the view according to its content based on width
android:layout_heightwrap_contentWraps the view according to its content based on height
android:text@string/counter_valueSets the value of the TextView by accessing the string.xml file and looking for the name counter_value
android:textAppreance@style/TextAppearance.AppCompat.Display4Sets the appearance of the text
android:textColor@android:color/blackSets the color of the text
android:textStyleboldSets the style of the text
app:fontfamilymonospaceSets the font family of the TextView
app:layout_constraintBottom_toTopOf@+id/increase_buttonSets the bottom constraint of the view to the provided view’s topside. Other views can be accessed by their id’s
app:layout_constraintLeft_toLeftOfparentSet the left constraint of the view to the parent’s left side
app:layout_constraintRight_toRightOfparentSet the right constraint of the view to the parent’s right side
app:layout_constraintTop_toTopOfparentSet the top constraint of the view to the parent’s topside
Reset Button: <Button />

The following table includes the attributes, their values and usage used within the Reset Button

AttributeValueUsage
android:id@+id/reset_buttonSets the id of the view through which it can either be connected to other views or referenced in the Context (MainActivity.java).
style@style/Widget.AppCompat.Button.BorderlessSets the style of the view. We are choosing Borderless Button which has no borders and background
android:layout_widthwrap_contentWraps the view according to its content based on width
android:layout_heightwrap_contentWraps the view according to its content based on height
android:onClickResetValueName of the method in this View’s context (MainActivity) to invoke when the view is clicked.
android:text@string/resetSets the value of the TextView by accessing the string.xml file and looking for the name reset
android:textColor@android:color/dark_graySets the text color. @android:color/dark_gray is a color stored in the android namespace.
app:layout_constraintBottom_toTopOf@+id/decrease_buttonSets the bottom constraint of the view to the other view’s topside. Other views can be accessed by their id’s
app:layout_constraintEnd_toEndOfparentSets the end (right) constraint of the view to the parent’s end (right) side
app:layout_constraintStart_toStartOfparentSets the start (left) constraint of the view to the parent’s start (left) side
app:layout_constraintTop_toBottomOf@+id/counting_valueSets the top constraint of the view to the provided view’s bottom side. Other views can be accessed by their id’s

Here is how your user interface should look by now:

After running the application on an emulator or a physical device the user interface must look the same as in the above image but when the buttons are pressed, nothing happens. The reason is very simple, there is no logic or instructions behind the buttons to execute. Let’s create the logic.

Creating the Logic

Navigate to app β†’ java β†’ com.example.counter β†’ MainActivity. For our ease, we will distribute the logic into steps:

  1. Declare an integer count which will hold the counter value
  2. Add reference of the view which will display the count value
  3. Set the count value to the referenced view
  4. Create an onClick handler for increase button
  5. Instructions for increasing counter value
    1. Increment count
    2. Step 3
  6. Create an onClick handler for decrease button
  7. Instructions for decreasing counter value
    • Check if count is greater than 0
      1. Decrement count
      2. Step 3
    • If count is not greater than 0
      1. Show a Toast to the user with concerning information
  8. Create an onClick handler for resetting the value
    • Check if count is not equal to 0
      1. Set count to 0
      2. Step 3
    • If count is equal to 0
      1. Show a Toast to the user with concerning information
Step #1: Declare an integer count which will hold the counter value

Declare an instance variable count with a value of 0 and Data Type of Integer. An instance variable is always placed outside the function and within a class. So, the count variable will be accessible to the whole MainActivity class only due to its private access modifier.

private int count = 0
Step #2: Add reference of the view which will display the count value

Declare another instance variable with a variable name counter and a Data Type of TextView without initializing a value.

private TextView counter;

We are going to reference the TextView from our layout using the id we set in the TextView (counting_value). In the onCreate() lifecycle method we are going to find the view by its id using the predefined method finViewById(@IntRes int id) in the AppCompatActivity class. The findViewById method takes an integer as an argument. We are going to pass the id of the TextView in the layout by calling the R.id.counting_value. After adding the argument, we will set the predefined method and the argument equal to the counter instance variable. This is how we reference views in Android Studio.

counter = findView(R.id.counting_value);
Step #3: Set the count value to the referenced view

As Step 3 is being repeated again and again, we will create a user-defined function for it. As we have both counter and count instance variables. First, we will create an empty function updateUI() with no parameters. Within the function, we will set the text of the counterusing setText() method to the value of the count which is an Integer. We are using the String class function String.format() which takes in a Locale, a format and Objects as arguments. We set the Locale to get the default locale, and then use %d format which is a very good way of Formatting Numeric Print Output and in the end the values we want to place.

private void updateUI() {
    counter.setText(String.format(Locale.getDefault(), "%d", count));
}

Now, in the onCreate() method after referencing the view, call the updateUI() function. So onCreate() becomes:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Add reference of the view which will display the count value
    counter = findViewById(R.id.counting_value);

    // Setting value of counter to counter view
    updateUI();
}
Step #4: Create an onClick handler for increase button

In our layout file for the MainActivity class activity_main.xml, we have a FloatingActionButton tag with an attribute onClick which has a value of IncreaseValue. The onClick attribute looks for a function called IncreaseValue in the context (MainActivity.java). Let’s write the function. To create an onClick handler we use the public access modifier and the return value as void and then the function name as written in the onClick attribute value (IncreaseView) in activity_main.xml, and at last, add a single View parameter.

private void IncreaseValue(View view) {
    // Add instructions for increasing counter value
}
Step #5: Instructions for increasing counter value

To increase the counter value, we add a double plus sign (++) after the instance variable count which stores the counter value or we can add 1 to the count and store it again in the count variable. Here are both ways it can be done:

count++;
// or
count = count + 1;

Both instructions do the same work. We prefer using the count++ because it’s short and we are only adding one to it. After incrementing the count, we call the updateUI() function which sets the incremented value to the counter TextView.

public void IncreaseValue(View view) {
        
    // Increment the count
    count++;
        
    // Set the text in the counter TextView
    updateUI();
}
Step #6: Create an onClick handler for decrease button

Repeat Step #4 where we create an onClick handler by writing another function but with a different name DecreaseValue.

private void DecreaseValue(View view) {
    // Add instructions for decreasing counter value
}
Step #7: Instructions for decreasing counter value

We will use an if-else conditional statement to check if the count variable is storing a value greater than 0 because we are not allowing negative numbers so we need to perform a check. If the count variable is storing a value greater than 0, it should decrement (count--) the counter and update the counter TextView and if the count variable is not storing a value greater than 0, we display a Toast to the user informing "Value cannot decrease than 0". Let’s write the if part first:

if (count > 0) {
    count--;
    updateUI();
}

Before writing the else part, let’s first create a user-defined function showToast() as we are going to use the Toast functionality multiple time. We create the showToast() which returns void and takes a string id which is an integer (int stringId) as a parameter. We call the Toast class instance method .makeText which takes in a context, a string value, and the duration of the toast to be displayed. For the context, we pass this, for the string value, we pass the stringId and for the duration, we access a constant from the Toast class LENGTH_SHORT which displays the toast for a short period of time. And at last, we use the show() method to display the Toast. Here is the code:

private void showToast(@StringRes int stringId) {
    Toast.makeText(this, stringId, Toast.LENGTH_SHORT).show();
}

We have used an annotation @StringRes which allows only String resources from the strings.xml. Now in the else part of the code, we use the showToast() function with R.string.below_zero_err as an argument. R.string.below_zero_err means to access the Resources and look for a String with the name below_zero_err. Here is how we can call the showToast function in the else part:

showToast(R.string.already_at_zero);

So, instructions for decreasing the value becomes:

public void DecreaseValue(View view) {
        
    // Checking if value is greater than 0
    if (count > 0) {
            
        // Decrement the count
        count--;

        // Setting value of counter to counter view
        updateUI();
    } else {
            
        // Display toast to user
        showToast(R.string.below_zero_err);
    }
        
}
Step #8: Create an onClick handler for resetting the value

Repeat Step #4 where we create an onClick handler by writing another function but with a different name ResetValue.

private void ResetValue(View view) {
    // Add instructions for resetting counter value
}

Let’s add instructions for resetting the counter value. First, we perform a check on the count variable. If the count variable stores a value which is not equal to 0, then we can perform to reset it or we should display a Toast "Already at Zero". If the count variable is not equal to 0, we set the value of count to 0 and then call the updateUI() function to set the count in the counter TextView. Here’s how:

public void ResetValue(View view) {
        
    // Check if count is not equal to 0
    if (count != 0) {
            
        // Set count to 0
        count = 0;

        // Setting value of counter to counter view 
        updateUI();
    } else {
            
        // Display toast to user
        showToast(R.string.already_at_zero);
    }
}

We have changed the argument value of showToast() to already_at_zero as it contains the concerning text we need to display in the Toast.

Here is a look at our MainActivity class after adding all the necessary steps:

package com.example.counter;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    private TextView counter;

    // Setting counter initial value to 0
    private int count = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Finding the counter value view
        counter = findViewById(R.id.counting_value);

        // Setting value of counter to counter view
        updateUI();
    }

    // Increase counter value
    public void IncreaseValue(View view) {
        // Increasing Value
        count++;
        updateUI();
    }

    // If value greater than 0, decrease counter value
    public void DecreaseValue(View view) {
        // Checking if value is greater than 0
        if (count > 0) {
            count--;
            updateUI();
        } else {
            showToast(R.string.below_zero_err);
        }
    }

    // Set counter value to 0 if value is not 0
    public void ResetValue(View view) {
        if (count != 0) {
            count = 0;
            updateUI();
        } else {
            showToast(R.string.already_at_zero);
        }
    }

    // Helper function for setting text to counter view
    private void updateUI() {
        counter.setText(String.format(Locale.getDefault(), "%d", count));
    }

    // Helper function for showing Toast
    private void showToast(@StringRes int stringId) {
        Toast.makeText(this, stringId, Toast.LENGTH_SHORT).show();
    }

}

Run the app by pressing the green play button in the top-right menu bar. After running the application, test it by increasing, decreasing and resetting the counter value.

Thank you for taking the time out to read this detailed article on how to Develop a Simple Counter Application. For better practices, run and test your code every time when you assign event handlers.

Link to GitHub repo for this project is available at http://bit.ly/cajgrac

Let us know in the comments if you were successful in developing and understanding the counter application. If you’re encountering errors, kindly let us know about them too in the comments.