본문 바로가기
Android

안드로이드 스튜디오, JAVA] 실습 : 레트로핏, 리사이클러뷰, 피카소를 이용한 영화 앱 만들기.

by 김마리님 2020. 7. 31.

사용할 포스팅 : 

https://itstudy-mary.tistory.com/222

 

안드로이드 스튜디오, JAVA 실습 : 영화 API 사이트에서 데이터 가져오기

이용하는 개념 포스팅 : https://itstudy-mary.tistory.com/221 안드로이드 스튜디오, JAVA] Retrofit2를 이용하여 데이터 통신하기 https://square.github.io/retrofit/ Retrofit A type-safe HTTP client for An..

itstudy-mary.tistory.com

 

결과화면 :

 

(연결될 의존성)

앱 수준의 build.gradle :

    implementation 'com.squareup.picasso:picasso:2.71828'
    implementation 'com.squareup.retrofit2:retrofit:2.1.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'

    implementation 'com.google.android.material:material:1.2.0-alpha02'
    implementation 'com.makeramen:roundedimageview:2.3.0'
    compileOnly 'org.projectlombok:lombok:1.18.10'
    annotationProcessor 'org.projectlombok:lombok:1.18.10'

 

먼저 이 리사이클러 뷰로 돌릴 하나의 카드를 제작한다.

 

card_item.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    android:id="@+id/frame_card"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.cardview.widget.CardView
            android:id="@+id/cardView"
            android:layout_margin="20dp"
            android:layout_width="match_parent"
            android:layout_height="120dp"
            app:cardCornerRadius="10dp"
            app:cardElevation="20dp">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <TextView
                    android:id="@+id/tv_title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="16dp"
                    android:layout_marginEnd="16dp"
                    android:textStyle="bold"
                    android:text="벤자민 버튼의 시간은 거꾸로 간다."
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintHorizontal_bias="1.0"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <TextView
                    android:id="@+id/tv_rating"
                    android:layout_width="29dp"
                    android:layout_height="23dp"
                    android:layout_marginTop="12dp"
                    android:text="9.9"
                    app:layout_constraintEnd_toEndOf="@+id/tv_title"
                    app:layout_constraintTop_toBottomOf="@+id/tv_title" />

                <RatingBar
                    android:id="@+id/rating_bar"
                    style="?android:attr/ratingBarStyleIndicator"
                    android:layout_width="186dp"
                    android:layout_height="36dp"
                    android:layout_marginTop="28dp"
                    android:numStars="5"
                    app:layout_constraintEnd_toEndOf="@+id/tv_rating"
                    app:layout_constraintTop_toTopOf="@+id/tv_rating" />

            </androidx.constraintlayout.widget.ConstraintLayout>

        </androidx.cardview.widget.CardView>

    </RelativeLayout>

    <RelativeLayout

        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <com.makeramen.roundedimageview.RoundedImageView
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/iv_poster"
            android:src="@drawable/main"
            android:layout_marginLeft="40dp"
            android:layout_marginTop="10dp"
            android:layout_width="110dp"
            android:layout_height="120dp"
            android:scaleType="fitXY"
            app:riv_corner_radius="20dip"
            app:riv_border_width="2dip"
            app:riv_border_color="#FFFFFF"
            app:riv_mutate_background="true"
            app:riv_tile_mode="clamp" />

    </RelativeLayout>


</FrameLayout>

 

그리고 툴바는 커스텀 툴바를 이용한다.

toolbar_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
    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="wrap_content"
    android:id="@+id/toolbar"
    android:elevation="20dp"
    app:contentInsetStart="0dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:layout_marginLeft="10dp"
            android:id="@+id/menu_Icon"
            android:layout_width="45dp"
            android:layout_height="40dp"
            android:src="@drawable/ic_dehaze"
            android:scaleType="fitXY"
            android:layout_centerVertical="true"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:textSize="25sp"
            android:textStyle="bold"
            android:text="Movie"
            android:gravity="center_vertical|center_horizontal" />

        <ImageView
            android:layout_marginRight="10dp"
            android:id="@+id/search_icon"
            android:layout_width="45dp"
            android:layout_height="40dp"
            android:src="@drawable/ic_search"
            android:scaleType="fitXY"
            android:layout_centerVertical="true"
            android:layout_alignParentRight="true"/>

    </RelativeLayout>

</androidx.appcompat.widget.Toolbar>

 

이제 메인화면에 리사이클러 뷰와 툴바를 붙인다.

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

  <include layout="@layout/toolbar_main"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rc_card"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#F5F3F3"/>

</LinearLayout>

 

먼저, 레트로핏을 이용해 데이터를 가져온다.

 

데이터를 가지고 올 모델 클래스를 만든다

 

YtsData.java

package com.mary.movieapp;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class YtsData {
    private String status;
    private String status_message;
    private MyData data;

    @Data
    public class MyData {
        private int movie_count;
        private int limit;
        private int page_number;
        private List<Movie> movies;

        @Data
        public class Movie {
            private String title;
            private float rating;
            private String medium_cover_image;


        }

        @Override
        public String toString() {
            return "MyData{" +
                    "movie_count=" + movie_count +
                    ", limit=" + limit +
                    ", page_number=" + page_number +
                    ", movies=" + movies +
                    '}';
        }
    }

    @Override
    public String toString() {
        return "YtsData{" +
                "status='" + status + '\'' +
                ", status_message='" + status_message + '\'' +
                ", data=" + data +
                '}';
    }
}

package com.mary.movieapp;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class YtsData {
    private String status;
    private String status_message;
    private MyData data;

    @Data
    public class MyData {
        private int movie_count;
        private int limit;
        private int page_number;
        private List<Movie> movies;

        @Data
        public class Movie {
            private String title;
            private float rating;
            private String medium_cover_image;


        }

        @Override
        public String toString() {
            return "MyData{" +
                    "movie_count=" + movie_count +
                    ", limit=" + limit +
                    ", page_number=" + page_number +
                    ", movies=" + movies +
                    '}';
        }
    }

    @Override
    public String toString() {
        return "YtsData{" +
                "status='" + status + '\'' +
                ", status_message='" + status_message + '\'' +
                ", data=" + data +
                '}';
    }
}

 

다음 연결할 인터페이스를 생성한다.

 

YtsService.java

package com.mary.movieapp;

import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;
import retrofit2.http.Query;
import retrofit2.http.QueryMap;

public interface YtsService {

    @GET("list_movies.json")
    Call<YtsData> 영화목록가져오기(
            @Query("sort_by") String sort_by,
            @Query("limit") int limit,
            @Query("page") int page
    );

    public static final Retrofit retrofit=new Retrofit.Builder()
            .baseUrl("https://yts.mx/api/v2/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
}

 

이전 데이터에서는 데이터를 가져오는 자원 리소스를 메인에 설정했지만, 여기서는 가독성을 위해 따로 빼서 제작했다.

 

MainActivity.java

package com.mary.movieapp;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main_Activity";

    private Context mContext=MainActivity.this;
    private ImageView ivImage;
    private TextView tvTitie, tvScore;
    private YtsAdapter cardAdapter=new YtsAdapter();
    private RecyclerView.LayoutManager layoutManager;
    private RecyclerView rcCard;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        init();
        initDownload();
        listener();
    }

    private void init(){
        ivImage=findViewById(R.id.iv_poster);
        tvTitie=findViewById(R.id.tv_title);
        tvScore=findViewById(R.id.tv_rating);

        rcCard=findViewById(R.id.rc_card);
        layoutManager=new LinearLayoutManager(mContext,RecyclerView.VERTICAL,false);
        rcCard.setLayoutManager(layoutManager);
    }

    private YtsData initDownload(){
        YtsService ytsService=YtsService.retrofit.create(YtsService.class);
        Call<YtsData> call= ytsService.영화목록가져오기("rating",10,1);
        call.enqueue(new Callback<YtsData>() {
            @Override
            public void onResponse(Call<YtsData> call, Response<YtsData> response) {
                if(response.isSuccessful()==true) {
                    YtsData ytsData = response.body();
                    Log.d(TAG, "onResponse: "+ytsData);
                    //리사이클러 뷰에 여기서 연결하기

                    rcCard.setAdapter(cardAdapter);
                    cardAdapter.addCardModel(ytsData.getData().getMovies());
                }

            }

            @Override
            public void onFailure(Call<YtsData> call, Throwable t) {
                Log.d(TAG, "onFailure: "+t);
                Toast.makeText(mContext, "다운로드 실패", Toast.LENGTH_SHORT).show();
            }
        });

        return null;
    }

 

담아온 데이터를 돌려줄 리사이클러뷰 어댑터를 제작한다.

 

YtsAdapter.java

package com.mary.movieapp;

import android.media.Rating;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RatingBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.squareup.picasso.Picasso;

import java.util.ArrayList;
import java.util.List;

public class YtsAdapter extends RecyclerView.Adapter<YtsAdapter.CardView> {
    private static final String TAG = "YtsAdapter";

    private List<YtsData.MyData.Movie> cardModels = new ArrayList<>();

    public void addCardModel(YtsData.MyData.Movie movie) {
        cardModels.add(movie);
    }

    public void addCardModel(List<YtsData.MyData.Movie> movies) {
        cardModels = movies;
    }

    @NonNull
    @Override
    public CardView onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.card_item, parent, false);
        return new CardView(view);
    }

    @Override
    public void onBindViewHolder(@NonNull CardView holder, int position) {
        Log.d(TAG, "onBindViewHolder: "+position);
        holder.setCard(cardModels.get(position));
    }

    @Override
    public int getItemCount() {
        return cardModels.size();
    }

    public static class CardView extends RecyclerView.ViewHolder {

        private TextView tvTitle, tvScore;
        private ImageView ivImage;
        private RatingBar ratingBar;

        public CardView(@NonNull View itemView) {
            super(itemView);
            tvTitle = itemView.findViewById(R.id.tv_title);
            tvScore = itemView.findViewById(R.id.tv_rating);
            ivImage = itemView.findViewById(R.id.iv_poster);
            ratingBar = itemView.findViewById(R.id.rating_bar);

        }

        public void setCard(YtsData.MyData.Movie movie) {
            tvTitle.setText(movie.getTitle());
            tvScore.setText(movie.getRating() + "");
            Picasso.get().load(movie.getMedium_cover_image()).into(ivImage);
            ratingBar.setRating(movie.getRating() / 2);
        }

    }
}

 

반응형