참고한 사이트
최근에 영화를 보고
영화 API를 이용하여서 영화 순위를 볼 수 있는 App 만들어 API 연습을하면 좋겠다고 생각하여 만들어보았다.
우선,
영화진흥위원회에 접속을 하여
회원가입 후
"키 발급/관리" 창을 들어가 Key값을 받아준다.
그럼 위와 같은 KEY값이 발급되었다.
API를 호출하여 데이터를 다루는 안내는 아래 링크에 걸어두겠다.
(링크가 아닌 경우 파일을 다운 받으면 파일 안에 정보가 적혀있다.)
안드로이드 스튜디오에서 프로젝트를 하나 생성해준다.
우선 액티비티에 RecyclerView
를 통해 데이터들을 표현해주어야하므로 activity_main.xml에 RecyclerView
를 선언해주고 Button
뷰를 하나 배치해준다.
RecyclerView
와 Retrofit2
를 사용하기 위해서는 Gradle에 아래 코드를 추가해준다.
implementation 'com.android.support:recyclerview-v7:28.0.0' //recyclerview implementation 'com.squareup.retrofit2:retrofit:2.7.2' //retrofit2 implementation 'com.squareup.retrofit2:converter-gson:2.7.2'
서버에 통신하여 정보를 받아 사용할 것이므로 AndroidManifest.xml에 인터넷 접근 권한 허용을 해주어야한다.
아래 코드를 추가해준다.
<uses-permission android:name="android.permission.INTERNET"/> <application android:usesCleartextTraffic="true" ...>
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_recyclerview" android:layout_width="match_parent" android:layout_height="0dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" android:orientation="horizontal" app:layout_constraintBottom_toTopOf="@+id/button" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="RecyclerView show" android:onClick="click_btn" app:layout_constraintBottom_toBottomOf="parent" tools:layout_editor_absoluteX="111dp" /> </androidx.constraintlayout.widget.ConstraintLayout>
위 와 같은 액티비티 화면이 만들어진다.
테마 색상은 여담으로
한 때 메가박스 아르바이트를 했었고 지금도 자주 애용해서 메가박스의 로고 색으로 테마색을 맞췄다.
(위에 타이틀은 보통 넣지 않지만 포트폴리오를 작성해야하므로 넣었습니다.)
이제 RecyclerView
에 표시될 데이터들을 하나씩 어떻게 표현해줄지 recyclerview_item을 생성하여 만들어줄 것이다.
지금 만드는 건 데이터 하나를 어떤식으로 표현해줄지를 만드는 것이다.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="500dp" xmlns:tools="http://schemas.android.com/tools"> <TextView android:id="@+id/tv_rank" android:layout_width="30dp" android:layout_height="30dp" android:background="#332666" android:gravity="center" android:textColor="#FFFFFF" android:textSize="24sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.429" /> <TextView android:id="@+id/tv_movieNm" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:textStyle="bold" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_rank" app:layout_constraintVertical_bias="0.598" tools:text="movieName" /> <TextView android:id="@+id/tv_openDt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="14sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_movieNm" app:layout_constraintVertical_bias="0.736" tools:text="2020-01-01" /> </androidx.constraintlayout.widget.ConstraintLayout>
포스터를 넣어주고 싶었지만 포스터를 담은 API를 찾지 못해 뷰 안에 순위를 넣기로 했다.
TextView
는 위에서부터 [ 순위, 영화 제목, 개봉 날짜 ]를 표시해줄 것이다.
RecyclerView
와 앞에 만든 recyclerview_item의 뷰들을 연결해주기 위해 어댑터를 만들어준다.
package my.app.moviemovie; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.Map; public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { // 받은 딕셔너리 리스트들을 담을 배열 변수 선언 private ArrayList<Map<String, Object>> items= new ArrayList<Map<String, Object>>(); // 받은 데이터를 옮겨 담아준다. public MyAdapter(ArrayList<Map<String, Object>> resultList){ this.items=resultList; } //데이터가 연결 된 리스트를 반환. @NonNull @Override public MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item , parent, false); return new MyViewHolder(itemView); } // 데이터들의 순서(position)에 맞는 데이터들끼리 `MyViewHolder`로 전달 @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { Map<String, Object> item = items.get(position); holder.setItem(item); } // 받은 데이터들의 갯수를 반환. @Override public int getItemCount() { return items.size(); } // `MyViewHolder` ->`RecyclerView`에 있는 뷰들을 연결. // `onBindViewHolder`에서 받은 값을 뷰들에 연결. public static class MyViewHolder extends RecyclerView.ViewHolder { public TextView tvRank, tvMovieNm, tvOpenDt; public MyViewHolder(View itemView) { super(itemView); tvRank=itemView.findViewById(R.id.tv_rank); tvMovieNm=itemView.findViewById(R.id.tv_movieNm); tvOpenDt=itemView.findViewById(R.id.tv_openDt); } public void setItem(Map<String, Object> item){ //정보들을 확인하기 위해 로그를 찍어봄. Log.d("json Info. " , item.toString()); //"rank", "movieNm", "openDt"은 Json파일에 저장되어 있던 key값 tvRank.setText(item.get("rank").toString()); tvMovieNm.setText(item.get("movieNm").toString()); tvOpenDt.setText(item.get("openDt").toString()); } } }
package my.app.moviemovie; import java.util.Map; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Header; import retrofit2.http.Path; import retrofit2.http.Query; public interface RetrofitInterface { // @GET은 Http Method로, [ GET / POST / PUT / DELETE / HEAD ] 중 무슨 작업인지 표현 // ()안에는 URI에서 URL을 제외한 End Point(URI) @GET("/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json") Call<Map<String,Object>> //메소드명 getBoxOffice //@Query에서 String key에 해당하는 부분이 key에 값을 대입. (@Query("key") String key, @Query("targetDt") String targetDt); } }
package my.app.moviemovie; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.RecyclerView; import android.content.Context; import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.Button; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Date; import java.util.Map; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class MainActivity extends AppCompatActivity { private final String TAG = this.getClass().getSimpleName(); private RecyclerView recyclerView; private RecyclerView.Adapter mAdapter; private String baseUrl = "http://www.kobis.or.kr"; private String API_KEY = "발급받은 API키 값을 넣어주세요."; Retrofit retrofit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 입력한 날짜를 기준으로 현재 상영작 중 순위를 알려주는데, 입력한 날짜를 자동으로 현재 날짜로 맞춰놓으려고 선언 LocalDate now = LocalDate.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); String formatedNow = Integer.toString(Integer.parseInt(now.format(formatter))-1); // 로그에 날짜 찍어보기 Log.d("current-Date :", formatedNow); recyclerView = findViewById(R.id.rv_recyclerview); // 자바 객체를 json으로 표현해주기 위해 GSON 사용 retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build(); RetrofitInterface retrofitInterface = retrofit.create(RetrofitInterface.class); retrofitInterface.getBoxOffice(API_KEY, formatedNow).enqueue(new Callback<Map<String, Object>>() { public void onResponse(Call<Map<String, Object>> call, Response<Map<String, Object>> response) { //'boxOfficeResult'에서 딕셔너리를 가져와 저장. Map<String, Object> boxOfficeResult = (Map<String, Object>) response.body().get("boxOfficeResult"); //'boxOfficeResult'중 'dailyBoxOfficeList'라는 키를 가진 딕셔너리를 리스트로 저장 후, 그 리스트들을 어댑터에 보내 어댑터와 연관된 값을 가지는 값들을 뽑아준다. ArrayList<Map<String, Object>> jsonList = (ArrayList) boxOfficeResult.get("dailyBoxOfficeList"); mAdapter = new MyAdapter(jsonList); } @Override public void onFailure(Call<Map<String, Object>> call, Throwable throwable) { } }); } //버튼을 누르면 값들이 `recyclerview`에 뿌려지도록한다. public void click_btn(View view) { recyclerView.setAdapter(mAdapter); } }
사이트에서 제공해주는 정보를 살펴보면,
'boxOfficeResult'를 KEY값으로 하여 그 안에 또 딕셔너리가 들어있다. 그 안에서 'dailyBoxOfficeList'를 키로 가지는 딕셔너리들 중 원하는 값들을 뽑으면 된다.
나는 RecyclerView
를 Horizontal
값으로 설정하여 리스트들을 수평으로 넘기면서 볼 수 있다.