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:
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.
Merhaba. Konu anlatımınız çok yararlı oldu. Fakat verdiğiniz örnek kod linki bozuk. Güncelleme yaparsanız sevinirim.
Merhaba, emeğiniz için teşekkür ederim.
satir_layout.xml ta
LinearLayout taki
android:layout_width=”match_parent” olmalı