20231031_TIL

이상훈·2023년 10월 30일

TIL

목록 보기
70/83

최종 프로젝트

AndroidManifest.xml

# build.gradle.kts(:app) implementation("com.android.support:recyclerview-v7:27.1.1") implementation("com.github.bumptech.glide:glide:3.7.0") implementation("com.squareup.okhttp3:okhttp:3.11.0") implementation("com.google.code.gson:gson:2.8.5")

themes.xml

<!-- 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" />

list_item.xml

<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"/>

my_menu.xml

<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"/>

activity_detail.xml

<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>

Movie.kt

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
)

MyViewAdapter.kt

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)
}

}

MainActivity.kt

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
    }
}

}

DetailActivity.kt

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
    }
}

}

Youtube.kt

package com.example.moviedatabase

data class Youtube(
val id: String,
val key: String,
val name: String,
val size: String
)

MyAsyncTask.kt

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) // 비동기 작업 시작
}

Android 기술면접 질문 답변하기

11. enum은 무엇인가요?

enum이란 enumerated type의 줄임말로 열거형이라고 부르기도 하는데 컴퓨터 프로그래밍에서 열거형(enumerated type, enumeration)은 요소, 멤버라 불리는 명명된 값의 집합을 이루는 자료형이다.

17. 오버로딩과 오버라이딩의 차이

오버로딩은 같은 이름의 메서드 여러개를 가지면서 매개변수의 유형과 개수가 다르도록 하는 것을 의미한다. 오버라이딩은 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의해서 사용하는 것을 의미한다.

15. 접근제어자는 어떤게 있을까요

접근 제어자는 public, protected, default, private로 구성되어 각각의 역할을 한다. default(기본값) : 동일 패키지 내에서만 접근 가능. 접근 제한 범위에 순으로 나열하면, public > protected > default > private 순이다.

16. 패키지는 무엇일까요?

패키지란 코드를 구조화하고 관리하는 데 사용되는 논리적인 단위이다. 패키지는 클래스, 함수, 변수 등의 식별자들을 그룹화하여 코드의 유지 보수성과 가독성을 높일 수 있다. 패키지는 점(.)으로 구분된 계층 구조를 가질 수 있으며, 반드시 식별자 이름이 전체 패키지 경로를 반영해야 한다.

25. data class는 무엇일까요

데이터 클래스(Data Class)는 데이터를 다루는데 최적화된 클래스로 equals(), hashCode(), toString(), copy(), componentN() 5가지 유용한 함수들을 내부적으로 자동으로 생성해준다. 이 함수들은 코딩에서 캡슐화를 위해 필수적이지만 자바에서 사용할 때 코드를 생성해줘야하는 번거로움이 있다.

26. object 키워드로 할수 있는것

  1. 객체 선언(Object Declaration)
    객체 선언은 싱클턴 패턴을 쉽게 구현할 수 있게 해준다. 싱글턴 패턴이란 디자인 패턴 중 하나로, 객체가 단 하나 존재하는 클래스를 말한다. 이런 클래스가 필요한 경우 객체 선언을 통해 쉽게 구현할 수 있다. Example1에서는 클래스이자 단일 객체인 Score 하나로 학생들의 성적을 관리하는 경우를 다뤘다.

객체 선언은 클래스 정의하는 방법에서 ‘class’ 키워드를 ‘object’로 바꾼 것 뿐이다.

  1. 객체 식(Object Expression)
    무명 객체 반환(Returning Anonymous object)
    객체 ‘식’이라는 이름에서 볼 수 있듯이, 객체 식은 객체를 반환한다. 여기서 객체는 이름이 없는 무명(Anonymous) 객체이다. object 키워드로 만들 객체도 클래스를 상속 받을 수 있고 인터페이스를 구현할 수 있다고 하였다. 추상 클래스나 인터페이스를 구현할 때, object 키워드를 사용하면 클래스 따로 정의하지 않아도 간편하게 구현을 할 수 있다. 객체 식을 정의하면 추상 클래스나 인터페이스를 구현한 객체가 생성되고, 이 객체를 변수에 저장하여 재사용하는 것도 가능하다.

익명의 내부 클래스 한 개는 여러 개의 추상 클래스나 인터페이스를 구현할 수 있고, 익명 객체를 포함하는 함수나 클래스의 프로퍼티에도 접근이 가능하고 var 프로퍼티의 경우 값을 변경할 수 있다.

  1. 동반 객체(Companion Object)
    동반 객체(companion object) 정의하기
    일반적으로 클래스에 정의한 메서드, 프로퍼티는 생성한 객체마다 다른 값을 가질 수 있다. 그러나 어떤 경우에는 하나의 클래스에 속하는 객체들이 각각 다른 값을 갖는 것이 아닌 같은 값을 공유하도록 의도할 수 있다. 동반 객체는 그 경우에 사용될 수 있다. 즉, 하나의 클래스가 갖는 동반 객체(companion object)에는 그 클래스의 모든 객체가 공유하는 메서드, 프로퍼티를 정의할 수 있다. 동반 객체는 클래스 내부에 ‘companion object’라는 키워드를 사용하여 정의할 수 있고, 내부에는 메서드, 프로퍼티와 초기화블록이 올 수 있으나, 생성자는 만들 수 없다.

12. when 은 어떨때 사용하나요?

when은 조건에 따라 다른 동작을 수행할 때 사용하는 구문입니다. 자바의 switch 문과 유사하게, 여러 조건을 나열하고, 해당 조건이 만족될 때 수행할 동작을 정의할 수 있습니다.

42. 안드로이드에서 사이즈가 큰 이미지를 불러오려고 합니다. 어떤 방법을 사용할 수 있을까요?

큰 이미지를 처리할 때는 Glide 또는 Picasso와 같은 이미지 로딩 라이브러리를 사용하는 것이 좋습니다. 라이브러리들은 이미지의 메모리 캐싱, 리사이징 등을 자동으로 처리해줍니다.

21. 코틀린의 interface와 자바의 Interface 차이

코틀린의 interface는 자바의 Interface와 유사하게 인터페이스를 정의할 수 있지만, 코틀린에서는 인터페이스에 메서드 구현을 제공할 수 있습니다. 이는 자바에서는 default 메서드를 통해 가능하지만, 코틀린에서는 더 간결한 문법으로 제공됩니다.

41. Retrofit이란 무엇인가요?

Retrofit은 안드로이드 및 자바에서 사용되는 HTTP 클라이언트 라이브러리입니다. REST API와 통신하기 위한 코드를 쉽게 작성할 수 있도록 도와주며, 요청/응답 데이터를 객체로 쉽게 변환할 수 있습니다.

profile
열심히 하자

0개의 댓글