<!-- Base application theme. -->
<style name="Base.Theme.MovieDataBase" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#FF6200EE</item>
<item name="colorPrimaryDark">#062342</item>
<item name="colorAccent">#6bb2ff</item>
</style>
# activity_main.xml
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<!--RecyclerView-->
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:scrollbars="vertical" />
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"/>
<item
android:id="@+id/action_search"
android:icon="@android:drawable/ic_menu_search"
android:title="Search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always"/>
<!-- Settings, should always be in the overflow -->
<item android:id="@+id/action_settings"
android:title="설정"
app:showAsAction="never"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_poster"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_title"
android:textSize="20dp"
android:paddingLeft="8dp"
android:paddingTop="8dp"
android:textColor="@color/colorAccent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_original_title"
android:paddingLeft="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_release_date"
android:paddingLeft="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_overview"
android:paddingLeft="8dp"
android:paddingTop="8dp"
android:textSize="16dp"
android:layout_marginBottom="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.youtube.player.YouTubePlayerView
android:id="@+id/youtube_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"></com.google.android.youtube.player.YouTubePlayerView>
</LinearLayout>
</ScrollView>
package com.example.moviedatabase
data class Movie(
val title: String,
val originalTitle: String,
val posterPath: String,
val overview: String,
val backdropPath: String,
val releaseDate: String
)
package com.example.moviedatabase
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
class MyRecyclerViewAdapter(private val context: Context, private val itemList: ArrayList) :
RecyclerView.Adapter<MyRecyclerViewAdapter.RecyclerViewHolders>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolders {
val view = inflater.inflate(R.layout.list_item, parent, false)
return RecyclerViewHolders(view)
}
override fun onBindViewHolder(holder: RecyclerViewHolders, position: Int) {
// 포스터만 출력하자.
val url = "https://image.tmdb.org/t/p/w500${itemList[position].posterPath}"
Glide.with(context)
.load(url)
.centerCrop()
.into(holder.imageView)
}
override fun getItemCount(): Int {
return itemList.size
}
inner class RecyclerViewHolders(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.imageView)
}
}
package com.example.moviedatabase
import android.app.ProgressDialog
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.lang.Exception
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: MyRecyclerViewAdapter
private val movieList = ArrayList<Movie>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recycler_view)
// Asynctask - OKHttp
val mAsyncTask = MyAsyncTask()
mAsyncTask.execute()
// LayoutManager
recyclerView.layoutManager = GridLayoutManager(this, 2)
}
inner class MyAsyncTask : AsyncTask<String, Void, Array<Movie>?>() {
// 로딩중 표시
private val progressDialog: ProgressDialog by lazy { ProgressDialog(this@MainActivity) }
override fun onPreExecute() {
super.onPreExecute()
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER)
progressDialog.setMessage("\t로딩중...")
// show dialog
progressDialog.show()
}
override fun doInBackground(vararg strings: String): Array<Movie>? {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.themoviedb.org/3/movie/upcoming?api_key=<your api key>&language=ko-KR&page=1")
.build()
try {
val response: Response = client.newCall(request).execute()
val gson = Gson()
val parser = JsonParser()
val rootObject: JsonElement = parser.parse(response.body()?.charStream())
.asJsonObject["results"]
return gson.fromJson(rootObject, Array<Movie>::class.java)
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
override fun onPostExecute(result: Array<Movie>?) {
super.onPostExecute(result)
progressDialog.dismiss()
// ArrayList에 차례대로 집어 넣는다.
result?.let {
movieList.addAll(it)
}
// 어댑터 설정
adapter = MyRecyclerViewAdapter(this@MainActivity, movieList)
recyclerView.adapter = adapter
}
}
}
package com.example.moviedatabase
import android.content.Intent
import android.os.Bundle
import android.os.AsyncTask
import android.os.Bundle
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
import androidx.appcompat.app.AppCompatActivity
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import kotlinx.android.synthetic.main.activity_detail.*
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
class DetailActivity : YouTubeBaseActivity() {
private val youtubeList = ArrayList<Youtube>()
private lateinit var youTubeView: YouTubePlayerView
private lateinit var trailer01: String
private lateinit var m_id: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
val intent: Intent = intent
m_id = intent.getStringExtra("id")
val title: String? = intent.getStringExtra("title")
val original_title: String? = intent.getStringExtra("original_title")
val poster_path: String? = intent.getStringExtra("poster_path")
val overview: String? = intent.getStringExtra("overview")
val release_date: String? = intent.getStringExtra("release_date")
tv_title.text = title
tv_original_title.text = original_title
Glide.with(this)
.load(poster_path)
.centerCrop()
.into(iv_poster)
tv_overview.text = overview
tv_release_date.text = release_date
// Asynctask
val mProcessTask = YoutubeAsyncTask()
mProcessTask.execute(m_id)
}
fun playVideo(videoId: String, youTubePlayerView: YouTubePlayerView) {
// Initialize YouTube player view
youTubePlayerView.initialize("Your API key",
object : YouTubePlayer.OnInitializedListener {
override fun onInitializationSuccess(
provider: YouTubePlayer.Provider,
youTubePlayer: YouTubePlayer,
wasRestored: Boolean
) {
youTubePlayer.cueVideo(videoId)
}
override fun onInitializationFailure(
provider: YouTubePlayer.Provider,
youTubeInitializationResult: YouTubeInitializationResult
) {
}
})
}
inner class YoutubeAsyncTask : AsyncTask<String, Void, Array<Youtube>?>() {
override fun onPostExecute(youtubes: Array<Youtube>?) {
super.onPostExecute(youtubes)
// Add items to the ArrayList
youtubes?.let {
youtubeList.addAll(it)
}
// Use the YouTube view to display the video
if (youtubeList.isNotEmpty()) {
trailer01 = youtubeList[0].key
youTubeView = findViewById(R.id.youtube_view)
playVideo(trailer01, youTubeView)
}
}
override fun doInBackground(vararg strings: String): Array<Youtube>? {
val m_id = strings[0]
val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.themoviedb.org/3/movie/$m_id/videos?api_key=your api key")
.build()
try {
val response: Response = client.newCall(request).execute()
val gson = Gson()
val parser = JsonParser()
val rootObject: JsonElement = parser.parse(response.body()?.charStream())
.asJsonObject["results"]
return gson.fromJson(rootObject, Array<Youtube>::class.java)
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
}
}
package com.example.moviedatabase
data class Youtube(
val id: String,
val key: String,
val name: String,
val size: String
)
package com.example.moviedatabase
import android.app.ProgressDialog
import android.os.AsyncTask
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonParser
class MyAsyncTask : AsyncTask<String, Void, Array?>() {
private val progressDialog: ProgressDialog by lazy { ProgressDialog(MainActivity@this) }
override fun onPreExecute() {
super.onPreExecute()
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER)
progressDialog.setMessage("로딩중...")
progressDialog.show()
// 목록 배열의 내용을 클리어
movieList.clear()
}
override fun doInBackground(vararg strings: String): Array<Movie>? {
Log.d("AsyncTask", "url : " + strings[0])
val client = OkHttpClient()
val request = Request.Builder()
.url(strings[0])
.build()
try {
val response: Response = client.newCall(request).execute()
val gson = Gson()
val parser = JsonParser()
val rootObject: JsonElement = parser.parse(response.body()?.charStream())
.asJsonObject["results"]
return gson.fromJson(rootObject, Array<Movie>::class.java)
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
override fun onPostExecute(result: Array<Movie>?) {
super.onPostExecute(result)
progressDialog.dismiss()
// ArrayList에 차례대로 집어 넣기
result?.let {
movieList.addAll(it)
}
// 어댑터 설정 및 갱신
adapter = MyRecyclerViewAdapter(MainActivity@this, movieList)
recyclerView.adapter = adapter
adapter.notifyDataSetChanged()
}
}
// MainActivity 내에서 MyAsyncTask를 실행하는 코드
GlobalScope.launch(Dispatchers.IO) {
val asyncTask = MyAsyncTask()
asyncTask.execute(url) // 비동기 작업 시작
}
enum이란 enumerated type의 줄임말로 열거형이라고 부르기도 하는데 컴퓨터 프로그래밍에서 열거형(enumerated type, enumeration)은 요소, 멤버라 불리는 명명된 값의 집합을 이루는 자료형이다.
오버로딩은 같은 이름의 메서드 여러개를 가지면서 매개변수의 유형과 개수가 다르도록 하는 것을 의미한다. 오버라이딩은 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의해서 사용하는 것을 의미한다.
접근 제어자는 public, protected, default, private로 구성되어 각각의 역할을 한다. default(기본값) : 동일 패키지 내에서만 접근 가능. 접근 제한 범위에 순으로 나열하면, public > protected > default > private 순이다.
패키지란 코드를 구조화하고 관리하는 데 사용되는 논리적인 단위이다. 패키지는 클래스, 함수, 변수 등의 식별자들을 그룹화하여 코드의 유지 보수성과 가독성을 높일 수 있다. 패키지는 점(.)으로 구분된 계층 구조를 가질 수 있으며, 반드시 식별자 이름이 전체 패키지 경로를 반영해야 한다.
데이터 클래스(Data Class)는 데이터를 다루는데 최적화된 클래스로 equals(), hashCode(), toString(), copy(), componentN() 5가지 유용한 함수들을 내부적으로 자동으로 생성해준다. 이 함수들은 코딩에서 캡슐화를 위해 필수적이지만 자바에서 사용할 때 코드를 생성해줘야하는 번거로움이 있다.
객체 선언은 클래스 정의하는 방법에서 ‘class’ 키워드를 ‘object’로 바꾼 것 뿐이다.
익명의 내부 클래스 한 개는 여러 개의 추상 클래스나 인터페이스를 구현할 수 있고, 익명 객체를 포함하는 함수나 클래스의 프로퍼티에도 접근이 가능하고 var 프로퍼티의 경우 값을 변경할 수 있다.
when은 조건에 따라 다른 동작을 수행할 때 사용하는 구문입니다. 자바의 switch 문과 유사하게, 여러 조건을 나열하고, 해당 조건이 만족될 때 수행할 동작을 정의할 수 있습니다.
큰 이미지를 처리할 때는 Glide 또는 Picasso와 같은 이미지 로딩 라이브러리를 사용하는 것이 좋습니다. 라이브러리들은 이미지의 메모리 캐싱, 리사이징 등을 자동으로 처리해줍니다.
코틀린의 interface는 자바의 Interface와 유사하게 인터페이스를 정의할 수 있지만, 코틀린에서는 인터페이스에 메서드 구현을 제공할 수 있습니다. 이는 자바에서는 default 메서드를 통해 가능하지만, 코틀린에서는 더 간결한 문법으로 제공됩니다.
Retrofit은 안드로이드 및 자바에서 사용되는 HTTP 클라이언트 라이브러리입니다. REST API와 통신하기 위한 코드를 쉽게 작성할 수 있도록 도와주며, 요청/응답 데이터를 객체로 쉽게 변환할 수 있습니다.