Genel

42 Android Programlama – RecyclerView Nedir ?

Merhaba Bugün ki yazımızda Recycler view ile ilgili bir ders yapacağız. Haftaya jsoup ile birleştirerek örnek bir uygulamasını göstereceğiz.

Şimdi recycler view nedir ?

ListView’in ileri bir versiyonu olan recycler view, list view’e göre performans bakımından bir çok yararı vardır. Listeli verileri göstermek için listview ‘leri kullanıyorsanız Lolipop 5 sürümü ile gelen ve kullanımı Listview’e benzeyen bir widget ile karşınızdayız. Daha düşük sürümlerde ise support v7 kütüphanesi kullanılabilir.

Daha Detaylı Bilgi için :

//developer.android.com/reference/android/support/v7/widget/RecyclerView.html

//developer.android.com/training/material/lists-cards.html

RecyclerView vs ListView

  • ListView’i implement ederken daha performanslı çalışması için ViewHolder kullanmışınızdır. Böylelikle daha akıcı ve performanslı bir liste görünümü sağlamış oluruz. RecyclerView’da ise ViewHolder kullanımı zorunlu kılınmıştır.
  • ListView doğası gereği dikey olarak kullanılmaktadır. Bazı workaround’lar ya da custom yapılar ile yatay olarak da kullanabilirsiniz. İşte bu durumda karşımıza LayoutManager sınıfı çıkıyor. Bu sınıf ile Linear (her zamanki alışkın olduğumuz şekilde listeler), StaggaredGrid (Pinterest usülü) ve Grid (resim galerisi gibi) tipinde listeler oluşturabilirsiniz. Dediğim gibi bu şekilde yapıları zaten oluşturabiliyorduk fakat bunun için custom kütüphaneler kullanmamız gerekiyordu.
  • ListView ile animasyon oluşturmak için yine custom yapılar ile animasyonlar yapabiliyorduk. RecyclerView ile artık tamamen yeni bir sınıf olan ItemAnimator sınıfını kullanabileceğiz. Bunun için örnek vermek gerekirse Gmail uygulamasında silme, seçme, çoklu mail seçme işlemlerinde kullanılan animasyonları verebiliriz. Ayrıca Gmail uygulaması RecyclerView için verilebilecek en güzel örnektir.
  • ListView ile çalışırken heralde Adapter’ı duymayanınız yoktur. RecyclerView’de ise Adapter sınıfı biraz daha genişletilerek kullanabiliyoruz.
  • ListView oluşturduğumuz da itemlar arasında çizgi ya da ayırıcı (divider) olduğunu görüyoruz. Bunu istersek kaldırabiliriz ya da farklı bir şekilde tasarlayabiliriz. Bu ise RecyclerView’da default olarak görünmez şekilde geliyor.
  • ListView’da OnItemClickListener işlevini OnTouchItemListener ile kullanacağız.

Hadi RecyclerView oluşturalım.

Liste id’sine sahip recyclerview oluşturduk.

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

Activity’de çağırmak için;
RecyclerView rv = (RecyclerView)findViewById(R.id.liste);

Not : Eğer Recyclerview’in boyutunu değiştirmeyecekseniz performansı arttırmak için boyutunu sabitleyebilirsiniz.
rv.setHasFixedSize(true);

LayoutManager Nedir ve LayoutManager Oluşturma

LayoutManager Recyclerview da öğelerin yerleşimini yapmamız için gereklidir, Kendi layoutManager’imizi RecyclerView.LayoutManager’i extend ederek kullanabiliriz ya da hali hazırda olan LayoutManager’i da kullanabiliriz. 3 tane LayoutManager Çeşidi var,

  • LinearLayoutManager : Alt alta tek sıra olacak şekilde.
  • GridLayoutManager: Alt alta fakat sıra sayısını siz belirleyebilirsiniz.
  • StaggeredGridLayoutManager: Alt alta ama boyutunu görselleştirilmesini siz belirlersiniz. Özellikle dergi, kıyafet uygulamalarında harika gidiyor.

Varsayılan ListView gibi LayoutManager kullanmak için,

LinearLayoutManager llm = new LinearLayoutManager(context);
rv.setLayoutManager(llm);

Verilerin Tanımlanması:

Verilere erişmek için bir adapter’e ihtiyaç var. Kişileri temsil eden basit bir sınıf yazalım. ve verilerimizi ekleyelim. Burada örnek bir veri tanımlanması yapıyorum. Kendi örneğimde tek class olduğundan böyle bir tanımlama yapmadım.

class Person {
    String name;
    String age;
    int photoId;
 
    Person(String name, String age, int photoId) {
        this.name = name;
        this.age = age;
        this.photoId = photoId;
    }
}
 
private List<Person> persons;
 
// This method creates an ArrayList that has three Person objects
// Checkout the project associated with this tutorial on Github if
// you want to use the same images.
private void initializeData(){
    persons = new ArrayList<>();
    persons.add(new Person("İsimsiz Eleman", "23 ", R.drawable.photo_1));
    persons.add(new Person("Ümit KÖSE", "24 ", R.drawable.photo_2));
    persons.add(new Person("Eleman İsimsiz", "25", R.drawable.photo_3));
}

Adapter Yazalım
package com.umiitkose.recyclerviewexample1.Adapter;

/**
 * Created by umiitkose on 6.08.2017.
 */import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.umiitkose.recyclerviewexample1.R;
import java.util.ArrayList;

public class CountryAdapter  extends RecyclerView.Adapter<CountryAdapter.ViewHolder> {
    private ArrayList<String> countries;

    public CountryAdapter(ArrayList<String> countries) {
        this.countries = countries;
    }

    @Override
    public CountryAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.satir_layout, viewGroup, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {

        viewHolder.tv_country.setText(countries.get(i));
    }

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

    public void addItem(String country) {
        countries.add(country);
        notifyItemInserted(countries.size());
    }

    public void removeItem(int position) {
        countries.remove(position);
        notifyItemRemoved(position);
        notifyItemRangeChanged(position, countries.size());
    }
    public class ViewHolder extends RecyclerView.ViewHolder{
        TextView tv_country;
        public ViewHolder(View view) {
            super(view);

            tv_country = (TextView)view.findViewById(R.id.tv_country);
        }
    }
}

 

Detaya inersek,

 

public class ViewHolder extends RecyclerView.ViewHolder{
    TextView tv_country;
    public ViewHolder(View view) {
        super(view);

        tv_country = (TextView)view.findViewById(R.id.tv_country);
    }
}

 

ViewHolder Nedir :

ViewHolder ListView kullanımının performans açısından iyileştirilmesini sağlayan bir pattern’dır. BizgetView()metodumuzu ViewHolder kullanmadan yazarsak eğer her eleman içingetViewmetodu çağırıldığındaview injectiondediğimiz xml dosyamızdaki view’ların id’leri üzerinden bulunarak arka plandacastingişlemi ardından bir değişkene atanması işlemi her seferinde gerekleşecekti. Ama bu pattern bizi bu zahmetli ve masraflı işten kurtarıp performans artışı sağlamaktadır.

Örneklendirecek olursak;

Listviewiçin her item yükleneceği sıradafindViewById()ile bileşenleri yaniviewçağrılıp ve içine veriler yükleniyor ve ekrana basılıyor. Elimizde ne kadar veri varsa her seferinde bu işlem gerçekleşiyor. Bu durum elimizde binlerce veri olduğunda sıkıntı oluşturuyor ve performans kaybı yaşıyoruz.ViewHolderburada devreye giriyor ve her seferinde yeni birviewoluşturması yerine, eskiviewtekrar kullanılıyor ve veriler yüklenip ekrana basılıyor. Bu sayede ram şişmesi gibi bir problemden de kurtulmuş oluyoruz. Ancak burada şunu belirtmeliyim bizim menümüz veya yapacağınız 10 – 15 öğeli bir menü için bu yapı gözle görülür bir performans artışı sağlamaz.

 

Şimdi Adaptere devam ediyoruz. Constractırımızı yazıyoruz.

    private ArrayList<String> countries;

    public CountryAdapter(ArrayList<String> countries) {
        this.countries = countries;
    }

Adapter yazarken 3 tane implement edeceğiz metodumuzla ilgileniyoruz.

getItemCount Metodu :

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

viewHolderin başlatılması için metodumuz:
   @Override
    public CountryAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.satir_layout, viewGroup, false);
        return new ViewHolder(view);
    }

ve verileri göstermek için metodumuz:
  @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {

        viewHolder.tv_country.setText(countries.get(i));
    }

Diğer metodlar ise silme ve güncelleme işlemleri içindir.

adapteri activitymizde kullanmak için,

RVAdapter adapter = new RVAdapter(persons);
rv.setAdapter(adapter);

Şimdi MainActivity’imiz,
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;

import com.umiitkose.recyclerviewexample1.Adapter.CountryAdapter;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private ArrayList<String> countries =  new ArrayList<>();
    private CountryAdapter adapter;
    private RecyclerView recyclerView;
    private AlertDialog.Builder alertDialog;
    private EditText et_country;
    private int edit_position;
    private View view;
    private Paint p = new Paint();

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

        initViews();
        initDialog();
    }

    private void initViews(){
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(this);
        recyclerView = (RecyclerView)findViewById(R.id.card_recycler_view);
        recyclerView.setHasFixedSize(true);
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(layoutManager);
        adapter = new CountryAdapter(countries);
        recyclerView.setAdapter(adapter);
        countries.add("Türkiye");
        countries.add("Arjantin");
        countries.add("Brezilya");
        countries.add("Fransa");
        countries.add("Mısır");
        adapter.notifyDataSetChanged();
        initSwipe();

    }
    private void initSwipe(){
        ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {

            @Override
            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                return false;
            }

            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                int position = viewHolder.getAdapterPosition();

                if (direction == ItemTouchHelper.LEFT){
                    adapter.removeItem(position);
                } else {
                    removeView();
                    edit_position = position;
                    alertDialog.setTitle("Edit Country");
                    et_country.setText(countries.get(position));
                    alertDialog.show();
                }
            }

            @Override
            public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {

                Bitmap icon;
                if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){

                    View itemView = viewHolder.itemView;
                    float height = (float) itemView.getBottom() - (float) itemView.getTop();
                    float width = height / 3;

                    if(dX > 0){
                        p.setColor(Color.parseColor("#388E3C"));
                        RectF background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX,(float) itemView.getBottom());
                        c.drawRect(background,p);
                        icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_edit_white);
                        RectF icon_dest = new RectF((float) itemView.getLeft() + width ,(float) itemView.getTop() + width,(float) itemView.getLeft()+ 2*width,(float)itemView.getBottom() - width);
                        c.drawBitmap(icon,null,icon_dest,p);
                    } else {
                        p.setColor(Color.parseColor("#D32F2F"));
                        RectF background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(),(float) itemView.getRight(), (float) itemView.getBottom());
                        c.drawRect(background,p);
                        icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_delete_white);
                        RectF icon_dest = new RectF((float) itemView.getRight() - 2*width ,(float) itemView.getTop() + width,(float) itemView.getRight() - width,(float)itemView.getBottom() - width);
                        c.drawBitmap(icon,null,icon_dest,p);
                    }
                }
                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }
        };
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
        itemTouchHelper.attachToRecyclerView(recyclerView);
    }
    private void removeView(){
        if(view.getParent()!=null) {
            ((ViewGroup) view.getParent()).removeView(view);
        }
    }
    private void initDialog(){
        alertDialog = new AlertDialog.Builder(this);
        view = getLayoutInflater().inflate(R.layout.dialog_layout,null);
        alertDialog.setView(view);
        alertDialog.setPositiveButton("Save", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if(add){
                    add =false;
                    adapter.addItem(et_country.getText().toString());
                    dialog.dismiss();
                } else {
                    countries.set(edit_position,et_country.getText().toString());
                    adapter.notifyDataSetChanged();
                    dialog.dismiss();
                }

            }
        });
        et_country = (EditText)view.findViewById(R.id.et_country);
    }
    @Override
    public void onClick(View v) {

        switch (v.getId()){
            case R.id.fab:
                removeView();
                alertDialog.setTitle("Add Country");
                et_country.setText("");
                alertDialog.show();
                break;
        }
    }
}

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="//schemas.android.com/apk/res/android"
    xmlns:tools="//schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

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

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:src="@drawable/ic_add" />

</android.support.design.widget.CoordinatorLayout>

dialog_layout.xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="//schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/et_country"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

 

satir_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="//schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginBottom="1dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_country"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="15dp"
        android:layout_marginTop="15dp"
        android:textSize="18sp"
        android:textStyle="bold" />

</LinearLayout>

Uygulamanın kodu için:

//github.com/umiitkose/RecyclerViewExample

Uygulamanın ekran görüntüleri:

 

 

20643710_1306698082760743_881338224_n 20668274_1306698106094074_1150448362_n 20668343_1306698062760745_135560701_n

 

3 thoughts on “42 Android Programlama – RecyclerView Nedir ?

  • fatih

    merhaba.
    paylaşımınız için teşekkürler.
    yazılarınızdan çok şey öğrendim. fakat:

    Uygulamanın kodu için:

    //github.com/umiitkose/RecyclerViewExample

    burada verdiğniz link çalışmıyor. lütfen güncel bir link verr misiniz.

    Yanıtla
  • Fatih

    Merhaba. Konu anlatımınız çok yararlı oldu. Fakat verdiğiniz örnek kod linki bozuk. Güncelleme yaparsanız sevinirim.

    Yanıtla
  • Derleme life

    Merhaba, emeğiniz için teşekkür ederim.

    satir_layout.xml ta
    LinearLayout taki
    android:layout_width=”match_parent” olmalı

    Yanıtla

fatih için bir cevap yazın Cevabı iptal et

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir