How to Build a Simple Expense Calculator App in Android?
Last Updated :
29 Oct, 2021
Pre-requisites:
A simple expense calculator lets you add income and expenditures in a simplified manner. This is a glimpse of the application we are going to build. The application contains a single Activity with a RecyclerView, two EditTexts (one to enter amount and the other to enter a short note of the transaction), 1 clickable TextView to specify loss or gain, 1 clickable image to add the transaction to RecyclerView, and finally a custom ActionBar to show the balance. It includes Shared Preferences to store the data locally. A sample video 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 Java language.
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 Java as the programming language.
Step 2:
Before moving to the coding section let's add the necessary dependencies. The only dependency we have to add for the project is for Gson. It is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. Go to app-level build.gradle file and add the following dependency and click on sync now.
implementation 'com.google.code.gson:gson:2.8.6'
Here is a reference,
Step 3:
Let's add the necessary vector assets and drawable resource files. Go to app > res > drawable and add the following xml files.
ic_delete.xml (Delete Icon)
XML
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>
Preview:
ic_send.xml (Send Icon)
XML
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorPrimarySurface"
android:autoMirrored="true">
<path
android:fillColor="@color/white"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>
Preview:
ic_balance.xml (Wallet Icon)
XML
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M21,18v1c0,1.1 -0.9,2 -2,2L5,21c-1.11,0 -2,-0.9 -2,-2L3,5c0,-1.1 0.89,-2 2,-2h14c1.1,0 2,0.9 2,2v1h-9c-1.11,0 -2,0.9 -2,2v8c0,1.1 0.89,2 2,2h9zM12,16h10L22,8L12,8v8zM16,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
</vector>
Preview:
etbg.xml (Selector for Edit Text)
XML
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false">
<shape android:shape="rectangle">
<corners android:radius="3dp"/>
<stroke android:color="#DDD" android:width="2dp"/>
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="rectangle">
<corners android:radius="3dp"/>
<stroke android:color="@color/purple_500" android:width="2dp"/>
</shape>
</item>
</selector>
Here is a screenshot for reference.
Step 4:
Now let's add layout resource files for the custom ActionBar and RecyclerView row layout. Go to app > res > layout and add the following xml files. Below is the code for the custom_action_bar.xml file.
XML
<?xml version = "1.0" encoding = "utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/purple_500"
android:gravity="center_vertical"
android:paddingStart="5dp"
android:paddingTop="10dp"
android:paddingEnd="5dp"
android:paddingBottom="10dp">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="10dp"
android:text="Expense Calculator"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
app:srcCompat="@drawable/ic_balance" />
<TextView
android:id="@+id/tvBalance"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start"
android:text="0.00"
android:textColor="@color/white"
android:textSize="20sp" />
</LinearLayout>
Preview:
Below is the code for the transaction_row_layout.xml file. (RecyclerView Row Layout)
XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
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="wrap_content"
app:cardBackgroundColor="#000000"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="horizontal"
android:padding="5dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="6"
android:orientation="vertical">
<TextView
android:id="@+id/tvAmount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Amount"
android:textColor="@color/black"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Message"
android:textColor="@color/black" />
</LinearLayout>
<ImageView
android:id="@+id/ivDelete"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_gravity="center"
android:layout_weight="1"
android:clickable="true"
app:srcCompat="@drawable/ic_delete"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
</LinearLayout>
</androidx.cardview.widget.CardView>
Preview:
Here is a screenshot for reference.
Step 5:
We have added the necessary resource files for the application we are building. Now, Let's design the UI for our application. Add this xml file to app > res > layout. Below is the code for the activity_main.xml file.
XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvTransactions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_dodgeInsetEdges="bottom">
</androidx.recyclerview.widget.RecyclerView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:cardBackgroundColor="#DDD"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="false"
app:layout_dodgeInsetEdges="bottom">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="1dp"
android:background="#FFFFFF"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="@+id/tvSign"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:clickable="true"
android:gravity="center"
android:text="+₹"
android:textColor="#00c853"
android:textSize="24sp"
tools:ignore="TouchTargetSizeCheck" />
<EditText
android:id="@+id/etAmount"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="3"
android:background="@drawable/etbg"
android:ems="10"
android:hint="Amount"
android:inputType="number"
android:maxLength="7"
android:padding="5dp"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
<EditText
android:id="@+id/etMessage"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:layout_weight="7"
android:background="@drawable/etbg"
android:ems="10"
android:hint="Message"
android:inputType="textPersonName"
android:maxLength="50"
android:maxLines="1"
android:padding="5dp"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
<ImageView
android:id="@+id/ivSend"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:clickable="true"
app:srcCompat="@drawable/ic_send"
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/tvEmpty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="No transactions found!"
android:textColor="@color/black"
android:textSize="20sp" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Preview:
Step 6:
We have added every resource file required for this application. Now let's jump into the coding part. Let's first create the Model Class for each transaction to be updated in the RecyclerView. It should contain four members as follows.
- Amount (of type Integer)
- Message (a short note about the Transaction)
- Positive (a boolean variable to check if the amount is received ( Positive = true ) or spent (Positive = False)
Here is the Java code for the Model Class along with the corresponding constructor, getters, and setters. Below is the code for the TransactionClass.java file. (Model Class)
Java
package com.cs.expensecalculator;
public class TransactionClass {
private int amount;
private String message;
private boolean positive;
public TransactionClass(int amount, String message, boolean positive) {
this.amount = amount;
this.message = message;
this.positive = positive;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public boolean isPositive() {
return positive;
}
public void setPositive(boolean positive) {
this.positive = positive;
}
}
Step 7:
Now it's time to create an Adapter for this Model Class. Let's create a Class named TransactionAdapter that extends RecyclerView Adapter, and implement the necessary methods. If you're new to RecyclerView, check out this article before proceeding. Below is the code for the TransactionAdapter.java file. Comments are added inside the code to understand the code in more detail.
Java
package com.cs.expensecalculator;
import static com.cs.expensecalculator.MainActivity.calculateBalance;
import static com.cs.expensecalculator.MainActivity.checkIfEmpty;
import static com.cs.expensecalculator.MainActivity.setBalance;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.TViewHolder> {
Context ctx;
// List containing data for recyclerview
ArrayList<TransactionClass> transactionList;
// Constructor for TransactionAdapter
public TransactionAdapter(Context ctx, ArrayList<TransactionClass> transactionList) {
this.ctx = ctx;
this.transactionList = transactionList;
}
// On Create View Holder to Inflate transaction row layout
@NonNull
@Override
public TransactionAdapter.TViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(ctx).inflate(R.layout.transaction_row_layout,parent,false);
return new TransactionAdapter.TViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull TransactionAdapter.TViewHolder holder, int position) {
// Setting Message to a TextView in Row Layout
holder.tvMessage.setText(transactionList.get(holder.getAdapterPosition()).getMessage());
// If the transaction is Positive (Received Money) set Text Color to Green
if(transactionList.get(holder.getAdapterPosition()).isPositive())
{
holder.tvAmount.setTextColor(Color.parseColor("#00c853"));
// Setting Amount to a TextView in the row layout
holder.tvAmount.setText("+₹"+Integer.toString(transactionList.get(holder.getAdapterPosition()).getAmount()));
}
// If the transaction is Negative (Spent Money) set Text Color to Red
else {
holder.tvAmount.setTextColor(Color.parseColor("#F44336"));
// Setting Amount to a TextView in the row layout
holder.tvAmount.setText("-₹"+Integer.toString(transactionList.get(holder.getAdapterPosition()).getAmount()));
}
// On Click Listener for Delete Icon
holder.ivDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Confirmation Alert to delete a Transaction
AlertDialog dialog = new AlertDialog.Builder(ctx)
.setCancelable(false)
.setTitle("Are you sure? The transaction will be deleted.")
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
transactionList.remove(holder.getAdapterPosition());
dialogInterface.dismiss();
notifyDataSetChanged();
checkIfEmpty(getItemCount());
setBalance(transactionList);
}
})
.create();
dialog.show();
}
});
}
// To get size of the list
@Override
public int getItemCount() {
return transactionList.size();
}
// View Holder for a Transaction
public static class TViewHolder extends RecyclerView.ViewHolder{
TextView tvAmount,tvMessage;
ImageView ivDelete;
public TViewHolder(@NonNull View itemView) {
super(itemView);
tvAmount = itemView.findViewById(R.id.tvAmount);
tvMessage = itemView.findViewById(R.id.tvMessage);
ivDelete = itemView.findViewById(R.id.ivDelete);
}
}
}
Step 8:
We have defined the Model Class and the associated Adapter Class for Recycler View. Now it's time to initialize everything in MainActivity, and implement functions to set custom action bar, set balance, and store and retrieve data locally using shared preferences in activity's onStop() and onCreate() methods respectively. Here is the complete code for MainActivity. Below is the code for the MainActivity.java file. Comments are added inside the code to understand the code in more detail.
Java
package com.cs.expensecalculator;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
TextView tvSign;
public static TextView tvEmpty, tvBalance;
EditText etAmount, etMessage;
ImageView ivSend;
boolean positive = true;
RecyclerView rvTransactions;
TransactionAdapter adapter;
ArrayList<TransactionClass> transactionList;
// On create method
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Function to initialize views
initViews();
// Function to load data from shared preferences
loadData();
// Function to set custom action bar
setCustomActionBar();
// To check if there is no transaction
checkIfEmpty(transactionList.size());
// Initializing recycler view
rvTransactions.setHasFixedSize(true);
rvTransactions.setLayoutManager(new LinearLayoutManager(this));
adapter = new TransactionAdapter(this,transactionList);
rvTransactions.setAdapter(adapter);
// On click sign change
tvSign.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
changeSign();
}
});
// On click Send
ivSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Input Validation
if(etAmount.getText().toString().trim().isEmpty())
{
etAmount.setError("Enter Amount!");
return;
}
if(etMessage.getText().toString().isEmpty())
{
etMessage.setError("Enter a message!");
return;
}
try {
int amt = Integer.parseInt(etAmount.getText().toString().trim());
// Adding Transaction to recycler View
sendTransaction(amt,etMessage.getText().toString().trim(),positive);
checkIfEmpty(transactionList.size());
// To update Balance
setBalance(transactionList);
etAmount.setText("");
etMessage.setText("");
}
catch (Exception e){
etAmount.setError("Amount should be integer greater than zero!");
}
}
});
}
// To set custom action bar
private void setCustomActionBar() {
this.getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
getSupportActionBar().setDisplayShowCustomEnabled(true);
View v = LayoutInflater.from(this).inflate(R.layout.custom_action_bar,null);
// TextView to show Balance
tvBalance = v.findViewById(R.id.tvBalance);
// Setting balance
setBalance(transactionList);
getSupportActionBar().setCustomView(v);
getSupportActionBar().setElevation(0);
}
// To set Balance along with sign (spent(-) or received(+))
public static void setBalance(ArrayList<TransactionClass> transactionList){
int bal = calculateBalance(transactionList);
if(bal<0)
{
tvBalance.setText("- ₹"+calculateBalance(transactionList)*-1);
}
else {
tvBalance.setText("+ ₹"+calculateBalance(transactionList));
}
}
// To load data from shared preference
private void loadData() {
SharedPreferences pref = getSharedPreferences("com.cs.ec",MODE_PRIVATE);
Gson gson = new Gson();
String json = pref.getString("transactions",null);
Type type = new TypeToken<ArrayList<TransactionClass>>(){}.getType();
if(json!=null)
{
transactionList=gson.fromJson(json,type);
}
}
// To add transaction
private void sendTransaction(int amt,String msg, boolean positive) {
transactionList.add(new TransactionClass(amt,msg,positive));
adapter.notifyDataSetChanged();
rvTransactions.smoothScrollToPosition(transactionList.size()-1);
}
// Function to change sign
private void changeSign() {
if(positive)
{
tvSign.setText("-₹");
tvSign.setTextColor(Color.parseColor("#F44336"));
positive = false;
}
else {
tvSign.setText("+₹");
tvSign.setTextColor(Color.parseColor("#00c853"));
positive = true;
}
}
// To check if transaction list is empty
public static void checkIfEmpty(int size) {
if (size == 0)
{
MainActivity.tvEmpty.setVisibility(View.VISIBLE);
}
else {
MainActivity.tvEmpty.setVisibility(View.GONE);
}
}
// To Calculate Balance by iterating through all transactions
public static int calculateBalance(ArrayList<TransactionClass> transactionList)
{
int bal = 0;
for(TransactionClass transaction : transactionList)
{
if(transaction.isPositive())
{
bal+=transaction.getAmount();
}
else {
bal-=transaction.getAmount();
}
}
return bal;
}
// Initializing Views
private void initViews() {
transactionList = new ArrayList<TransactionClass>();
tvSign = findViewById(R.id.tvSign);
rvTransactions = findViewById(R.id.rvTransactions);
etAmount = findViewById(R.id.etAmount);
etMessage = findViewById(R.id.etMessage);
ivSend = findViewById(R.id.ivSend);
tvEmpty = findViewById(R.id.tvEmpty);
}
// Storing data locally
// using shared preferences
// in onStop() method
@Override
protected void onStop() {
super.onStop();
SharedPreferences.Editor editor = getSharedPreferences("com.cs.ec",MODE_PRIVATE).edit();
Gson gson = new Gson();
String json = gson.toJson(transactionList);
editor.putString("transactions",json);
editor.apply();
}
}
That's it. Now we can run the application. Make sure that your project contains all the following files.
Here is the preview of the final application.
Output:
Similar Reads
How to build a simple Calculator app using Android Studio?
Create a simple calculator which can perform basic arithmetic operations like addition, subtraction, multiplication, or division depending upon the user input. A sample video 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
6 min read
How to Build Age Calculator in Android Studio?
Hello geeks, today we are going to make an application to calculate Age or time period between two dates. By making this application one can calculate his/her exact age, also one can calculate the exact difference between two dates. Prerequisites: Before making this application, you can go through t
6 min read
How to Build a Simple e-Crackers App using Android?
Pre-requisites: Android App Development Fundamentals for BeginnersGuide to Install and Set up Android StudioHow to Create/Start a New Project in Android Studio?Running your first Android appHow to add Lottie Animation in an Android AppMediaPlayer Class in Android In this article, we are going to bui
12 min read
How to Build a Simple Notes App in Android?
Notes app is used for making short text notes, updating when you need them, and trashing when you are done. It can be used for various functions as you can add your to-do list in this app, some important notes for future reference, etc. The app is very useful in some cases like when you want quick a
9 min read
How to Build a Temperature Converter App in Android?
Temperature Converter Application is built using android studio where temperature entered in Celsius can be converted to Fahrenheit. The output will be displayed on Snackbar. A sample video is given below to get an idea about what we are going to do in this article. Note that we are going to impleme
3 min read
How to Build a Simple Reflex Game in Android?
A reflex game is a simple fun game that measures your responding speed. It is quite simple to make and understand. We will be designing a reflex game that will calculate your responding speed. The rules are simple just press the stop button when you see the change in background color, and the time y
4 min read
How to Build a Simple TikTok Clone Android App using Firebase?
TikTok is a mobile application that can be downloaded on smartphones and tablets. It is available on both iOS and Android operating systems and can be downloaded for free from the respective app stores. TikTok is a social media platform where users can view short video content i.e. 15 to 60 secs. A
5 min read
How to Build a Simple Bill Splitter App in Android?
Many payment applications such as Paytm, PhonePe, and others provide the functionality to split the bills among a number of people to divide it equally. This is a very useful feature that helps users to split the bills amongst each other. In this article, we will take a look at How to create a simpl
4 min read
How to Build a Body Mass Index Calculator in Android Studio?
The Body Mass Index (BMI) Calculator can be used to calculate BMI values based on height and weight. BMI is a fairly reliable indicator of body fatness for most people. Formula: BMI = (weight) / (height * height) Approach: BMI is a number calculated from an individualâs weight and height. To find ou
4 min read
How to Build a Sensor App in Android?
Android mobile phones have sensors, so we can perform various functions like Controlling screen brightness, Monitoring acceleration along a single axis, Motion detection, etc. In this article, we will be building an application that will determine the intensity of light in the room with the help of
5 min read