영화 정보 카테고리를 추가해 보겠습니다.
완성하면 아래 앱이 됩니다.
https://play.google.com/store/apps/details?id=com.enigmah2k.movieinfo
popular 정보를 가져온 형식을 그대로 활용하여 Top rated, Upcomming, Now playing, Discover 카테고리를 추가하겠습니다.
1. Api.kt 에 사용 API 를 추가합니다.
API 레퍼런스 사이트에서 Movies 메뉴를 참고합니다.
결과적으로 GET 하는 API name 만 다를 뿐 나머지 파라메터는 동일합니다.
@GET("movie/top_rated")
fun getTopRatedMovies(
@Query("api_key") apiKey: String = "<< API KEY >>",
@Query("page") page : Int,
@Query("language") language : String = "ko,en-US"
): Call<GetMoviesResponse>
@GET("movie/upcoming")
fun getUpcomingMovies(
@Query("api_key") apiKey: String = "<< API KEY >>",
@Query("page") page : Int,
@Query("language") language : String = "ko,en-US"
): Call<GetMoviesResponse>
@GET("movie/now_playing")
fun getNowPlayingMovies(
@Query("api_key") apiKey: String = "<< API KEY >>",
@Query("page") page : Int,
@Query("language") language : String = "ko,en-US"
): Call<GetMoviesResponse>
@GET("discover/movie")
fun getDiscoverMovies(
@Query("api_key") apiKey: String = "<< API KEY >>",
@Query("page") page : Int,
@Query("language") language : String = "ko,en-US"
): Call<GetMoviesResponse>
2. MoviesRepository 클래스에 API 호출 함수들을 추가합니다.
함수명만 다르고 구조는 동일하여 중목 코드 같지만 어쩔수 없습니다.
fun getTopRatedMovies(page: Int = 1,
onSuccess: (movies: List<Movie>) -> Unit,
onError: () -> Unit ) {
api.getTopRatedMovies(page = page)
.enqueue(object : Callback<GetMoviesResponse> {
override fun onResponse(
call: Call<GetMoviesResponse>,
response: Response<GetMoviesResponse>
) {
if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody != null) {
onSuccess.invoke(responseBody.movies)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetMoviesResponse>, t: Throwable) {
onError.invoke()
}
})
}
fun getUpcomingMovies( page: Int = 1,
onSuccess: (movies: List<Movie>) -> Unit,
onError: () -> Unit ) {
api.getUpcomingMovies(page = page)
.enqueue(object : Callback<GetMoviesResponse> {
override fun onResponse(
call: Call<GetMoviesResponse>,
response: Response<GetMoviesResponse>
) {
if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody != null) {
onSuccess.invoke(responseBody.movies)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetMoviesResponse>, t: Throwable) {
onError.invoke()
}
})
}
fun getNowPlayingMovies( page: Int = 1,
onSuccess: (movies: List<Movie>) -> Unit,
onError: () -> Unit ) {
api.getNowPlayingMovies(page = page)
.enqueue(object : Callback<GetMoviesResponse> {
override fun onResponse(
call: Call<GetMoviesResponse>,
response: Response<GetMoviesResponse>
) {
if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody != null) {
onSuccess.invoke(responseBody.movies)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetMoviesResponse>, t: Throwable) {
onError.invoke()
}
})
}
fun getDiscoverMovies( page: Int = 1,
onSuccess: (movies: List<Movie>) -> Unit,
onError: () -> Unit ) {
api.getDiscoverMovies(page = page)
.enqueue(object : Callback<GetMoviesResponse> {
override fun onResponse(
call: Call<GetMoviesResponse>,
response: Response<GetMoviesResponse>
) {
if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody != null) {
onSuccess.invoke(responseBody.movies)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetMoviesResponse>, t: Throwable) {
onError.invoke()
}
})
}
3. Strings 추가
<string name="toprated">Top Rated 평점</string>
<string name="most_toprated">최고 평점 영화</string>
<string name="upcoming">Upcoming</string>
<string name="now_playing">Now Playing</string>
<string name="discover">Discover</string>
한글로 해야 하는데 편의상 같은 값으로 사용해야 겠습니다.
4. 카테고리 레이아웃 추가
fragment_movie.xml 에 카테고리 레이아웃을 추가합니다.
popular layout 과 동일하게 하여 id 값만 구분하였습니다.
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/linearLayout">
<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/toprated"
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_toprated" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/top_rated_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
android:id="@+id/linearLayout3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/linearLayout2">
<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/upcoming"
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/upcoming" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/upcoming_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
android:id="@+id/linearLayout4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/linearLayout3">
<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/now_playing"
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/now_playing" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/nowplaying_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
android:id="@+id/linearLayout5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/linearLayout4">
<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/discover"
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/discover" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/discover_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
android:id="@+id/linearLayout6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@+id/linearLayout5">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="\n\n\n\n"
tools:layout_editor_absoluteX="9dp" />
</LinearLayout>
5. MovieFragment.kt 에 카테고리별 실행 코드 구현
카테고리별 변수를 선언하고 MoviesAdapter 실행 코드를 구현합니다.
동일한 구조의 코드가 다섯개가 되었네요.
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
private lateinit var topRatedMovies: RecyclerView
private lateinit var topRatedMoviesAdapter: MoviesAdapter
private lateinit var topRatedMoviesLayoutMgr: LinearLayoutManager
private var topRatedMoviesPage = 1
private lateinit var upcomingMovies: RecyclerView
private lateinit var upcomingMoviesAdapter: MoviesAdapter
private lateinit var upcomingMoviesLayoutMgr: LinearLayoutManager
private var upcomingMoviesPage = 1
private lateinit var nowplayingMovies: RecyclerView
private lateinit var nowplayingMoviesAdapter: MoviesAdapter
private lateinit var nowplayingMoviesLayoutMgr: LinearLayoutManager
private var nowplayingMoviesPage = 1
private lateinit var discoverMovies: RecyclerView
private lateinit var discoverMoviesAdapter: MoviesAdapter
private lateinit var discoverMoviesLayoutMgr: LinearLayoutManager
private var discoverMoviesPage = 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()
topRatedMovies = root.findViewById(R.id.top_rated_movies)
topRatedMoviesLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
topRatedMovies.layoutManager = topRatedMoviesLayoutMgr
topRatedMoviesAdapter = MoviesAdapter(mutableListOf())
topRatedMovies.adapter = topRatedMoviesAdapter
getTopRatedMovies()
upcomingMovies = root.findViewById(R.id.upcoming_movies)
upcomingMoviesLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
upcomingMovies.layoutManager = upcomingMoviesLayoutMgr
upcomingMoviesAdapter = MoviesAdapter(mutableListOf())
upcomingMovies.adapter = upcomingMoviesAdapter
getUpcomingMovies()
nowplayingMovies = root.findViewById(R.id.nowplaying_movies)
nowplayingMoviesLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
nowplayingMovies.layoutManager = nowplayingMoviesLayoutMgr
nowplayingMoviesAdapter = MoviesAdapter(mutableListOf())
nowplayingMovies.adapter = nowplayingMoviesAdapter
getNowplayingMovies()
discoverMovies = root.findViewById(R.id.discover_movies)
discoverMoviesLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
discoverMovies.layoutManager = discoverMoviesLayoutMgr
discoverMoviesAdapter = MoviesAdapter(mutableListOf())
discoverMovies.adapter = discoverMoviesAdapter
getDiscoverMovies()
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 getTopRatedMovies() {
MoviesRepository.getTopRatedMovies(
topRatedMoviesPage,
::onTopRatedMoviesFetched,
::onError
)
}
private fun attachTopRatedMoviesOnScrollListener() {
topRatedMovies.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = topRatedMoviesLayoutMgr.itemCount
val visibleItemCount = topRatedMoviesLayoutMgr.childCount
val firstVisibleItem = topRatedMoviesLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
topRatedMovies.removeOnScrollListener(this)
topRatedMoviesPage++
getTopRatedMovies()
}
}
})
}
private fun onTopRatedMoviesFetched(movies: List<Movie>) {
topRatedMoviesAdapter.appendMovies(movies)
attachTopRatedMoviesOnScrollListener()
}
private fun getUpcomingMovies() {
MoviesRepository.getUpcomingMovies(
upcomingMoviesPage,
::onUpcomingMoviesFetched,
::onError
)
}
private fun attachUpcomingMoviesOnScrollListener() {
upcomingMovies.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = upcomingMoviesLayoutMgr.itemCount
val visibleItemCount = upcomingMoviesLayoutMgr.childCount
val firstVisibleItem = upcomingMoviesLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
upcomingMovies.removeOnScrollListener(this)
upcomingMoviesPage++
getUpcomingMovies()
}
}
})
}
private fun onUpcomingMoviesFetched(movies: List<Movie>) {
upcomingMoviesAdapter.appendMovies(movies)
attachUpcomingMoviesOnScrollListener()
}
private fun getNowplayingMovies() {
MoviesRepository.getNowPlayingMovies(
nowplayingMoviesPage,
::onNowplayingMoviesFetched,
::onError
)
}
private fun attachNowplayingMoviesOnScrollListener() {
nowplayingMovies.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = nowplayingMoviesLayoutMgr.itemCount
val visibleItemCount = nowplayingMoviesLayoutMgr.childCount
val firstVisibleItem = nowplayingMoviesLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
nowplayingMovies.removeOnScrollListener(this)
nowplayingMoviesPage++
getNowplayingMovies()
}
}
})
}
private fun onNowplayingMoviesFetched(movies: List<Movie>) {
nowplayingMoviesAdapter.appendMovies(movies)
attachNowplayingMoviesOnScrollListener()
}
private fun getDiscoverMovies() {
MoviesRepository.getDiscoverMovies(
discoverMoviesPage,
::onDiscoverMoviesFetched,
::onError
)
}
private fun attachDiscoverMoviesOnScrollListener() {
discoverMovies.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = discoverMoviesLayoutMgr.itemCount
val visibleItemCount = discoverMoviesLayoutMgr.childCount
val firstVisibleItem = discoverMoviesLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
discoverMovies.removeOnScrollListener(this)
discoverMoviesPage++
getDiscoverMovies()
}
}
})
}
private fun onDiscoverMoviesFetched(movies: List<Movie>) {
discoverMoviesAdapter.appendMovies(movies)
attachDiscoverMoviesOnScrollListener()
}
private fun onError() {
Toast.makeText(activity, "error Movies", Toast.LENGTH_SHORT).show()
}
}
실행해 보면 아래와 같이 카테고리가 추가되어 나타납니다.
다음에는 상세 정보 페이지를 만들어 보겠습니다.
TMDB API 를 활용한 Android 앱 만들기
https://stockant.tistory.com/530
'프로그래밍 > 영화 TMDB API' 카테고리의 다른 글
영화 정보 앱 만들기 - TMDB API 사용, TV 정보 popular 추가 (0) | 2020.10.25 |
---|---|
영화 정보 앱 만들기 - TMDB API 사용, 영화 상세 정보 화면 (0) | 2020.10.24 |
영화 정보 앱 만들기 - TMDB API 사용, 인기 영화 정보 가져오기 2 (1) | 2020.10.22 |
영화 정보 앱 만들기 - TMDB API 사용, 인기 영화 정보 가져오기 1 (0) | 2020.10.21 |
영화 정보 앱 만들기 - TMDB API 사용, MovieFragment 추가 (0) | 2020.10.20 |