TV 정보 popular 추가
완성하면 아래 앱이 됩니다.
https://play.google.com/store/apps/details?id=com.enigmah2k.movieinfo
TMDB는 영화 정보 외에 TV 정보도 제공합니다.
영화 정보 페이지와 동일하게 구성하면 됩니다.
복습하는 차원에서 TV 정보 화면을 구현해 보겠습니다.
이미 만들어져 있는 HomeFragment 를 사용하겠습니다.
1. TV.kt 생성
TV 정보를 담을 class를 만듭니다.
common 폴더에서 마우스 오른쪽 클릭을 하여 TV class 를 생성합니다.
소스코드는 아래와 같이 작성했습니다.
data class TV (
@SerializedName("id") val id : Long,
@SerializedName("name") val name : String,
@SerializedName("overview") val overview : String,
@SerializedName("poster_path") val poster_path: String,
@SerializedName("backdrop_path") val backdrop_path: String,
@SerializedName("vote_average") val rating: Float,
@SerializedName("vote_count") val vcount: Long,
@SerializedName("first_air_date") val first_air_date: String,
@SerializedName("popularity") val prating: Float
) {}
Movie와 차이점은 title 을 name으로, release_date 를 first_air_date 로 변경했습니다.
https://developers.themoviedb.org/3/tv/get-popular-tv-shows
get-popular-tv-shows 의 TV 정보 결과 값입니다.
@SerializedName 는 응답받은 JSON KEY 값과 다른 이름으로 변수를 사용하고 싶을 때 사용합니다.
2. 응답을 받는 Class 생성. GetTVResponse.kt
개별 TV 정보 Class는 만들었고, 서버 응답 전체를 저장하는 Class를 만들겠습니다.
common 폴더에서 마우스 오른쪽 클릭하여 GetTVResponse.kt 파일을 생성합니다.
소스코드는 아래와 같이 작성했습니다.
data class GetTVResponse (
@SerializedName("page") val page: Int,
@SerializedName("results") val tvlist: List<TV>,
@SerializedName("totla_pages") val pages: Int,
@SerializedName("totla_results") val results: Int
) {}
get-popular-tv-shows 의 결과인 Responses 내용 전체를 묶었습니다.
results 값들을 List로 받습니다.
3. Api interface 에 TV관련 함수 추가
tv popular 함수를 아래와 같이 추가합니다.
@GET("tv/popular")
fun getPopularTV(
@Query("api_key") apiKey: String = "348eefabae0631d8003f24551c45a05c",
@Query("page") page : Int,
@Query("language") language : String = "ko,en-US"
): Call<GetTVResponse>
아래와 같이 필요한 파라메터 값을 @Query 로 전달합니다.
region값이 없지만 우리가 만든 API는 movie와 동일합니다.
4. 실행 함수 생성 - TVRepository .kt
Api에 정의된 함수를 직접 실행하는 Object 를 만들겠습니다.
common 폴더에서 마우스 오른쪽 클릭을 하여 New > Kotlin File/Class 를 선택합니다.
아래와 같이 Object 를 선택하고 TVRepository 를 입력하여 TVRepository .kt 파일을 생성합니다.
아래와 같이 초기화를 합니다.
private val api: Api //인터페이스 구현
init {
val retrofit = Retrofit.Builder()
.baseUrl("https://api.themoviedb.org/3/")
.addConverterFactory(GsonConverterFactory.create())
.build()
api = retrofit.create(Api::class.java)
}
Retrofit 을 Builder객체로 만듭니다.
baseUrl 은 "https://api.themoviedb.org/3/" 로 설정합니다.
JSON 데이터 객체를 사용하기 위해 GsonConverterFactory 를 사용합니다.
아래와 같이 실행 함수를 구현합니다.
fun getPopularTV(page: Int = 1,
onSuccess: (tvlist: List<TV>) -> Unit,
onError: () -> Unit ) {
TVRepository.api.getPopularTV(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()
}
})
}
Movie popular 함수를 복사하여 TV 용으로 수정하였습니다.
5. TV 관련 String을 추가합니다.
영어로만 사용한 다른 String은 그대로 사용하겠습니다.
<string name="most_popular_tv">가장 인기있는 TV</string>
<string name="most_toprated_tv">최고 평점 TV</string>
6. fragment_home.xml 에 layout 추가
<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_tv" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/popular_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>
</ScrollView>
7. 개별 TV 정보 layout 추가
Movie와 동일한 layout 입니다.
item_tv.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_tv_poster"
android:layout_width="match_parent"
android:layout_height="172dp" />
<TextView
android:id="@+id/item_tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title" />
</LinearLayout>
</androidx.cardview.widget.CardView>
8. TV Adapter 를 추가합니다.
home 폴더에서 마우스 오른쪽 클릭을 하여 TVAdapter를 추가합니다.
소스 코드는 아래와 같이 구성하였습니다.
Movie 파일에서 TV 용으로 수정하였습니다.
class TVAdapter (var tvlist: MutableList<TV>, var onTVClick: (tv: TV) -> Unit
) : RecyclerView.Adapter<TVAdapter.TvViewHolder>(){
inner class TvViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val poster: ImageView = itemView.findViewById(R.id.item_tv_poster)
fun bind(tv: TV) {
Glide.with(itemView)
.load("https://image.tmdb.org/t/p/w342${tv.poster_path}")
.transform(CenterCrop())
.into(poster)
itemView.item_tv_title.text = tv.name
itemView.setOnClickListener { onTVClick.invoke(tv) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TvViewHolder {
val view = LayoutInflater
.from(parent.context)
.inflate(R.layout.item_tv, parent, false)
return TvViewHolder(view)
}
override fun getItemCount(): Int = tvlist.size
override fun onBindViewHolder(holder: TvViewHolder, position: Int) {
holder.bind(tvlist[position])
}
fun appendTV(tvlist: List<TV>) {
this.tvlist.addAll(tvlist)
notifyItemRangeInserted(
this.tvlist.size,
tvlist.size - 1
)
}
}
9. HomeFragment.kt 파일에 호출 함수를 주가합니다.
TV 정보를 기존 HomeFragment 에 사용하겠습니다.
소스 코드는 아래와 같이 구성하였습니다.
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
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()
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 onError() {
Toast.makeText(activity, "error Movies", Toast.LENGTH_SHORT).show()
}
private fun showTVDetails(tv: TV) {
}
}
10. 상세 화면 이동
보여주는 정보가 Movie와 동일하므로 MovieDetailsActivity.kt를 호출하는 것을 그대로 사용해 보겠습니다.
showTVDetails() 함수를 아래와 같이 구성합니다.
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)
}
TV 상세 화면을 다르게 구성하려면 Extra Name 과 TVDetailsActivity.kt 를 새로 만들어 사용하면 되겠습니다.
실행해 보면 아래와 같이 잘 나타납니다.
상세화면
다음은 Top rated, TV On the Air, TV Airing Today API를 사용하여 카테고리를 구성해 보겠습니다.
TMDB API 를 활용한 Android 앱 만들기
https://stockant.tistory.com/530
'프로그래밍 > 영화 TMDB API' 카테고리의 다른 글
영화 정보 앱 만들기 - TMDB API 사용, 검색 기능 (0) | 2020.10.27 |
---|---|
영화 정보 앱 만들기 - TMDB API 사용, TV 카테고리 추가 (0) | 2020.10.26 |
영화 정보 앱 만들기 - TMDB API 사용, 영화 상세 정보 화면 (0) | 2020.10.24 |
영화 정보 앱 만들기 - TMDB API 사용, 영화 카테고리 추가 (0) | 2020.10.22 |
영화 정보 앱 만들기 - TMDB API 사용, 인기 영화 정보 가져오기 2 (1) | 2020.10.22 |