코리아 IT아카데미/android
15일차 | 지금까지 한 것 다 합한 예제
Sharon kim
2022. 3. 7. 18:24
//https://eva.design/
https://yts.lt/api/v2/list_movies.json?limit=20&page=1&sort_by=rating
https://www.jsonschema2pojo.org/
Eva Design System
Open Source Code Libraries No need to spend thousands of hours for implementation, testing and maintenance. Eva is supported by well known Angular and React Native UI component libraries. More to come.
eva.design
jsonschema2pojo
Reference properties For each property present in the 'properties' definition, we add a property to a given Java class according to the JavaBeans spec. A private field is added to the parent class, along with accompanying accessor methods (getter and sette
www.jsonschema2pojo.org
코드 추가
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
build.gradle
buildTypes {
...
buildFeatures{
// dataBinding true
viewBinding {
enabled true
}
}
}
def retrofit_version ="2.9.0"
dependencies {
...
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'de.hdodenhof:circleimageview:3.1.0'
}
Adapter>
MovieAdapter
package com.example.ytsmovie.Adapter;
import android.annotation.SuppressLint;
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 androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.FitCenter;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.example.ytsmovie.models.Movie;
import com.example.ytsmovie.R;
import java.util.ArrayList;
import java.util.List;
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MyViewHolder> {
private List<Movie> list = new ArrayList<>();
private static final String TAG = MovieAdapter.class.getName();
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View movieItemView = inflater.inflate(R.layout.item_movie_card, parent, false);
return new MyViewHolder(movieItemView); //R.id.layout.item_movie_card
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
Movie movie = list.get(position);
holder.setItem(movie);
}
@Override
public int getItemCount() {
return list.size();
}
//내부 클래스
public static class MyViewHolder extends RecyclerView.ViewHolder {
ImageView posterIv;
TextView titleTv;
TextView ratingTv;
RatingBar ratingBar;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
posterIv = itemView.findViewById(R.id.posterIv);
titleTv = itemView.findViewById(R.id.titleTV);
ratingTv = itemView.findViewById(R.id.ratingTV);
ratingBar = itemView.findViewById(R.id.ratingBar);
}
public void setItem(Movie movie) {
titleTv.setText(movie.getTitle());
ratingTv.setText(String.valueOf(movie.getRating()));
Glide.with(posterIv.getContext())
.load(movie.getMediumCoverImage())
.transform(new FitCenter(), new RoundedCorners(20))
.placeholder(R.drawable.round_image)
.into(posterIv);
Log.d(TAG, "movie.getRating() :" + movie.getRating());
ratingBar.setRating(movie.getRating().floatValue());
}
}
//통신으로 데이터가 전달되면 여기 메서드로 데이터를 전달 받게 한다.
@SuppressLint("NotifyDataSetChanged")
public void addItems(List<Movie> list) {
this.list = list;
this.notifyDataSetChanged();
}
}
models>
Data
Meta
Movie
YtsData
package com.example.ytsmovie.models;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class Data {
@SerializedName("movie_count")
@Expose
private Integer movieCount;
@SerializedName("limit")
@Expose
private Integer limit;
@SerializedName("page_number")
@Expose
private Integer pageNumber;
@SerializedName("movies")
@Expose
private List<Movie> movies = null;
public Integer getMovieCount() {
return movieCount;
}
public void setMovieCount(Integer movieCount) {
this.movieCount = movieCount;
}
public Integer getLimit() {
return limit;
}
public void setLimit(Integer limit) {
this.limit = limit;
}
public Integer getPageNumber() {
return pageNumber;
}
public void setPageNumber(Integer pageNumber) {
this.pageNumber = pageNumber;
}
public List<Movie> getMovies() {
return movies;
}
public void setMovies(List<Movie> movies) {
this.movies = movies;
}
}
package com.example.ytsmovie.models;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Meta {
@SerializedName("server_time")
@Expose
private Integer serverTime;
@SerializedName("server_timezone")
@Expose
private String serverTimezone;
@SerializedName("api_version")
@Expose
private Integer apiVersion;
@SerializedName("execution_time")
@Expose
private String executionTime;
public Integer getServerTime() {
return serverTime;
}
public void setServerTime(Integer serverTime) {
this.serverTime = serverTime;
}
public String getServerTimezone() {
return serverTimezone;
}
public void setServerTimezone(String serverTimezone) {
this.serverTimezone = serverTimezone;
}
public Integer getApiVersion() {
return apiVersion;
}
public void setApiVersion(Integer apiVersion) {
this.apiVersion = apiVersion;
}
public String getExecutionTime() {
return executionTime;
}
public void setExecutionTime(String executionTime) {
this.executionTime = executionTime;
}
}
package com.example.ytsmovie.models;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class Movie {
@SerializedName("id")
@Expose
private Integer id;
@SerializedName("url")
@Expose
private String url;
@SerializedName("imdb_code")
@Expose
private String imdbCode;
@SerializedName("title")
@Expose
private String title;
@SerializedName("title_english")
@Expose
private String titleEnglish;
@SerializedName("title_long")
@Expose
private String titleLong;
@SerializedName("slug")
@Expose
private String slug;
@SerializedName("year")
@Expose
private Integer year;
@SerializedName("rating")
@Expose
private Float rating;
@SerializedName("runtime")
@Expose
private Integer runtime;
@SerializedName("genres")
@Expose
private List<String> genres = null;
@SerializedName("summary")
@Expose
private String summary;
@SerializedName("description_full")
@Expose
private String descriptionFull;
@SerializedName("synopsis")
@Expose
private String synopsis;
@SerializedName("yt_trailer_code")
@Expose
private String ytTrailerCode;
@SerializedName("language")
@Expose
private String language;
@SerializedName("mpa_rating")
@Expose
private String mpaRating;
@SerializedName("background_image")
@Expose
private String backgroundImage;
@SerializedName("background_image_original")
@Expose
private String backgroundImageOriginal;
@SerializedName("small_cover_image")
@Expose
private String smallCoverImage;
@SerializedName("medium_cover_image")
@Expose
private String mediumCoverImage;
@SerializedName("large_cover_image")
@Expose
private String largeCoverImage;
@SerializedName("state")
@Expose
private String state;
@SerializedName("date_uploaded")
@Expose
private String dateUploaded;
@SerializedName("date_uploaded_unix")
@Expose
private Integer dateUploadedUnix;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getImdbCode() {
return imdbCode;
}
public void setImdbCode(String imdbCode) {
this.imdbCode = imdbCode;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitleEnglish() {
return titleEnglish;
}
public void setTitleEnglish(String titleEnglish) {
this.titleEnglish = titleEnglish;
}
public String getTitleLong() {
return titleLong;
}
public void setTitleLong(String titleLong) {
this.titleLong = titleLong;
}
public String getSlug() {
return slug;
}
public void setSlug(String slug) {
this.slug = slug;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Float getRating() {
return rating;
}
public void setRating(Float rating) {
this.rating = rating;
}
public Integer getRuntime() {
return runtime;
}
public void setRuntime(Integer runtime) {
this.runtime = runtime;
}
public List<String> getGenres() {
return genres;
}
public void setGenres(List<String> genres) {
this.genres = genres;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getDescriptionFull() {
return descriptionFull;
}
public void setDescriptionFull(String descriptionFull) {
this.descriptionFull = descriptionFull;
}
public String getSynopsis() {
return synopsis;
}
public void setSynopsis(String synopsis) {
this.synopsis = synopsis;
}
public String getYtTrailerCode() {
return ytTrailerCode;
}
public void setYtTrailerCode(String ytTrailerCode) {
this.ytTrailerCode = ytTrailerCode;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getMpaRating() {
return mpaRating;
}
public void setMpaRating(String mpaRating) {
this.mpaRating = mpaRating;
}
public String getBackgroundImage() {
return backgroundImage;
}
public void setBackgroundImage(String backgroundImage) {
this.backgroundImage = backgroundImage;
}
public String getBackgroundImageOriginal() {
return backgroundImageOriginal;
}
public void setBackgroundImageOriginal(String backgroundImageOriginal) {
this.backgroundImageOriginal = backgroundImageOriginal;
}
public String getSmallCoverImage() {
return smallCoverImage;
}
public void setSmallCoverImage(String smallCoverImage) {
this.smallCoverImage = smallCoverImage;
}
public String getMediumCoverImage() {
return mediumCoverImage;
}
public void setMediumCoverImage(String mediumCoverImage) {
this.mediumCoverImage = mediumCoverImage;
}
public String getLargeCoverImage() {
return largeCoverImage;
}
public void setLargeCoverImage(String largeCoverImage) {
this.largeCoverImage = largeCoverImage;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getDateUploaded() {
return dateUploaded;
}
public void setDateUploaded(String dateUploaded) {
this.dateUploaded = dateUploaded;
}
public Integer getDateUploadedUnix() {
return dateUploadedUnix;
}
public void setDateUploadedUnix(Integer dateUploadedUnix) {
this.dateUploadedUnix = dateUploadedUnix;
}
}
package com.example.ytsmovie.models;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class YtsData {
@SerializedName("status")
@Expose
private String status;
@SerializedName("status_message")
@Expose
private String statusMessage;
@SerializedName("data")
@Expose
private Data data;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatusMessage() {
return statusMessage;
}
public void setStatusMessage(String statusMessage) {
this.statusMessage = statusMessage;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
}
repository>
MovieService
package com.example.ytsmovie.repository;
import com.example.ytsmovie.models.YtsData;
import com.example.ytsmovie.Utils.Define;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;
import retrofit2.http.Query;
//https://yts.lt/api/v2/list_movies.json?limit=20&page=1&sort_by=rating
public interface MovieService {
//public static final이 생략되어 있음
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Define.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
//dto 만들어 주기
@GET("list_movies.json")
Call<YtsData> repoContributors(@Query("sort_by") String sortBy,
@Query("page") int page,
@Query("limit") int limit);
}
Utils>
Define
FragmentType
package com.example.ytsmovie.Utils;
public class Define {
public static String BASE_URL = "https://yts.lt/api/v2/";
}
package com.example.ytsmovie.Utils;
//상수값
public enum FragmentType {
MOVIE, INFO
}
InfoFragment
package com.example.ytsmovie;
import android.icu.text.IDNA;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.ytsmovie.databinding.FragmentInfoBinding;
import com.example.ytsmovie.databinding.FragmentMovieBinding;
public class InfoFragment extends Fragment {
//view Binding
private FragmentInfoBinding binding;
//싱글톤 패턴 적용
private static InfoFragment infoFragment;
public InfoFragment() {
// Required empty public constructor
}
public static InfoFragment getInstance() {
if (infoFragment == null){
infoFragment = new InfoFragment();
}
return infoFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentInfoBinding.inflate(inflater, container, false);
return binding.getRoot();
}
}
MainActivity
package com.example.ytsmovie;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import android.os.Bundle;
import com.example.ytsmovie.Utils.FragmentType;
import com.example.ytsmovie.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
//1. viewBind 사용하기
private ActivityMainBinding binding;
private static final String TAG = MainActivity.class.getName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//뷰 바인딩 설정 방법 (Activity)
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
initData();
addBottomNavigationListener();
replaceFragment(FragmentType.MOVIE);
}
private void initData() {
}
private void replaceFragment(FragmentType type){
Fragment fragment;
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
if (type == FragmentType.MOVIE) {
fragment = MovieFragment.getInstance();
} else {
fragment = InfoFragment.getInstance();
}
transaction.replace(binding.mainContainer.getId(), fragment);
transaction.commit();
// 매니저
// 트랜젝션
}
private void addBottomNavigationListener(){
binding.bottomNavigation.setOnItemSelectedListener(item -> {
switch (item.getItemId()){
case R.id.page_1:
replaceFragment(FragmentType.MOVIE);
break;
case R.id.page_2:
replaceFragment(FragmentType.INFO);
break;
}
return true;
});
}
}
MovieFragment
package com.example.ytsmovie;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.ytsmovie.Adapter.MovieAdapter;
import com.example.ytsmovie.models.Movie;
import com.example.ytsmovie.models.YtsData;
import com.example.ytsmovie.databinding.FragmentMovieBinding;
import com.example.ytsmovie.repository.MovieService;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MovieFragment extends Fragment {
private static final String TAG = MovieFragment.class.getName();
// view Binding
private FragmentMovieBinding binding;
// 싱글톤 패턴
private static MovieFragment movieFragment;
private MovieAdapter movieAdapter;
private LinearLayoutManager linearLayoutManager;
private MovieService service;
private MovieFragment() {
// Required empty public constructor
}
public static MovieFragment getInstance() {
if (movieFragment == null) {
movieFragment = new MovieFragment();
}
return movieFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
service = MovieService.retrofit.create(MovieService.class);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 플래그먼트에서 view binding 사용 방법
binding = FragmentMovieBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initRecyclerView();
requestMoviesData();
}
private void initRecyclerView() {
//1. 어댑터 필요
//2. 매니저 필요
//3. 세팅
movieAdapter = new MovieAdapter();
linearLayoutManager = new LinearLayoutManager(getContext());
binding.movieRecyclerView.setAdapter(movieAdapter);
binding.movieRecyclerView.setLayoutManager(linearLayoutManager);
binding.movieRecyclerView.hasFixedSize();
}
private void requestMoviesData() {
service.repoContributors("rating", 1, 10)
.enqueue(new Callback<YtsData>() {
@Override
public void onResponse(Call<YtsData> call, Response<YtsData> response) {
Log.d(TAG, "status code" + response.code());
if (response.isSuccessful()){
YtsData ytsData = response.body();
List<Movie> list = ytsData.getData().getMovies();
movieAdapter.addItems(ytsData.getData().getMovies());
}else{
Log.d(TAG, response.errorBody().toString());
}
}
@Override
public void onFailure(Call<YtsData> call, Throwable t) {
Log.d(TAG, t.getMessage());
}
});
}
}
drawable
아이콘 3개 생성
menu>
bottom_navigation_menu
top_app_bar
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/page_1"
android:icon="@drawable/ic_baseline_movie_creation_24"
android:title="@string/str_movie" />
<item
android:id="@+id/page_2"
android:icon="@drawable/ic_baseline_info_24"
android:title="@string/str_info" />
</menu>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/favorite"
android:icon="@drawable/ic_baseline_favorite_24"
android:title="@string/str_favorite"
app:showAsAction="ifRoom"/>
</menu>
layout>
activity_main
fragment_info
fragment_movie
item_movie_card
<?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">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/topAppBar"
style="@style/Widget.MaterialComponents.Toolbar.Primary"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/top_app_bar"
app:title="@string/app_name" />
<LinearLayout
android:id="@+id/mainContainer"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@id/bottomNaviContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/topAppBar" />
<LinearLayout
android:id="@+id/bottomNaviContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/mainContainer"
tools:layout_height="48dp">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.BottomNavigationView.Colored"
app:menu="@menu/bottom_navigation_menu" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/info"
tools:context=".InfoFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MovieFragment">
<!-- 리사이클러뷰 선언-->
<androidx.recyclerview.widget.RecyclerView
tools:listitem="layout/item_movie_card"
android:orientation="vertical"
android:id="@+id/movieRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="20dp"
android:backgroundTint="@color/danger"
app:cardCornerRadius="10dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline1"
android:layout_width="1dp"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_begin="135dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="1dp"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_end="16dp" />
<TextView
android:id="@+id/titleTV"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="end"
android:maxLines="2"
android:textColor="@color/black"
app:layout_constraintBottom_toTopOf="@id/ratingTV"
app:layout_constraintEnd_toEndOf="@id/guideline2"
app:layout_constraintStart_toStartOf="@id/guideline1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.7"
app:layout_constraintVertical_chainStyle="packed"
tools:text="벤자민 버튼의 시간은 거꾸로 간다 " />
<TextView
android:id="@+id/ratingTV"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="end"
android:text="9.9"
app:layout_constraintBottom_toTopOf="@id/ratingBar"
app:layout_constraintEnd_toEndOf="@id/guideline2"
app:layout_constraintStart_toStartOf="@id/guideline1"
app:layout_constraintTop_toBottomOf="@id/titleTV" />
<RatingBar
android:id="@+id/ratingBar"
style="?ratingBarStyleSmall"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:numStars="10"
tools:rating="1"
android:stepSize="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/guideline2"
app:layout_constraintStart_toStartOf="@id/guideline1"
app:layout_constraintTop_toBottomOf="@id/ratingTV" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</RelativeLayout>
<ImageView
android:id="@+id/posterIv"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginStart="35dp"
android:layout_marginTop="10dp"
android:scaleType="fitCenter"
android:src="@drawable/round_image" />
</FrameLayout>