MVP Retrofit RxJava RecyclerView Kullanımı

Merhaba arkadaşlar,
mobilhanem.com üzerinden anlattığımız/yayınladığımız derslere bugün sizlere MVP Retrofit RxJava Glide RecyclerView Kullanımı nı bir arada anlatan bir uygulama yapacağım. Bu derse başlamadan önce RecyclerView ve CardView kullanımı ile ilgili bu dersimizi incelemeniz faydalı olacaktır.

Öncelikle MVP den başlayalım. Android projelerimizin yapısını oluştururken çeşitli tasarım kalıplarına (Design Patterns) ihtiyaç duyarız. MVC, MVP, MVVM, MVI gibi bir çok tasarım kalıpları vardır. Tasarım kalıpları ile detaylı anlatım için bu linki inceleyebilirsiniz. MVP ( Model – View – Presenter ) ile asıl amaç kullanıcı arayüzü ile iş mantığını birbirinden ayırmaktır.

MVP Genel Yapısı

 

RecyclerView android uygulamalarımızda listeleme işlemlerini gerçekleştirebilmemiz için kullandığımız view öğesidir.

Retrofit açık kaynak kodlu oluşturulmuş rest istemcisidir. Yapacağımız uygulamada; belirli bir url adresinde API ile dışarı açılmış verileri çekip RecyclerView de listeleyeceğiz. İşte bu API ye istek atabilmeyi Retrofit kütüphanesi ile sağlıyoruz.

Asenkron işlemleri daha performanslı bir şekilde yönetebilmek ve reaktif programlama anlayışını daha basit bir şekilde özümseyebilmek için RxJava ya ihtiyaç duyarız.

Glide;  resim, gif ve video’ları kolayca uygulamamıza dahil edip, memory ve disk cache gibi mekanizmaları kendi içerisinde barındıran ve kolayca çözümler sunan; açık kaynak bir kütüphanedir.

Hepsinden kısaca bahsettiğimize göre şimdi örnek bir proje oluşturalım daha sonra build.gradle kısmında kullanacağımız kütüphaneleri ekleyelim.

Retrofit kütüphanesi için:

implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'

RxJava kütüphanesi için:

implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'

Glide kütüphanesi kullanımı için ise aşağıdaki kod parçalarını build.gradle da dependencies kısmına ekliyoruz.

implementation 'com.github.bumptech.glide:glide:4.9.0'

build.gradle dosyamızın son hali ise aşağıdaki gibi olacaktır.

build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.mobilhanem.rxjavamvpproject"
        minSdkVersion 16
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'

    // recyclerview and cardview
    implementation 'com.android.support:design:28.0.0'

    implementation 'com.android.support.constraint:constraint-layout:1.1.3'

    // retrofit
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.retrofit2:retrofit:2.5.0'

    // rxjava
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
    implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'

    // glide
    implementation 'com.github.bumptech.glide:glide:4.9.0'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

Daha sonra aşağıdaki gibi bir yapı oluşturmalıyız. Gördüğüz gibi çeşitli parçalara böldük ve paketler oluşturduk. Her bir paketin altına da ilgili dosyaları oluşturacağız. Böylece daha anlaşılır ve okunabilir bir paket yapısına sahip olduk.

İstek atacağımız api yi belirtmemiz gerekiyor bu yüzden Const adını verdiğimiz bir java sınıfı oluşturuyoruz. İçerisine de base url adresini yazıyoruz.

Const.java

package com.mobilhanem.rxjavamvpproject;

public class Const {

    public static final String BASE_URL = "https://api.androidhive.info/";

}

Api den dönen cevapta çeşitli filimler ve filimlere ait resimler bulunmaktadır. Bu filimlerden dönen cevapları tutabileceğimiz bir model sınıfına ihtiyaç var bu yüzden de Movie adını vereceğimiz bir java sınıfı oluşturuyoruz. Movie sınıfınıda yukarıda oluşturduğumuz model adını verdiğimiz paket içine atıyoruz.

Movie.java

package com.mobilhanem.rxjavamvpproject.model;

import com.google.gson.annotations.SerializedName;

import java.util.List;

public class Movie {

    @SerializedName("title")
    private String title;
    @SerializedName("image")
    private String image;
    @SerializedName("rating")
    private Double rating;
    @SerializedName("releaseYear")
    private Integer releaseYear;
    @SerializedName("genre")
    private List<String> genre = null;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public Double getRating() {
        return rating;
    }

    public void setRating(Double rating) {
        this.rating = rating;
    }

    public Integer getReleaseYear() {
        return releaseYear;
    }

    public void setReleaseYear(Integer releaseYear) {
        this.releaseYear = releaseYear;
    }

    public List<String> getGenre() {
        return genre;
    }

    public void setGenre(List<String> genre) {
        this.genre = genre;
    }

}

Movie leri listeleyeceğimiz için görsel olarak bir şablona ihtiyacımız var. movie_item adını verdiğimiz xml layout dosyamızı oluşturuyoruz.

movie_item.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    card_view:cardCornerRadius="4dp"
    android:layout_marginBottom="10dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:paddingTop="5dp"
        android:paddingBottom="5dp">

        <ImageView
            android:id="@+id/movieIcon"
            android:layout_marginRight="10dp"
            android:src="@drawable/ic_launcher_background"
            android:layout_width="90dp"
            android:layout_height="90dp" />

        <TextView
            android:id="@+id/movieTxt"
            android:text="asdasdasd"
            android:layout_centerVertical="true"
            android:gravity="center"
            android:layout_toRightOf="@+id/movieIcon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </RelativeLayout>

</android.support.v7.widget.CardView>

Listeleme yapabilmemiz için şimdi bir adapter sınıfına ihtiyacımız var ve MovieAdapter adını verdiğimiz adapter sınıfını oluşturuyoruz. Bu sınıfı da adapters paketi içine atıyoruz.  Aşağıda gördüğünüz gibi Adapter sınıfı işleyeceği veriyi alıyor. Movie tipinde verileri tutan listeden de filimin ismini TextView e, resmini de Glide kütüphanesi yardımı ile ImageView e set ediyoruz.

MovieAdapter.java

package com.mobilhanem.rxjavamvpproject.adapters;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.mobilhanem.rxjavamvpproject.R;
import com.mobilhanem.rxjavamvpproject.model.Movie;

import java.util.List;

public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MoviesHolder>  {

    List<Movie> movieList;
    Context context;

    public MoviesAdapter(List<Movie> movieList, Context context) {
        this.movieList = movieList;
        this.context = context;
    }

    @Override
    public MoviesHolder onCreateViewHolder(ViewGroup parent, int i) {
        View v = LayoutInflater.from(context).inflate(R.layout.movie_item,parent,false);
        MoviesHolder moviesHolder = new MoviesHolder(v);
        return moviesHolder;
    }

    @Override
    public void onBindViewHolder(MoviesHolder moviesHolder, int position) {

        moviesHolder.movieName.setText(movieList.get(position).getTitle());
        Glide.with(context).load(movieList.get(position).getImage()).apply(new RequestOptions()
                .centerCrop()).into(moviesHolder.movieImage);
    }

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

    public class MoviesHolder extends RecyclerView.ViewHolder {

        TextView movieName;
        ImageView movieImage;

        public MoviesHolder(View view) {
            super(view);
            movieName =  view.findViewById(R.id.movieTxt);
            movieImage = view.findViewById(R.id.movieIcon);
        }
    }
}

Adapter sınıfımızı da oluşturduğumuza göre şimdi MVP yapısını oluşturabilmemiz için IMovieContract adını verdiğimiz bir interface oluşturuyoruz. Aşağıda gördüğünüz gibi View katmanına ait interface bloğu ve Presenter katmanına ait interface bloğu yer almaktadır. interface blokları içinde de ilgili methodlarımız bulunmaktadır.

IMovieContract.java

package com.mobilhanem.rxjavamvpproject.contract;

import com.mobilhanem.rxjavamvpproject.model.Movie;

import java.util.List;

public interface IMovieContract {

     interface View {

        void init();
        void showProgress();
        void hideProgress();
        void showError(String errorMsg);
        void loadList(List<Movie> movieList);

    }

     interface Presenter {
        void start();
        void fetchMovies();
    }
}

Şimdi IMovieService adını verdiğimiz bir interface sınıfını oluşturuyoruz ve services paketi içine atıyoruz. Aşağıdaki kod yapısını incelediğimizde; yukarıda belirttiğimiz base url e get methodu ile istek atıyoruz ve istek atarkende url in kalan kısmını belirtiyoruz. getMovieList methodu çağırıldıktan sonrada Observable sonucunu dönüyoruz.

IMovieService.java

package com.mobilhanem.rxjavamvpproject.services;

import com.mobilhanem.rxjavamvpproject.model.Movie;

import java.util.List;

import io.reactivex.Observable;
import retrofit2.http.GET;

public interface IMovieService {
    @GET("json/movies.json")
    Observable<List<Movie>> getMovieList();
}

MoviePresenter adını verdiğimiz java sınıfını oluşturuyoruz ve daha önce oluşturduğumuz Presenter dan implements ediyoruz. Constructor methodunda dışarıdan bir view aldığını görüyorsunuz. Presenter burada view e bağımlı ve yukarıda anlattığım model view presenter yapısınıda daha belirgin bir şekilde oluşturuş oluyoruz. fetchMovies methodunda ise; Rxjava ile Retrofit bileşenlerini bir arada kullanıyoruz. onNext methodunda ise api den istek atıp da dönen sonucu gösterebilmek için view in loadList methoduna gönderiyoruz. Bu istek attığımız sırada sunucudan cevap dönene kadar kullanıcıya bir Progress çıkarması gerektiğini belirtiyoruz işte o işlem ui yani arayüzü ilgilendirdiği için yarattığımız view interface içinde yer alan methodları kullanarak gerekli tanımlamaları yapıyoruz.

MoviePresenter.java

package com.mobilhanem.rxjavamvpproject.presenter;


import com.mobilhanem.rxjavamvpproject.Const;
import com.mobilhanem.rxjavamvpproject.contract.IMovieContract;
import com.mobilhanem.rxjavamvpproject.model.Movie;
import com.mobilhanem.rxjavamvpproject.services.IMovieService;

import java.util.List;

import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

public class MoviePresenter implements IMovieContract.Presenter {

    IMovieContract.View mView;

    public MoviePresenter(IMovieContract.View view)
    {
        this.mView = view;
    }

    @Override
    public void start() {

        mView.init();

    }

    @Override
    public void fetchMovies() {

        IMovieService iMovieService = new Retrofit.Builder()
                .baseUrl(Const.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build().create(IMovieService.class);

        iMovieService.getMovieList()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(new Observer<List<Movie>>() {
                               @Override
                               public void onSubscribe(Disposable d) {

                                   mView.showProgress();
                               }

                               @Override
                               public void onNext(List<Movie> movieList) {

                                   mView.hideProgress();
                                   mView.loadList(movieList);
                               }

                               @Override
                               public void onError(Throwable e) {
                                   mView.showError(e.toString());
                               }

                               @Override
                               public void onComplete() {

                               }
                           }
                );
    }
}

Şimdi MainActivity nin layout xml kısmını oluşturalım;

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ProgressBar
        android:id="@+id/progressBar"
        android:visibility="gone"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />


</RelativeLayout>

Son olarak MainActivity sınıfını oluşturalım. Aşağıdaki kod yapısına baktığımızda gayet sade ve okunaklı bir yapı oluştuğunu göreceksiniz. İlgili activity sınıfımızda satırlarca işlem yok MVP sayesinde view ile uygulama logic ini birbirinden ayırdık. MoviePresenter sınıfımızı yarattık daha sonrasında da gerekli işlemleri çalıştırdık. Kaynak kodu indirip çalıştırdığınızda ve debug yaptığınızda çalışma mantığının nasıl olduğunu daha iyi anlayacaksınız.

MainActivity.java

package com.mobilhanem.rxjavamvpproject;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.mobilhanem.rxjavamvpproject.adapters.MoviesAdapter;
import com.mobilhanem.rxjavamvpproject.contract.IMovieContract;
import com.mobilhanem.rxjavamvpproject.model.Movie;
import com.mobilhanem.rxjavamvpproject.presenter.MoviePresenter;

import java.util.List;

public class MainActivity extends AppCompatActivity implements IMovieContract.View {

    private RecyclerView recyclerView;
    private MoviesAdapter moviesAdapter;
    private ProgressBar progressBar;

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

        moviePresenter = new MoviePresenter(MainActivity.this);
        moviePresenter.start();
    }

    @Override
    public void init() {

        recyclerView = findViewById(R.id.recyclerview);
        progressBar = findViewById(R.id.progressBar);

        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(linearLayoutManager);
        moviePresenter.fetchMovies();

    }

    @Override
    public void showProgress() {

        progressBar.setVisibility(View.VISIBLE);

    }

    @Override
    public void hideProgress() {

        if(progressBar!=null && progressBar.isShown()){
            progressBar.setVisibility(View.GONE);
        }

    }

    @Override
    public void showError(String errorMsg) {

        Toast.makeText(MainActivity.this, ""+errorMsg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void loadList(List<Movie> movieList) {

        moviesAdapter = new MoviesAdapter(movieList,getApplicationContext());
        recyclerView.setAdapter(moviesAdapter);
    }
}

Ekran çıktısı:

Evet arkadaşlar MVP Retrofit RxJava Glide RecyclerView Kullanımını temel bir proje yaparak sizlere anlatmaya çalıştım. Bu kavramları bir arada kullanmak tam anlamıyla ne işe yaradıklarını daha iyi kavrayabilmemizi sağlayacaktır. Rxjava gerçekten çok kapsamlı bir konu ayrıntılı olarak değinmedim sadece temel bir kullanım yaparak sizlere göstermeye çalıştım. Umarım herkese faydalı olur. Bir sonraki derste de Dagger konusunu anlatacağım ve bu proje üzerinde Dagger in nasıl kullanılacağını göstereceğim.

Tüm Android Ders, Proje ve Kaynak Kodlar için tıklayınız.

Mobilhanem.com üzerinden anlattığımız android uygulama geliştirme derslerine devam edeceğiz. Konu hakkında sorunuzu yorum alanından sorabilirsiniz. Konu dışı sorularınızı ve tüm yazılımsal sorularınızı sorucevap.mobilhanem.com sitemizden de sorabilirsiniz.

Bir dahaki dersimizde görüşmek dileğiyle..

5

Alper Beyler

Yüksek Lisans: Çankaya Üniversitesi / Bilgisayar Mühendisliği
Lisans: Çankaya Üniversitesi / Bilgisayar Mühendisliği (4/3.30) (2010-2014)
Lisans : Viyana Teknik Üniversitesi / Bilgisayar Bilimleri (2013)

4 Yorum

Haftalık Bülten

Mobilhanem'de yayınlanan dersleri haftalık mail almak ister misiniz?