Kotlin Enums Tutorial for Android: Getting Started
In this tutorial, you’ll build an Android app, using the full potential of Kotlin Enums to handle a list of cartoon avatars and help your users easily create their profiles. By Caio Fukelmann Landau.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Kotlin Enums Tutorial for Android: Getting Started
25 mins
- Getting Started
- Kotlin Enums
- Are Enums Bad for Memory Management?
- Defining a Basic Enum
- Using The Enum
- Benefits of Enums
- Displaying an Avatar for Each Enum Case
- Add Extra Data to Enums
- Replace when() With Enum Property
- Enums Automatically Implement Equality
- Using the Name of an Enum Case
- Caveats of the name Property
- The Enum ordinal Property
- Obtaining Data From the Intent
- Enums Can Have Methods
- Enum Extras: Interfaces and Synthetic Methods
- Where to Go From Here?
Caveats of the name Property
You shouldn’t use name
for user-facing text. These strings are not localized, which means there’s no easy way to translate them to different languages if your app supports that. Instead, you can use name
for:
-
Programmatically referencing enum cases: A great example is the above. When your app needs to pass enum cases around via
Intent
, you can use its name to reference it. - Debugging: It can be useful to log a given enum case for debugging purposes. For example: You could log the selected avatar name to help identify a bug in your code.
With that said, you should not hardcode those strings to reference them later. Renaming an enum case will change its name
, and the hardcoded string will no longer work. A special case to look out for is that of using the name
to persist data that references enums. This can lead to trouble! Imagine if you rename DOG
to SUNGLASS_DOG
. The new version is no longer backwards compatible, and the persisted string now references a non-existent case! Make sure to account for the possibility of changing names when designing your code to avoid trouble later.
The Enum ordinal Property
In addition to name
, enums also have an ordinal
property. This references the zero-based position at which the enum case appears in code. Using CartoonAvatar
as an example, see what each case’s ordinal
is:
enum class CartoonAvatar(@DrawableRes val drawableRes: Int) {
DOG(R.mipmap.avatar_dog), // ordinal = 0
MONSTER(R.mipmap.avatar_monster), // ordinal = 1
OCTOPUS(R.mipmap.avatar_octopus), // ordinal = 2
NONE(R.mipmap.avatar_none) // ordinal = 3
}
ordinal
can change as you move cases around, add more, remove some, etc. For that reason, be mindful when using it. Avoid hardcoding ordinal
values to reference enum cases. Use name
to reference them instead of ordinal
.
Now, you’ll continue with the app.
Obtaining Data From the Intent
Open AvatarSelectedActivity.kt and add the following method below // TODO 8
:
private fun populateView(fullName: String, cartoonAvatar: CartoonAvatar) {
// 1
fullNameTextView.text = fullName
// 2
when (cartoonAvatar) {
CartoonAvatar.NONE -> {
initialTextView.text = fullName.uppercasedInitial()
selectedAvatarImageView.visibility = View.GONE
initialTextView.visibility = View.VISIBLE
}
else -> {
selectedAvatarImageView.setImageResource(cartoonAvatar.drawableRes)
selectedAvatarImageView.visibility = View.VISIBLE
initialTextView.visibility = View.GONE
}
}
}
Here’s what the code above does:
- Populates the
fullNameTextView
with the full name passed viaIntent
- Using
when()
with anelse
clause, you’re able to omit having to reference all cases individually. If the selectedCartoonAvatar
isNONE
, populating the view means placing the user’s name’s initial in theinitialTextView
and hiding the selected avatar image. For all other cases, it means hiding theinitialTextView
and displaying the selected avatar’s image.
Now, paste the following below // TODO 9
:
private fun loadDataFromIntent() {
// 1
val extras = intent.extras
val fullName = extras?.getString(EXTRA_FULL_NAME)
val cartoonEnumCaseName = extras?.getString(EXTRA_CARTOON)
if (cartoonEnumCaseName == null) {
// 2
finish()
return
}
// 3
val cartoonAvatar = CartoonAvatar.valueOf(cartoonEnumCaseName)
populateView(fullName ?: "", cartoonAvatar)
}
Here’s what you’re doing in the code above:
- Read the data passed via
Intent
, using the constants defined in thecompanion object
. - If you started the activity incorrectly — without using the
newIntent()
method — this finishes it because there’s nothing it can do. - Enums have a static method called
valueOf()
that returns the enum case from itsname
. Using this method, you recover the case that the user selected in the previous screen. With all the necessary data, you callpopulateView()
.
To combine everything, find // TODO 10
at the end of onCreate()
and add the following below it:
loadDataFromIntent()
Build and run. After typing your name and selecting an avatar, press Continue.
As you can see above, the view will now show the name and avatar.
Enums Can Have Methods
The last part your app is missing is a description of the selected avatar in AvatarSelectedActivity
.
Here, you’ll use another functionality enums provide: They can have methods. So, open CartoonAvatar.kt and add ;
right after the last case of the enum so that it looks like the following:
NONE(R.mipmap.avatar_none); // <- Added the semicolon
This is necessary to separate the cases from the rest of the enum body.
Between NONE
and }
, paste the following code:
abstract fun selectionDescription(context: Context): String
The line above defines a method that every enum case must implement. Since this is an abstract
method and none of your cases implement that yet, Android Studio will start complaining. To fix it, right-click the case highlighted in red and select Show Context Actions ▸ Implement Members like below:
Select Implement members in the pop-up above. A window will show up. Press OK. See the image below for reference:
Make sure to select the missing method and then press OK.
Repeat the steps above for all enum cases. Android Studio will add TODO()
marks to those methods. Replace them with the following code:
DOG
:
val name = context.getString(R.string.avatar_description_dog)
return context.getString(R.string.your_selected_avatar_description, name)
MONSTER
:
val name = context.getString(R.string.avatar_description_monster)
return context.getString(R.string.your_selected_avatar_description, name)
OCTOPUS
:
val name = context.getString(R.string.avatar_description_octopus)
return context.getString(R.string.your_selected_avatar_description, name)
NONE
:
return "" // No avatar selected, show no text
The code above returns a string like "Your selected avatar: Dog With Sunglasses"
, depending on which character received the call to selectionDescription()
. For the special case of NONE
, it returns an empty string.
Finally, plug all that in by opening AvatarSelectedActivity.kt and adding the following line inside populateView()
, below fullNameTextView.text = fullName
:
selectedAvatarDescriptionTextView.text = cartoonAvatar.selectionDescription(this)
The code above sets the description you just defined in the enum as the text for selectedAvatarDescriptionTextView
.
Build and run. After making all selections, you'll see the description below the profile picture and name card:
Great work! But what if you want to know more about enums?
Enum Extras: Interfaces and Synthetic Methods
What else can you do with enums? See below:
- Kotlin enums are classes. As such, they can implement interfaces. Then, each enum case can implement methods and properties separately, or the enum definition can provide a general implementation. Note that enums cannot extend from other classes.
- There are global functions to allow accessing enums in a generic way:
enumValues
and() enumValueOf
.()
An example of a bad practice is adding too much data to enum constants. Unlike instances of classes, which the garbage collector helps deallocate when they are no longer needed, enum constants may stay in memory for the lifetime of your application. So, be mindful of your app's memory consumption.
An example of a bad practice is adding too much data to enum constants. Unlike instances of classes, which the garbage collector helps deallocate when they are no longer needed, enum constants may stay in memory for the lifetime of your application. So, be mindful of your app's memory consumption.
For examples and a quick overview of the main features of enums, check out Kotlin's official documentation on enum classes.