Introduction to Kotlin Lambdas: Getting Started

In this tutorial you will learn how to use lambda expressions and other functional literals provided by Kotlin for the Android platform. Lambda expression is simplified representation of a function. It can be passed as a parameter, stored in a variable or even returned as a value. By Rachit .

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Understanding Lambda Concepts

Lambdas have two special characteristics.

First, the onClickListener() that you just defined could’ve been written this other way:

button_add_song_empty.setOnClickListener(View.OnClickListener {
  showAddSongDialog()
})

As you can see, in this case, the lambda is defined inside the parentheses as a parameter. If a lambda has a function as its last or only parameter, you can omit the parentheses as you did in the previous code.

The second characteristic is that you can replace interfaces with a single method with a lambda expression. You can check the Android Documentation on View.OnClickListener to see that View.OnClickListener is an interface with just one method: onClick(View v).

Finally, call setClickListeners() from the onCreate() function in the MainActivity:

  setClickListeners()

Build and run, and click the Add Song button again. You will see the dialog to add a new song.

Next, you will add logic to show songs and hide empty UI.

Hiding the Empty UI

The MainActivity class initially shows an empty screen. You need to hide the empty UI once you start adding songs.

In the MainActivity.kt file, add the following lambda expression as a new property below private lateinit var songStore: SongStore to toggle the empty UI visibility:

// 1
val toggleEmptyView = { show: Boolean ->
  // 2
  group_empty.visibility = if (show) View.VISIBLE else View.GONE
  button_add_song.visibility = if (show) View.GONE else View.VISIBLE
}	

In the snippet above, you:

  1. Define a lambda that has a parameter show of Boolean type.
  2. Show the empty UI if the show value is true, else hide it.

Next, you will add the code to show songs from SongStore on the UI.

Displaying Songs

As specified earlier, the SongStore.kt stores the song to preferences. It stores each song as a comma-separated String item in StringSet preference in the format title, artist, year.

The SongStore class provides you with a property allSongs to access all the currently stored songs. You will now add the code to show song list on the screen. You will also add a bit of styling to the song list by underlining the song title.

In the MainActivity.kt file, and add the following function below the setClickListeners() method to show the available songs. Don’t forget to import the necessary classes using Opt + Enter (or Alt + Enter on Windows):

// 1
private fun showAllSongs(songs: List<String>) {
  // 2
  val spans = mutableListOf<Spanned>()
  // 3
  val underlineTitle: (String) -> SpannableString = {
    val songTitle = it.split(",")[0]
    SpannableString(it).apply {
      setSpan(UnderlineSpan(), 0, songTitle.length, 
      Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
    }
  }
  // 4
  for (song in songs) {
    spans.add(underlineTitle(song))
    spans.add(SpannedString("\n\n"))
  }
  // 5
  text_view_all_songs.text = TextUtils.concat(*spans.toTypedArray())
  // 6
  toggleEmptyView(spans.isEmpty())
}

In this code, you:

  1. Define function showAllSongs(), which takes a list of songs as parameter.
  2. Initialize list of Spanned to hold each styled song item.
  3. Define lambda expression which takes a parameter of String type and has a return type of SpannableString. The lambda finds the song title by splitting the song at , and reading the first value. It then applies the underline span to each song title.
  4. Apply the underline style to each song title by passing each song to the underlineTitle lambda variable as underlineTitle(song). You also add new line span to add spacing between song items in UI.
  5. Show the song list on the UI by joining all the spans and setting it to text_view_all_songs view.
  6. Toggle the empty UI by calling the toggleEmptyView based on if the styled song item list is not empty.

Reloading Songs

In the MainActivity.kt file, add the following line below the setClickListeners() call in the onCreate() function:

  showAllSongs(songStore.allSongs.toList())

In this code block, you call the showAllSongs and pass the current stored songs by reading the store.allSongs.toList() value from SongStore class.

Also, add following code to the onSongAdded function:

  showAllSongs(songStore.allSongs.toList())
  toggleEmptyView(false)

The onSongAdded function is called by the AddSongFragment every time a new song is added.

In the above code, you call the showAllSongs and pass the current stored songs from SongStore class. You also toggle the empty UI.

Save your changes and run the app. No songs are visible yet as the SongStore is still empty. Next, you will go ahead and add the code to handle the addition of songs.

Anonymous Functions

As you learnt in the previous section, lambda expressions can’t have a return statement. The return type is either inferred from the variable which stores the lambda or from the last statement of the lambda body. Another missing thing is multiple return points you might often need.

You can use anonymous functions to solve these shortcomings. You define an anonymous function as a regular function omitting its name.

After you type in the song title, artist and year in the input fields on the screen, click the Save button.

Nothing happens yet as you must still add the code to read and store the input song. Next, you will add the code to save the song once you click the Save button.

Saving Songs

Open the AddSongFragment.kt file and add the following code below the handleSongSaveClick() function to read input data:

// 1
private fun saveSong(): Boolean {
  // 2
  val title = edit_text_title.text?.toString()
  val artist = edit_text_artist.text?.toString()
  val year = edit_text_year.text?.toString()

  return true
}

In this code, you:

  1. Define a function to saveSong() with a Boolean return type.
  2. Read and store the input values in the respective variables. You store the title, artist and year in the title, artist, year variables.

Next, you will add the following code inside the onClickListener { } block code defined in handleSongSaveClick() to call the saveSong function each time you click the Save button:

if (saveSong()) {
  dismiss()
}

Here, you dismiss the input screen dialog if the saveSong() returns true.

You might wonder why the saveSong() function returns a Boolean value. Next, you will find out why validation is important and the use case for anonymous functions.

Validating Song Data

Whenever you take user input, it is necessary for you to validate the input data and make sure input data is correct and consistent. Inconsistent data leads to app crashes and bad user experiences.

Add the following code below the saveSong() function in the AddSongFragment.kt file. You will need to validate the input data using the following functions before storing it:


// 1
private fun isValidArtist(artist: String?): Boolean {
  // 2
  val artistValidator = fun(value: String?): Boolean {
  // 3
  if (value.isNullOrEmpty()) {
    showError(R.string.error_artist_empty)
    return false
  }

  if (value.length < 2) { 
    showError(R.string.error_artist_invalid) 
    return false 
  } 
  return true } 
  
  // 4 
  return artistValidator(artist) 
} 

// 5 
private fun isValidYear(year: String?): Boolean { 
  val yearValidator: (String?) -> Boolean = { !it.isNullOrEmpty() && it.toInt() in 1877..2019 }
  return yearValidator(year)
}

In the code block above, you:

  1. Define a function isValidArtist() with Boolean return type.
  2. Initialize artistValidator variable with an anonymous function.
  3. Return false if the input value of artist name is null or empty or if its length is less than two characters. Otherwise, you return true from the anonymous function.
  4. Execute the anonymous function by calling artistValidator() and passing artist as parameter.
  5. Define a function isValidYear(), which returns a true or false value. It calls the lambda yearValidator on the year value to validate the input data and returns its result.

You will need to call these validation functions whenever a song is being saved.