Make a Movies App Using TMDb API in Kotlin Part 5 - Movie Details

Feature #3: As a user, I want to be able to see the details of a movie

For offline viewing, get the eBook (PDF & ePUB) version + source code here.

Movie Details Screen

  1. Right click mymovies package, select New -> Activity -> Empty Activity.
  2. Enter the activity name - MovieDetailsActivity.
  3. Open activity_movie_details.xml and let’s build our UI.

     <androidx.core.widget.NestedScrollView 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="match_parent"
         android:fillViewport="true">
    
         <androidx.constraintlayout.widget.ConstraintLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content">
    
             <ImageView
                 android:id="@+id/movie_backdrop"
                 android:layout_width="0dp"
                 android:layout_height="0dp"
                 app:layout_constraintBottom_toBottomOf="@+id/backdrop_guideline"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toTopOf="parent" />
    
             <androidx.cardview.widget.CardView
                 android:id="@+id/movie_poster_card"
                 android:layout_width="128dp"
                 android:layout_height="172dp"
                 android:layout_marginStart="16dp"
                 android:layout_marginEnd="8dp"
                 app:cardCornerRadius="4dp"
                 app:layout_constraintBottom_toBottomOf="@+id/backdrop_guideline"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toBottomOf="@id/backdrop_guideline">
    
                 <ImageView
                     android:id="@+id/movie_poster"
                     android:layout_width="match_parent"
                     android:layout_height="match_parent" />
    
             </androidx.cardview.widget.CardView>
    
             <androidx.constraintlayout.widget.Guideline
                 android:id="@+id/backdrop_guideline"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 app:layout_constraintGuide_percent="0.4" />
    
             <TextView
                 android:id="@+id/movie_title"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="16dp"
                 android:layout_marginTop="16dp"
                 android:layout_marginEnd="16dp"
                 android:textColor="@android:color/white"
                 android:textSize="18sp"
                 android:textStyle="bold"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toEndOf="@+id/movie_poster_card"
                 app:layout_constraintTop_toBottomOf="@+id/backdrop_guideline" />
    
             <TextView
                 android:id="@+id/movie_release_date"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:textColor="#757575"
                 android:textSize="12sp"
                 app:layout_constraintStart_toStartOf="@+id/movie_title"
                 app:layout_constraintTop_toBottomOf="@+id/movie_title" />
    
             <androidx.constraintlayout.widget.Barrier
                 android:id="@+id/movie_poster_title_barrier"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 app:barrierDirection="bottom"
                 app:constraint_referenced_ids="movie_rating,movie_release_date" />
    
             <TextView
                 android:id="@+id/movie_overview"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="16dp"
                 android:layout_marginTop="16dp"
                 android:layout_marginEnd="16dp"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toBottomOf="@+id/movie_poster_title_barrier" />
    
             <RatingBar
                 android:id="@+id/movie_rating"
                 style="@style/Widget.AppCompat.RatingBar.Small"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="8dp"
                 app:layout_constraintEnd_toEndOf="@+id/movie_poster_card"
                 app:layout_constraintStart_toStartOf="@+id/movie_poster_card"
                 app:layout_constraintTop_toBottomOf="@+id/movie_poster_card" />
         </androidx.constraintlayout.widget.ConstraintLayout>
     </androidx.core.widget.NestedScrollView>
    
  4. Open your MovieDetailsActivity and instantiate the views.

     class MovieDetailsActivity : AppCompatActivity() {
    
         private lateinit var backdrop: ImageView
         private lateinit var poster: ImageView
         private lateinit var title: TextView
         private lateinit var rating: RatingBar
         private lateinit var releaseDate: TextView
         private lateinit var overview: TextView
    
         override fun onCreate(savedInstanceState: Bundle?) {
             super.onCreate(savedInstanceState)
             setContentView(R.layout.activity_movie_details)
    
             backdrop = findViewById(R.id.movie_backdrop)
             poster = findViewById(R.id.movie_poster)
             title = findViewById(R.id.movie_title)
             rating = findViewById(R.id.movie_rating)
             releaseDate = findViewById(R.id.movie_release_date)
             overview = findViewById(R.id.movie_overview)
         }
     }
    
  5. Just above the class name, add your intent variables.

     const val MOVIE_BACKDROP = "extra_movie_backdrop"
     const val MOVIE_POSTER = "extra_movie_poster"
     const val MOVIE_TITLE = "extra_movie_title"
     const val MOVIE_RATING = "extra_movie_rating"
     const val MOVIE_RELEASE_DATE = "extra_movie_release_date"
     const val MOVIE_OVERVIEW = "extra_movie_overview"
    
     class MovieDetailsActivity : AppCompatActivity() { ... }
    

    These variables will be used as keys when we pass intent extras to MovieDetailsActivity.

  6. Use the keys above to populate the movie’s details.

     class MovieDetailsActivity : AppCompatActivity() {
         ...
    
         override fun onCreate(savedInstanceState: Bundle?) {
             ...
    
             val extras = intent.extras
    
             if (extras != null) {
                 populateDetails(extras)
             } else {
                 finish()
             }
         }
    
         private fun populateDetails(extras: Bundle) {
             extras.getString(MOVIE_BACKDROP)?.let { backdropPath ->
                 Glide.with(this)
                     .load("https://image.tmdb.org/t/p/w1280$backdropPath")
                     .transform(CenterCrop())
                     .into(backdrop)
             }
    
             extras.getString(MOVIE_POSTER)?.let { posterPath ->
                 Glide.with(this)
                     .load("https://image.tmdb.org/t/p/w342$posterPath")
                     .transform(CenterCrop())
                     .into(poster)
             }
    
             title.text = extras.getString(MOVIE_TITLE, "")
             rating.rating = extras.getFloat(MOVIE_RATING, 0f) / 2
             releaseDate.text = extras.getString(MOVIE_RELEASE_DATE, "")
             overview.text = extras.getString(MOVIE_OVERVIEW, "")
         }
     }
    

    The available backdrop sizes are:

    • w300
    • w780
    • w1280
    • original

Open Movie Details Screen from Movie List Screen

  1. Open your MainActivity and create a new method called showMovieDetails().

     class MainActivity : AppCompatActivity() {
         ...
         private fun showMovieDetails(movie: Movie) {
             val intent = Intent(this, MovieDetailsActivity::class.java)
             intent.putExtra(MOVIE_BACKDROP, movie.backdropPath)
             intent.putExtra(MOVIE_POSTER, movie.posterPath)
             intent.putExtra(MOVIE_TITLE, movie.title)
             intent.putExtra(MOVIE_RATING, movie.rating)
             intent.putExtra(MOVIE_RELEASE_DATE, movie.releaseDate)
             intent.putExtra(MOVIE_OVERVIEW, movie.overview)
             startActivity(intent)
         }
         ...
     }
    
  2. Open your MoviesAdapter and add a new parameter in the constructor which is a higher-order function that will be called when a movie is clicked.

     class MoviesAdapter(
         private var movies: MutableList<Movie>,
         private val onMovieClick: (movie: Movie) -> Unit
     ) : ...
    
  3. Invoke onMovieClick when a movie is clicked.

     class MoviesAdapter(
         private var movies: MutableList<Movie>,
         private val onMovieClick: (movie: Movie) -> Unit
     ) : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
    
         ...
    
         inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
             ...
             fun bind(movie: Movie) {
                 ...
                 itemView.setOnClickListener { onMovieClick.invoke(movie) }
             }
         }
     }
    
  4. Open your MainActivity and pass a higher-order function to your adapters which calls the showMovieDetails() method that you’ve just created.

     class MainActivity : AppCompatActivity() {
    
         ...
    
         override fun onCreate(savedInstanceState: Bundle?) {
             ...
             popularMoviesAdapter = MoviesAdapter(mutableListOf()) { movie -> showMovieDetails(movie) }
             ...
    
             ...
             topRatedMoviesAdapter = MoviesAdapter(mutableListOf()) { movie -> showMovieDetails(movie) }
             ...
    
             ...
             upcomingMoviesAdapter = MoviesAdapter(mutableListOf()) { movie -> showMovieDetails(movie) }
             ...
         }
         ...
     }
    
  5. Run the app, tap any movie and you should see something like this:

Congratulations! You have just made a full-blown and portfolio-worthy Android app that you can put on Google Play Store.

In the next chapter, we’ll explore some feature ideas that you can do next to even improve this version. Head over to Part 6 - What’s Next?

7 ways to become a really good Android developer