인기 영화 정보 가져오기
완성하면 아래 앱이 됩니다.
https://play.google.com/store/apps/details?id=com.enigmah2k.movieinfo
서버에서 movie/popular 를 호출하여 영화목록을 가져오는 것 까지 하였습니다.
이번에는 recyclerview 와 CardView 를 적용하여 아래와 같이 나타나도록 구현해 보겠습니다.
데이터를 잘 가져올 수 있다는 전제로 필요한 사항은 다음과 같습니다.
1. 개별 영화 아이템을 구성할 layout - CardView
2. 영화 목록을 보여줄 카테고리 layout - RecyclerView
3. 내용을 채울 어뎁터 - Adapter
RecyclerView 사용법만 알면 됩니다.
즉, RecyclerView 사용법에 대한 사전 학습이 되어 있어야 합니다.
1. 개별 영화 아이템 layout 구성 - item_movie
아래와 같이 개별 영화 정보를 표시하는 layout을 구성해 보겠습니다.
\TMDBtest\app\src\main\res\layout 폴더에 item_movie.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"
android:layout_width="128dp"
android:layout_height="192dp"
android:layout_marginEnd="8dp"
app:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/item_movie_poster"
android:layout_width="match_parent"
android:layout_height="172dp" />
<TextView
android:id="@+id/item_movie_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title" />
</LinearLayout>
</androidx.cardview.widget.CardView>
CardView 로 만들었고, ImageView 와 TextView 를 구성하였습니다.
2. 영화 목록 카테고리 layout - fragment_movie
MovieFragment.kt를 만들었으므로, fragment_movie.xml 에 구현하면 됩니다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".ui.movie.MovieFragment">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/popular"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@string/most_popular" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/popular_movies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingEnd="16dp" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
향후 Top rated 정보나 Now playing 등 카테고리 확장을 위해 ScrollView 로 적용하였습니다.
제목과 세부설명은 TextView 로 영화 정보는 RecyclerView 로 구성하였습니다.
3. Adapter 구현 - MoviesAdapter.kt
class MoviesAdapter (var movies: MutableList<Movie>) : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>(){
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val poster: ImageView = itemView.findViewById(R.id.item_movie_poster)
fun bind(movie: Movie) {
Glide.with(itemView)
.load("https://image.tmdb.org/t/p/w342${movie.poster_path}")
.transform(CenterCrop())
.into(poster)
itemView.item_movie_title.text = movie.title
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
val view = LayoutInflater
.from(parent.context)
.inflate(R.layout.item_movie, parent, false)
return MovieViewHolder(view)
}
override fun getItemCount(): Int = movies.size
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bind(movies[position])
}
fun appendMovies(movies: List<Movie>) {
this.movies.addAll(movies)
notifyItemRangeInserted(
this.movies.size,
movies.size - 1
)
}
}
item_movie.xml 로 구성된 개별 영화 정보 layout으로 ViewHolder를 구성합니다.
이미지는 Glide 를 사용하여 load 합니다.
실제 이미지가 있는 URL은 https://image.tmdb.org/t/p/ 입니다.
w342 는 이미지의 사이즈를 나타냅니다.
이미지 경로와 사이즈는 아래 API 명세에 잘 나와 있습니다.
https://developers.themoviedb.org/3/configuration/get-api-configuration
{
"images": {
"base_url": "http://image.tmdb.org/t/p/",
"secure_base_url": "https://image.tmdb.org/t/p/",
"backdrop_sizes": [
"w300",
"w780",
"w1280",
"original"
],
"logo_sizes": [
"w45",
"w92",
"w154",
"w185",
"w300",
"w500",
"original"
],
"poster_sizes": [
"w92",
"w154",
"w185",
"w342",
"w500",
"w780",
"original"
],
영화 정보를 가져왔을 때 이미지 위치는 base url 과 사이즈 값이 없는 파일명만 넘어옵니다.
4. MoviesAdapter 실행
영화 정보 결과를 RecyclerView 에 적용할 수 있도록 Adapter를 아래 형식으로 구성합니다.
private lateinit var popularMovies: RecyclerView
private lateinit var popularMoviesAdapter: MoviesAdapter
private lateinit var popularMoviesLayoutMgr: LinearLayoutManager
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? ): View? {
root = inflater.inflate(R.layout.fragment_movie, container, false)
popularMovies = root.findViewById(R.id.popular_movies)
popularMoviesLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
popularMovies.layoutManager = popularMoviesLayoutMgr
popularMoviesAdapter = MoviesAdapter(mutableListOf())
popularMovies.adapter = popularMoviesAdapter
getPopularMovies()
return root
}
private fun onPopularMoviesFetched(movies: List<Movie>) {
popularMoviesAdapter.appendMovies(movies)
}
onPopularMoviesFetched() 에서 영화 정보를 append 합니다.
실행을 하면 아래와 같이 잘 나타납니다.
하지만 처음 호출 결과 인 첫페이지 정보 만큼만 스크롤 됩니다.
더 많은 정보를 가져오기 위해 추가 페이지 결과를 표시하는 것이 필요합니다.
5. 다음 페이지 정보 추가
page 값을 저장하는 변수를 선언합니다.
private var popularMoviesPage = 1
getPopularMovies() 호출시 전달하는 page 값을 popularMoviesPage 로 변경합니다.
스크롤이 넘어갈 경우 다음 페이지 정보를 호출하는 OnScrollListener 를 추가합니다.
private fun attachPopularMoviesOnScrollListener() {
popularMovies.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = popularMoviesLayoutMgr.itemCount
val visibleItemCount = popularMoviesLayoutMgr.childCount
val firstVisibleItem = popularMoviesLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
popularMovies.removeOnScrollListener(this)
popularMoviesPage++
getPopularMovies()
}
}
})
}
onPopularMoviesFetched() 에서 attachPopularMoviesOnScrollListener() 를 호출합니다.
private fun onPopularMoviesFetched(movies: List<Movie>) {
popularMoviesAdapter.appendMovies(movies)
attachPopularMoviesOnScrollListener()
}
이렇게 하면 스크롤이 계속 됩니다.
MovieFragment.kt 의 전체 코드는 아래와 같습니다.
class MovieFragment : Fragment() {
lateinit var root: View
private lateinit var popularMovies: RecyclerView
private lateinit var popularMoviesAdapter: MoviesAdapter
private lateinit var popularMoviesLayoutMgr: LinearLayoutManager
private var popularMoviesPage = 1
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? ): View? {
root = inflater.inflate(R.layout.fragment_movie, container, false)
popularMovies = root.findViewById(R.id.popular_movies)
popularMoviesLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
popularMovies.layoutManager = popularMoviesLayoutMgr
popularMoviesAdapter = MoviesAdapter(mutableListOf())
popularMovies.adapter = popularMoviesAdapter
getPopularMovies()
return root
}
private fun getPopularMovies() {
MoviesRepository.getPopularMovies(
popularMoviesPage,
::onPopularMoviesFetched,
::onError
)
}
private fun onPopularMoviesFetched(movies: List<Movie>) {
popularMoviesAdapter.appendMovies(movies)
attachPopularMoviesOnScrollListener()
}
private fun attachPopularMoviesOnScrollListener() {
popularMovies.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = popularMoviesLayoutMgr.itemCount
val visibleItemCount = popularMoviesLayoutMgr.childCount
val firstVisibleItem = popularMoviesLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
popularMovies.removeOnScrollListener(this)
popularMoviesPage++
getPopularMovies()
}
}
})
}
private fun onError() {
Toast.makeText(activity, "error Movies", Toast.LENGTH_SHORT).show()
}
}
다음에는 API를 추가하여 Top rated, Upcomming, Now playing, Discover 카테고리를 추가해 보겠습니다.
TMDB API 를 활용한 Android 앱 만들기
https://stockant.tistory.com/530
'프로그래밍 > 영화 TMDB API' 카테고리의 다른 글
영화 정보 앱 만들기 - TMDB API 사용, 영화 상세 정보 화면 (0) | 2020.10.24 |
---|---|
영화 정보 앱 만들기 - TMDB API 사용, 영화 카테고리 추가 (0) | 2020.10.22 |
영화 정보 앱 만들기 - TMDB API 사용, 인기 영화 정보 가져오기 1 (0) | 2020.10.21 |
영화 정보 앱 만들기 - TMDB API 사용, MovieFragment 추가 (0) | 2020.10.20 |
영화 정보 앱 만들기 - TMDB API 사용, 기본 틀 만들기 (0) | 2020.10.20 |