Android Kotlin Tutorial:
Build Your Own Tip Calculator

Learn how to use Android’s first class language to build a sleek-looking tip calculator.

Version: Kotlin 1.3.21, Android 4, Android Studio 3

You ended a sumptuous dinner with your buddies, and everyone starts to whip out their smartest calculator app to compute individual’s expenses.
However, you, armed with some Android development background, why don’t you build your
exclusive calculator?

If you have not developed on Android or Kotlin before, you can check out Introduction to Kotlin tutorials here to get some basic understanding before proceeding further.

In this tutorial, you’ll learn how to build a Tip Calculator App from scratch using Kotlin.

This is how it looks like:

You key in total amount, preferred tip amount and number of people to compute individual’s expense real-time.

Getting Started

At a high level, this is how the entire interface looks like:

Download the starter project. Extract and open the starter project in Android Studio 3.0 or later. Build and run your project; you’ll see a blank white screen, and this is the starting point.

The project has TODOs added and labeled in chronological order to guide you.
To view them, go to View -> Tool Windows -> TODO.

Explore the project and open up colors.xml to check out the color theme. In strings.xml, static texts are already preset for you. In styles.xml, there are a few font styles preset
(h1Bold, h2, h2Bold, h4 and a custom operationButton) for you.

Build Expense Section

Open activity_main.xml and add the following codes inside LinearLayout (#1):

<TextView
android:id="@+id/expensePerPersonTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="30dp"
style="@style/h1Bold"
android:textColor="@color/colorAccent"
android:text="0"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="25dp"
style="@style/h2"
android:textColor="@color/colorAccent"
android:text="@string/perPersonStaticText"/>

Feel free to adjust the style values directory.
You can also play around the colors using the material.io tool.

Styles like h1, h2 are good ways to name the text styles.
Paddings help to create breathing spaces.

Build and run your project to make sure you see this:

Perfect! — The expense here computes real-time as you make changes to its parameters.
Hold your excitement; you are on your way there.

Build Bill Section

Add these codes to insert a new LinearLayout after the Expense Section (#2):

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/colorAccent">
<! — TODO #3: Build Bill Section →
</LinearLayout>

Close the LinearLayout after the list of TODOs.
Now add the following codes inside the added LinearLayout (#3):

<TextView
android:layout_margin="15dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorWhite"
style="@style/h4"
android:text="@string/billStaticText"/>
<EditText
android:id="@+id/billEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorWhite"
android:inputType="numberDecimal"
android:maxLines="1"
style="@style/h2Bold"
android:text="0"/>

As the main feature of this app is to calculate an individual’s expense, the text style here places more emphasis on the expensePerPersonTextView.
The EditText restricts input to one-liner and must be of numberDecimal inputType.

Build and run the project to try and input total damage (ouch! Yes it must’ve been massive damage to the pockets that it’s essential to split the bill!).

Build Tip & People Sections
Next, add the following codes to insert a new LinearLayout section inside the same inner LinearLayout for tip amount selection (#4):

<TextView
android:layout_margin="15dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorWhite"
style="@style/h4"
android:text="@string/tipStaticText"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="@+id/subtractTipButton"
style="@style/operationButton"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:src="@drawable/subtract"/>
<TextView
android:id="@+id/tipTextView"
android:layout_margin="15dp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/colorWhite"
android:layout_weight="1"
style="@style/h2Bold"
android:text="20%"/>
<ImageButton
android:id="@+id/addTipButton"
style="@style/operationButton"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:src="@drawable/add"/>
</LinearLayout>

This adds an entire section to increment and decrement the tip amount. The text has defaulted to 20 and ImageButtons have icons included in the drawable folder.

For Number of People selection section, it’s entirely like Tip’s selection.

Duplicate the entire section and amend the following (#5):

  • ImageButton ids (i.e. subtractPeopleButton, addPeopleButton)
  • TextView ids (i.e. numberOfPeopleStaticText, numberOfPeopleTextView)
  • DefaultText of numberOfPeopleTextView should be 4

Tip: For code hygiene purpose, you can do a Control+Option+I to auto-indent the lines of code.

Run the app, and you should be able to key in bill amount, press the add/subtract buttons, but nothing happens yet. This is the moment where you work on the “neuro system” of the app.

Connect the Views

Open up MainActivity.kt and add these into initViews function to connect the views (#6):

private fun initViews() {
expensePerPersonTextView = findViewById(R.id.expensePerPersonTextView)
billEditText = findViewById(R.id.billEditText)
addTipButton = findViewById(R.id.addTipButton)
tipTextView = findViewById(R.id.tipTextView)
subtractTipButton = findViewById(R.id.subtractTipButton)
addPeopleButton = findViewById(R.id.addPeopleButton)
numberOfPeopleTextView = findViewById(R.id.numberOfPeopleTextView)
subtractPeopleButton = findViewById(R.id.subtractPeopleButton)
//TODO #8: Bind Buttons to Listener//TODO #16: Bind EditText to TextWatcher}

Work on Add/Subtract Buttons

To handle Button clicks, implement View.OnClickListener at the Class level (#7):

class MainActivity : AppCompatActivity(), View.OnClickListener {

Oops! This caused a compiler error as you are now required to conform to the listener by implementing onClick function right after initViews function (#8):

override fun onClick(v: View?) {
when (v?.id) {
R.id.addTipButton -> incrementTip()
R.id.subtractTipButton -> decrementTip()
R.id.addPeopleButton -> incrementPeople()
R.id.subtractPeopleButton -> decrementPeople()
}
}

Kotlin has made usual switch case format with more superpowers; it is more readable and forces you to be more concise when you create functions. You can read this aloud:
“When I click addTipButton, incrementTip.” Isn’t that awesome?

Add the following codes to all the increment and decrement functions (#9 — #12):

private fun incrementTip() {
if (tipPercent != MAX_TIP) {
tipPercent += TIP_INCREMENT_PERCENT
tipTextView.text = String.format("%d%%", tipPercent)
}
}
private fun decrementTip() {
if (tipPercent != MIN_TIP) {
tipPercent -= TIP_INCREMENT_PERCENT
tipTextView.text = String.format("%d%%", tipPercent)
}
}
private fun incrementPeople() {
if (numberOfPeople != MAX_PEOPLE) {
numberOfPeople += PEOPLE_INCREMENT_VALUE
numberOfPeopleTextView.text = numberOfPeople.toString()
}
}
private fun decrementPeople() {
if (numberOfPeople != MIN_PEOPLE) {
numberOfPeople -= PEOPLE_INCREMENT_VALUE
numberOfPeopleTextView.text = numberOfPeople.toString()
}
}

Here, the code guards increment functions with max values (MAX_TIP & MAX_PEOPLE). Additionally, the code guards decrement functions with min values (MIN_TIP & MIN_PEOPLE).

Bind the listeners to the buttons like this in initViews function to finish the linkage (#13):

private fun initViews() {...addTipButton.setOnClickListener(this)
subtractTipButton.setOnClickListener(this)
addPeopleButton.setOnClickListener(this)
subtractPeopleButton.setOnClickListener(this)
//TODO #15: Bind EditText to TextWatcher
}

Run the app and play around with your new toy! You can now adjust the total damage, your generous tip and number of buddies! Hurray! You are getting there …

Implement Calculate Expense

Now, add the following codes to implement calculate expense (#14):

private fun calculateExpense() {val totalBill = billEditText.text.toString().toDouble()val totalExpense = ((HUNDRED_PERCENT + tipPercent) / HUNDRED_PERCENT) * totalBill
val individualExpense = totalExpense / numberOfPeople
expensePerPersonTextView.text = String.format("$%.2f", individualExpense)}

Next, call this function whenever you adjust the tip and number of people (#14).

private fun incrementTip() {}private fun decrementTip() {}private fun incrementPeople() {}private fun decrementPeople() {}

Run the app; it looks great! You could enter bill amount, adjust the tips and buddies to see the individual’s expense already getting updated! However, the app can be better.

If you try to delete the bill amount and then increment the tips or buddies, the app crashes as there is no validation for empty bill amount yet. Moreover, if you try to adjust the bill amount, the expense doesn’t get updated.

Getting Expense Computed Real-time

Implement TextWatcher at the Class level (#15):

class MainActivity : AppCompatActivity(), View.OnClickListener, TextWatcher {

Bind it to billEditText’s listener like this (#16):

billEditText.addTextChangedListener(this)

Add the following codes to implement functions to conform to TextWatcher (#17):

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (!billEditText.text.isEmpty()) {
calculateExpense()
}
}
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

Tips: Anytime you see a prompt for import, you can use the shortcut keys of Option+Return to import the package required.

Include afterTextChanged & beforeTextChanged to conform to TextWatcher. In onTextChanged, a validation check is present before calculating expense.

Now, run the app and enjoy your finished product!

Where to Go From Here?

Congratulations! You have just built your own Tip Calculator app in Kotlin!

You can download the final project for this tutorial here.

Kotlin is a first-class Android’s language which sparks a significant level of excitement among developers because of its type inference and concise syntax capabilities. It is definitely worth the time to dive deeper, so you can write more complex apps.

You can learn more about Kotlin, the classes, and tools I have used in this tutorial here:

  1. Kotlin Tutorials
  2. Color Picker from material.io
  3. TextWatcher

Googler. Loves God, Loves my wife, Loves my Family & Corgis. Freelance Writer when not coding. Author @ raywenderlich.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store