TV 카테고리 추가
완성하면 아래 앱이 됩니다.
https://play.google.com/store/apps/details?id=com.enigmah2k.movieinfo
TV Top rated, TV On the Air, TV Airing Today, Discover API 를 사용하여 TV 카테고리도 추가해 보겠습니다.
1. Api.kt 에 사용 API 를 추가합니다.
API 레퍼런스 사이트에서 TV 메뉴를 참고합니다.
결과적으로 GET 하는 API name 만 다를 뿐 나머지 파라메터는 동일합니다.
@GET("tv/top_rated")
fun getTopRatedTV(
@Query("api_key") apiKey: String = "348eefabae0631d8003f24551c45a05c",
@Query("page") page : Int,
@Query("language") language : String = "ko,en-US"
): Call<GetTVResponse>
@GET("tv/on_the_air")
fun getOnTheAirTV(
@Query("api_key") apiKey: String = "348eefabae0631d8003f24551c45a05c",
@Query("page") page : Int,
@Query("language") language : String = "ko,en-US"
): Call<GetTVResponse>
@GET("tv/airing_today")
fun getAiringTodayTV(
@Query("api_key") apiKey: String = "348eefabae0631d8003f24551c45a05c",
@Query("page") page : Int,
@Query("language") language : String = "ko,en-US"
): Call<GetTVResponse>
@GET("discover/tv")
fun getDiscoverTV(
@Query("api_key") apiKey: String = "348eefabae0631d8003f24551c45a05c",
@Query("page") page : Int,
@Query("language") language : String = "ko,en-US"
): Call<GetTVResponse>
2. TVRepository .kt 클래스에 API 호출 함수들을 추가합니다.
함수명만 다르고 구조는 동일하여 중목 코드 같지만 어쩔수 없습니다.
fun getTopRatedTV(page: Int = 1,
onSuccess: (tvlist: List<TV>) -> Unit,
onError: () -> Unit){
api.getTopRatedTV(page = page)
.enqueue(object : Callback<GetTVResponse> {
override fun onResponse(
call: Call<GetTVResponse>,
response: Response<GetTVResponse>
) {
if(response.isSuccessful) {
val responseBody = response.body()
if(responseBody != null) {
onSuccess.invoke(responseBody.tvlist)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetTVResponse>, t: Throwable) {
onError.invoke()
}
})
}
fun getOnTheAirTV(page: Int = 1,
onSuccess: (tvlist: List<TV>) -> Unit,
onError: () -> Unit){
api.getOnTheAirTV(page = page)
.enqueue(object : Callback<GetTVResponse> {
override fun onResponse(
call: Call<GetTVResponse>,
response: Response<GetTVResponse>
) {
if(response.isSuccessful) {
val responseBody = response.body()
if(responseBody != null) {
onSuccess.invoke(responseBody.tvlist)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetTVResponse>, t: Throwable) {
onError.invoke()
}
})
}
fun getAiringTodayTV(page: Int = 1,
onSuccess: (tvlist: List<TV>) -> Unit,
onError: () -> Unit){
api.getAiringTodayTV(page = page)
.enqueue(object : Callback<GetTVResponse> {
override fun onResponse(
call: Call<GetTVResponse>,
response: Response<GetTVResponse>
) {
if(response.isSuccessful) {
val responseBody = response.body()
if(responseBody != null) {
onSuccess.invoke(responseBody.tvlist)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetTVResponse>, t: Throwable) {
onError.invoke()
}
})
}
fun getDiscoverTV(page: Int = 1,
onSuccess: (tvlist: List<TV>) -> Unit,
onError: () -> Unit){
api.getDiscoverTV(page = page)
.enqueue(object : Callback<GetTVResponse> {
override fun onResponse(
call: Call<GetTVResponse>,
response: Response<GetTVResponse>
) {
if(response.isSuccessful) {
val responseBody = response.body()
if(responseBody != null) {
onSuccess.invoke(responseBody.tvlist)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetTVResponse>, t: Throwable) {
onError.invoke()
}
})
}
3. Strings 추가
<string name="on_the_air">On The Air</string>
<string name="airing_today">Airing Today</string>
Movie와 다른 카테고리만 추가했습니다.
4. 카테고리 레이아웃 추가
fragment_home.xml 에 layout 을 추가해 줍니다.
<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_tv" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/top_rated_tv"
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/on_the_air"
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/on_the_air" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/on_the_air_tv"
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/airing_today"
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/airing_today" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/airing_today_tv"
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_tv"
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. HomeFragment.kt 에 카테고리별 실행 코드 구현
카테고리별 변수를 선언하고 TVAdapter 실행 코드를 구현합니다.
동일한 구조의 코드가 다섯개가 되었네요.
class HomeFragment : Fragment() {
lateinit var root : View
private lateinit var popularTV: RecyclerView
private lateinit var popularTVAdapter: TVAdapter
private lateinit var popularTVLayoutMgr: LinearLayoutManager
private var popularTVPage = 1
private lateinit var topRatedTV: RecyclerView
private lateinit var topRatedTVAdapter: TVAdapter
private lateinit var topRatedTVLayoutMgr: LinearLayoutManager
private var topRatedTVPage = 1
private lateinit var onTheAirTV: RecyclerView
private lateinit var onTheAirTVAdapter: TVAdapter
private lateinit var onTheAirTVLayoutMgr: LinearLayoutManager
private var onTheAirTVPage = 1
private lateinit var airingTodayTV: RecyclerView
private lateinit var airingTodayTVAdapter: TVAdapter
private lateinit var airingTodayTVLayoutMgr: LinearLayoutManager
private var airingTodayTVPage = 1
private lateinit var discoverTV: RecyclerView
private lateinit var discoverTVAdapter: TVAdapter
private lateinit var discoverTVLayoutMgr: LinearLayoutManager
private var discoverTVPage = 1
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? ): View? {
root = inflater.inflate(R.layout.fragment_home, container, false)
popularTV = root.findViewById(R.id.popular_tv)
popularTVLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
popularTV.layoutManager = popularTVLayoutMgr
popularTVAdapter = TVAdapter(mutableListOf()) { tv -> showTVDetails(tv) }
popularTV.adapter = popularTVAdapter
getPopularTV()
topRatedTV = root.findViewById(R.id.top_rated_tv)
topRatedTVLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
topRatedTV.layoutManager = topRatedTVLayoutMgr
topRatedTVAdapter = TVAdapter(mutableListOf()) { tv -> showTVDetails(tv) }
topRatedTV.adapter = topRatedTVAdapter
getTopRatedTV()
onTheAirTV = root.findViewById(R.id.on_the_air_tv)
onTheAirTVLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
onTheAirTV.layoutManager = onTheAirTVLayoutMgr
onTheAirTVAdapter = TVAdapter(mutableListOf()) { tv -> showTVDetails(tv) }
onTheAirTV.adapter = onTheAirTVAdapter
getOnTheAirTV()
airingTodayTV = root.findViewById(R.id.airing_today_tv)
airingTodayTVLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
airingTodayTV.layoutManager = airingTodayTVLayoutMgr
airingTodayTVAdapter = TVAdapter(mutableListOf()) { tv -> showTVDetails(tv) }
airingTodayTV.adapter = airingTodayTVAdapter
getAiringTodayTV()
discoverTV = root.findViewById(R.id.discover_tv)
discoverTVLayoutMgr = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
discoverTV.layoutManager = discoverTVLayoutMgr
discoverTVAdapter = TVAdapter(mutableListOf()) { tv -> showTVDetails(tv) }
discoverTV.adapter = discoverTVAdapter
getDiscoverTV()
return root
}
private fun getPopularTV() {
TVRepository.getPopularTV(
popularTVPage,
::onPopularTVFetched,
::onError
)
}
private fun onPopularTVFetched(tvlist: List<TV>) {
popularTVAdapter.appendTV(tvlist)
attachPopularTVOnScrollListener()
}
private fun attachPopularTVOnScrollListener() {
popularTV.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = popularTVLayoutMgr.itemCount
val visibleItemCount = popularTVLayoutMgr.childCount
val firstVisibleItem = popularTVLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
popularTV.removeOnScrollListener(this)
popularTVPage++
getPopularTV()
}
}
})
}
private fun getTopRatedTV() {
TVRepository.getTopRatedTV(
topRatedTVPage,
::onTopRatedTVFetched,
::onError
)
}
private fun onTopRatedTVFetched(tvlist: List<TV>) {
topRatedTVAdapter.appendTV(tvlist)
attachTopRatedTVOnScrollListener()
}
private fun attachTopRatedTVOnScrollListener() {
topRatedTV.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = topRatedTVLayoutMgr.itemCount
val visibleItemCount = topRatedTVLayoutMgr.childCount
val firstVisibleItem = topRatedTVLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
topRatedTV.removeOnScrollListener(this)
topRatedTVPage++
getTopRatedTV()
}
}
})
}
private fun getOnTheAirTV() {
TVRepository.getOnTheAirTV(
onTheAirTVPage,
::onOnTheAirTVFetched,
::onError
)
}
private fun onOnTheAirTVFetched(tvlist: List<TV>) {
onTheAirTVAdapter.appendTV(tvlist)
attachOnTheAirTVOnScrollListener()
}
private fun attachOnTheAirTVOnScrollListener() {
onTheAirTV.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = onTheAirTVLayoutMgr.itemCount
val visibleItemCount = onTheAirTVLayoutMgr.childCount
val firstVisibleItem = onTheAirTVLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
onTheAirTV.removeOnScrollListener(this)
onTheAirTVPage++
getOnTheAirTV()
}
}
})
}
private fun getAiringTodayTV() {
TVRepository.getAiringTodayTV(
airingTodayTVPage,
::onAiringTodayTVFetched,
::onError
)
}
private fun onAiringTodayTVFetched(tvlist: List<TV>) {
airingTodayTVAdapter.appendTV(tvlist)
attachAiringTodayTVOnScrollListener()
}
private fun attachAiringTodayTVOnScrollListener() {
airingTodayTV.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = airingTodayTVLayoutMgr.itemCount
val visibleItemCount = airingTodayTVLayoutMgr.childCount
val firstVisibleItem = airingTodayTVLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
airingTodayTV.removeOnScrollListener(this)
airingTodayTVPage++
getAiringTodayTV()
}
}
})
}
private fun getDiscoverTV() {
TVRepository.getDiscoverTV(
discoverTVPage,
::onDiscoverTVFetched,
::onError
)
}
private fun onDiscoverTVFetched(tvlist: List<TV>) {
discoverTVAdapter.appendTV(tvlist)
attachDiscoverTVOnScrollListener()
}
private fun attachDiscoverTVOnScrollListener() {
discoverTV.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val totalItemCount = discoverTVLayoutMgr.itemCount
val visibleItemCount = discoverTVLayoutMgr.childCount
val firstVisibleItem = discoverTVLayoutMgr.findFirstVisibleItemPosition()
if (firstVisibleItem + visibleItemCount >= totalItemCount / 2) {
discoverTV.removeOnScrollListener(this)
discoverTVPage++
getDiscoverTV()
}
}
})
}
private fun onError() {
Toast.makeText(activity, "error Movies", Toast.LENGTH_SHORT).show()
}
private fun showTVDetails(tv: TV) {
val intent = Intent(activity, MovieDetailsActivity::class.java)
intent.putExtra(MainActivity.MOVIE_BACKDROP, tv.backdrop_path)
intent.putExtra(MainActivity.MOVIE_POSTER, tv.poster_path)
intent.putExtra(MainActivity.MOVIE_TITLE, tv.name)
intent.putExtra(MainActivity.MOVIE_RATING, tv.rating)
intent.putExtra(MainActivity.MOVIE_RELEASE_DATE, tv.first_air_date)
intent.putExtra(MainActivity.MOVIE_OVERVIEW, tv.overview)
startActivity(intent)
}
}
6. HomeFragment를 재활용 했지만 그래도 TV로 표시되도록 일부 수정해 줍니다.
String을 아래와 같이 수정합니다.
<string name="title_home">TV</string>
Vector Asset 도 live tv 아이콘을 선택하여, ic_tv_black_24dp 이름으로 생성합니다.
bottom_nav_menu.xml 파일에서
ic_home_black_24dp 을 ic_tv_black_24dp 로 수정합니다.
실행해 보면 영화와 마찬가지로 잘 나옵니다.
MovieDetailsActivity를 그대로 사용하였기 때문에 상세화면으로도 잘 이동합니다.
다음은 검색 기능 API 를 사용하여 구성해 보겠습니다.
TMDB API 를 활용한 Android 앱 만들기
https://stockant.tistory.com/530
'프로그래밍 > 영화 TMDB API' 카테고리의 다른 글
영화 정보 앱 만들기 - TMDB API 사용, Overview, API 사용법 (1) | 2020.10.29 |
---|---|
영화 정보 앱 만들기 - TMDB API 사용, 검색 기능 (0) | 2020.10.27 |
영화 정보 앱 만들기 - TMDB API 사용, TV 정보 popular 추가 (0) | 2020.10.25 |
영화 정보 앱 만들기 - TMDB API 사용, 영화 상세 정보 화면 (0) | 2020.10.24 |
영화 정보 앱 만들기 - TMDB API 사용, 영화 카테고리 추가 (0) | 2020.10.22 |