14 Şubat 2018 Çarşamba

Android Dersleri 35.2 - Material Design, RecyclerView, CardView nedir?

15.9 - RecyclerView
Bu dersin tamamını içeren pdf'in link'i :
Kaynaklar :
It is a modernized version of the ListView and the GridView classes provided by the Android framework. It is supposed to be the successor of ListView and GridView, and it can be found in the latest support-v7 version. It enforced a programming style that results in good performance. It also comes with default animations for removing and adding elements.
RecyclerView has a more extensible framework, for example it provides the ability to implement both horizontal and vertical layouts.
Use the RecyclerView widget when you have data collections whose elements change at runtime based on user action or network events. (Runtime'da değişebilen bir listview istiyorsanız RecyclerView kullanmalısınız.)
RecyclerView allows to use different layout managers(LinearLayout, GridLayout) for positioning items.
Recycler view uses a ViewHolder to store references to the views for one entry in the recycler view. A ViewHolder class is a static inner class in your adapter which holds references to the relevant views. With these references, your code can avoid the time-consuming findViewById()method to update the widgets with new data. ( ViewHolder, Recycler view'deki bir entry için olan view'lere reference'ı tutar. ViewHolder class'ı, adapter'inizde bir static inner class'dır. ViewHolder class'ı, ilgili view'lere refer eden reference'ları tutar. Bu reference'lar sayesinde, çok pahalı olan findViewById()method'unu kullanmaya gerek kalmaz.  )

If you want to use a RecyclerView, you will need to work with the following: ( Eğer bir RecyclerView kullanmak istiyorsanız, aşağıdaki şeylere ihtiyacınız olacaktır. )
RecyclerView.Adapter - To handle the data collection and bind it to the view. (Örneğin bir array'deki verileri alıp view'lere gömme işini burada yaparız. )
Since RecyclerView.Adapter is abstract you will have to implement these three methods:
  • public VH onCreateViewHolder(ViewGroup parent, int viewType)
  • public void onBindViewHolder(VH holder, int position)
  • public int getItemCount()
LayoutManager - Helps in positioning the items. ( Item'ları dikey veya yatay listede mi veya grid olarak mı göstereceğiz bunu LayoutManager belirler. )
ItemAnimator - Helps with animating the items for common operations such as Addition or Removal of item. ( Runtime'da ListView'e yeni bir satır eklerken veya silerken animasyon göstermek için bu class kullanılır. )
Dataset(Data model) :  Örneğin bir array olabilir, bir satırda gösterilecek olan verileri tutar.
RecyclerView
Furthermore, it provides animation support for ListView items whenever they are added or removed, which had been extremely difficult to do in the current implementation. ( RecyclerView, runtime'da ListView'e yeni bir satır eklerken veya silerken animasyon göstermek için kullanılır. Bu işi RecyclerView olmadan sadece ListView class'ını kullanarak yapmak felaket zor bir iştir.  )
RecyclerView also begins to enforce the ViewHolder pattern too, which was already a recommended practice but now deeply integrated with this new framework. (RecyclerView, ViewHolder pattern'i zorunlu olarak kullandırır. ViewHolder pattern zaten kullanılması tavsiye edilen bir pattern'dır. Bu yeni framework ViewHolder pattern'ın kullanılmasını zorunlu kılmıştır. )
Compared to ListView
RecyclerView differs from its predecessor ListView primarily because of the following features: (RecyclerView'in, atası ListView'den farkları şunlardır: )
  • Required ViewHolder in Adapters - ListView adapters do not require the use of the ViewHolder pattern to improve performance. In contrast, implementing an adapter for RecyclerView requires the use of the ViewHolder pattern. (ListView adapter'lerinde performansı arttırmak için ViewHolder pattern kullanılabilir ancak bu zorunlu değildir. Buna karşın RecyclerView adapter'lerinde ViewHolder pattern kullanmak zorunludur.  )
  • Customizable Item Layouts - ListView can only layout items in a vertical linear arrangement and this cannot be customized. In contrast, the RecyclerView has a RecyclerView.LayoutManager that allows any item layouts including horizontal lists or staggered grids. ( ListView ile item'lar sadece vertical linear halinde yani dikey liste halinde gösterilebilirler, custom bir layout seçeneği yoktur, örneğin yatay liste halinde gösterilemezler. Buna karşın, RecyclerView RecyclerView.LayoutManager class'ına sahiptir, bu class item'ların yatayveya grid layout'larda gösterilebilmesini sağlar.  )
  • Easy Item Animations - ListView contains no special provisions through which one can animate the addition or deletion of items. In contrast, the RecyclerView has the RecyclerView.ItemAnimator class for handling item animations. ( ListView'de gösterilen bir satırın(item'ın) silinirken veya yenisi eklenirken bir animation gösterme seçeneği yoktur. Buna karşın, RecyclerView'in sahip olduğu RecyclerView.ItemAnimator class'ı item animation'ları sağlar. )
  • Manual Data Source - ListView had adapters for different sources such as ArrayAdapter and CursorAdapter for arrays and database results respectively. In contrast, the RecyclerView.Adapter requires a custom implementation to supply the data to the adapter. ( ListView array veya veritabanından okuyacağı veriler için ArrayAdapter ve CursorAdapter gibi adapter'leri kullanır. Buna karşın, RecyclerView 'de hazır bir adapter yoktur, RecyclerView.Adapter class'ı implement edilmelidir.  )
  • Manual Item Decoration - ListView has the android:divider property for easy dividers between items in the list. In contrast, RecyclerView requires the use of a RecyclerView.ItemDecoration object to setup much more manual divider decorations. ( ListView'de, listedeki item'lar arasında basit divider'lar göstermek için android:divider property kullanılır. Buna karşın, RecyclerView'in sahip olduğu RecyclerView.ItemDecoration class'ı kullanılarak çok daha manuel divider decoration'ları kullanılabilir. )
  • Manual Click Detection: ListView has an AdapterView.OnItemClickListener interface for binding to the click events for individual items in the list. In contrast, RecyclerView only has support for RecyclerView.OnItemTouchListener which manages individual touch events but has no built-in click handling. ( ListView'in sahip olduğu AdapterView.OnItemClickListener class'ı satırlara tıklama listener'ı register eder. Buna karşın RecyclerView'in sahip olduğu RecyclerView.OnItemTouchListener class'ı ne yapar anlamadım....  )
Components of a RecyclerView

a - LayoutManagers
RecyclerView needs to have a layout manager and an adapter to be instantiated. A layout manager positions item views inside a RecyclerView and determines when to reuse item views that are no longer visible to the user. ( RecyclerView'in bir layout manager'a ve bir adapter'e ihtiyacı vardır. Layout manager, RecyclerView'in içindeki item view'leri pozisyonlandırır ve kullanıcıya gösterilmeyen item view'lerin ne zaman tekrar kullanılacağını belirler. )

RecyclerView provides these built-in layout managers:
  • LinearLayoutManager shows items in a vertical or horizontal scrolling list.
  • GridLayoutManager shows items in a grid.
  • StaggeredGridLayoutManager shows items in a staggered grid.
To create a custom layout manager, extend the RecyclerView.LayoutManager class. Here is Dave Smith's talk on custom layout manager.
b - RecyclerView.Adapter
RecyclerView includes a new kind of adapter. It’s a similar approach to the ones you already used, but with some peculiarities, such as a required ViewHolder. You will have to override two main methods: one to inflate the view and its view holder, and another one to bind data to the view. The good thing about this is that first method is called only when we really need to create a new view. No need to check if it’s being recycled. ( RecyclerView'İn sahip olduğu adapter şimdiye kullandığımız adapter'lere benzer ancak bazı farklılıkları vardır mesela ViewHolder kullanmayı gerektirir. RecyclerView'in adapter'inde şu 2 method'u override etmeliyiz:
- onCreateViewHolder() : view'i ve view holder'ı inflate eder. Bu method, sadece yeni bir view yaratılacağı zaman çağırılır. View'in recycle edilip edilmediğini check etmeye gerek yoktur. Adapter class'ında tanımladığımız inner ViewHolder class'ından bir object yaratılır, onCreateViewHolder() method'u bu object'i return eder. Adapter mContacts listesindeki verileri view'lere bind edecektir, getItemCount() method'u da override edilmeli mContacts listesinin size'Inı return etmelidir.
- onBindViewHolder() : verileri view'e bind eder. )
public class ContactsAdapter extends
   RecyclerView.Adapter<ContactsAdapter.ViewHolder> {

   private List<Contact> mContacts;

   // ... constructor and member variables
   public static class ViewHolder extends RecyclerView.ViewHolder
   {
...
   }

   // Usually involves inflating a layout from XML and returning the holder
   @Override
   public ContactsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       Context context = parent.getContext();
       LayoutInflater inflater = LayoutInflater.from(context);

       // Inflate the custom layout
       View contactView = inflater.inflate(R.layout.item_contact, parent, false);

       // Return a new holder instance
       ViewHolder viewHolder = new ViewHolder(contactView);
       return viewHolder;
   }

   // Involves populating data into the item through holder
   @Override
   public void onBindViewHolder(ContactsAdapter.ViewHolder viewHolder, int position) {
       // Get the data model based on position
       Contact contact = mContacts.get(position);

       // Set item views based on your views and data model
       TextView textView = viewHolder.nameTextView;
       textView.setText(contact.getName());
       Button button = viewHolder.messageButton;
       button.setText("Message");
   }

   // Returns the total count of items in the list
   @Override
   public int getItemCount() {
       return mContacts.size();
   }


}


c - ItemAnimator
RecyclerView.ItemAnimator will animate ViewGroup modifications such as add/delete/select that are notified to adapter. DefaultItemAnimator can be used for basic default animations and works quite well. See the section of this guide for more information. ( ViewGroup'da ekleme,silme,seçme yapıldığında RecyclerView.ItemAnimator class'ı animasyon göstermeyi sağlayacaktır. )
Using the RecyclerView
Using a RecyclerView has the following key steps:
  1. Gradle dosyasına şu RecyclerView support library'yi ekle :
dependencies {
   ...
   compile 'com.android.support:recyclerview-v7:25.2.0'
}
  1. Define a model class to use as the data source. ( Örneğin, Contact isimli liste RecyclerView'de gösterilecek satırlardaki view'lere bind edilecek diyelim. Bu durumda Contact class'ı bizim model class'ımızdır, data source olarak kullanılacaktır. )
  2. Add a RecyclerView to your activity to display the items. ( Activity'nin layout xml'ine RecyclerView element eklemeliyiz. )
  3. Create a custom row layout XML file to visualize the item. ( RecyclerView'de gösterilecek herbir satırın layout'unu bir xml dosyasında tanımlarız. )
  4. Create a RecyclerView.Adapter and ViewHolder to render the item. (RecyclerView.Adapter class'ını extend eden bir class tanımlanır. Bu class'da RecyclerView.ViewHolder class'ını extend eden bir inner class tanımlanır.  )
  5. Bind the adapter to the data source to populate the RecyclerView. (RecyclerView.Adapter class'ında gerekli method'lar override edilerek verilerin view'ler bind edilmesi sağlanır.  )
The steps are explained in more detail below. ( Şimdi bu adımları detaylıca inceleyelim. )
1 - Installation
Make sure the RecyclerView support library is listed as a dependency in your app/build.gradle:
dependencies {
   ...
   compile 'com.android.support:recyclerview-v7:25.2.0'
}
2 - Defining a Model
Every RecyclerView is backed by a source for data. In this case, we will define a Contact class which represents the data model being displayed by the RecyclerView: (RecyclerView'de gösterilecek satırlardaki view'lere, bu class'ın object'leri bind edilecektir. )
public class Contact {
   private String mName;
   private boolean mOnline;

   public Contact(String name, boolean online) {
       mName = name;
       mOnline = online;
   }

   public String getName() {
       return mName;
   }

   public boolean isOnline() {
       return mOnline;
   }

   private static int lastContactId = 0;

   public static ArrayList<Contact> createContactsList(int numContacts)
  {
       ArrayList<Contact> contacts = new ArrayList<Contact>();
       for (int i = 1; i <= numContacts; i++)
      {
           contacts.add(new Contact("Person " + ++lastContactId, i <= numContacts / 2));
       }
       return contacts;
   }
}
3- Add the RecyclerView element within layout xml
Inside the desired activity layout XML file in res/layout/activity_users.xml, let's add the RecyclerView from the support library: ( Activity'nin layout xml dosyasına, support library'deki RecyclerView element eklenir.  )
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent" >

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

</RelativeLayout>
4 - Creating the Custom Row Layout
Before we create the adapter, let's define the XML layout file that will be used for each row within the list. This item layout for now should contain a horizontal linear layout with a textview for the name and a button to message the person: ( Adapter'ü tanımlamadan önce, RecyclerView'de gösterilecek herbir satırın layout'unu bir xml dosyasında tanımlarız. Yatay linear layout içerisinde ismi gösteren bir textview ve sağda bir buton gösterilir. )
This layout file can be created in res/layout/item_contact.xml and will be rendered for each item row. Note that you should be using wrap_content for the layout_height because RecyclerView versions prior to 23.2.1 previously ignored layout parameters. See this link for more context. ( Herbir satır'da  bu layout gösterilecektir. Root layout olan LinearLayout'un layout_height'ı wrap_content olmalıdır. )
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:orientation="horizontal"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:paddingTop="10dp"
       android:paddingBottom="10dp" >

   <TextView
       android:id="@+id/contact_name"
       android:layout_width="0dp"
       android:layout_height="wrap_content"
       android:layout_weight="1"
       />

   <Button
       android:id="@+id/message_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:paddingLeft="16dp"
       android:paddingRight="16dp"
       android:textSize="10sp"
       />
</LinearLayout>
With the custom item layout complete, let's create the adapter to populate the data into the recycler view. ( RecyclerView'deki satırların layout'larını tanımladık. Şimdi ise satırlardaki view'lere verileri yükleyeceğiz.)
Creating the RecyclerView.Adapter
Here we need to create the adapter which will actually populate the data into the RecyclerView. The adapter's role is to convert an object at a position into a list row item to be inserted. ( Şimdi ise bir adapter tanımlayacağız. Adapter'Ün görevi, verileri RecyclerView'e doldurmaktır. Diğer bir deyişle object'leri satırlardaki view'lere doldurmaktır. )
However with a RecyclerView the adapter requires the existence of a "ViewHolder" object which describes and provides access to all the views within each item row. We can create the basic empty adapter and holder together in ContactsAdapter.java as follows: (RecyclerView'de, ViewHolder inner class tanımlanır. ViewHolder class'ında, herbir satırdaki view'lere erişilir. Aşağıda boş bir adapter class'ı tanımladık ve inner ViewHolder class'ı tanımladık. )
// Create the basic adapter extending from RecyclerView.Adapter (RecyclerView.Adapter<inner ViewHolder class> class'ını extend eden bir class tanımladık.)
// Note that we specify the custom ViewHolder which gives us access to our views ( Bu class'da RecyclerView.ViewHolder class'ını extend eden bir class tanımlayacağız. Bu ViewHolder class'ında satırlardaki view'lere erişeceğiz. )
public class ContactsAdapter extends
   RecyclerView.Adapter<ContactsAdapter.ViewHolder> {

   // Provide a direct reference to each of the views within a data item
   // Used to cache the views within the item layout for fast access. (ViewHolder'da gösterilecek herbir satırdaki tüm view'lere ViewHolder class'ında erişelim. Bu view'ler cache'lenecektir.)
   public static class ViewHolder extends RecyclerView.ViewHolder {
       Herbir satırın layout'unda TextView ve Button view göstermek istiyoruz diyelim. Bu durumda bu 2 view'e refer eden reference variable declare ederiz ViewHolder class'ında. ViewHolder class'ının constructor'ında ise bu view object'lere erişiriz.
       public TextView nameTextView;
       public Button messageButton;

       public ViewHolder(View itemView) {
           super(itemView);
           nameTextView = (TextView) itemView.findViewById(R.id.contact_name);
           messageButton = (Button) itemView.findViewById(R.id.message_button);
       }
   }
}
Now that we've defined the basic adapter and ViewHolder, we need to begin filling in our adapter. First, let's store a member variable for the list of contacts and pass the list in through our constructor: ( Yukarıda basit bir adapter ve ViewHolder tanımladık. Ama tabi işimiz daha bitmedi adapter class'ını geliştirmeye devam edeceğiz. Adapter class'ında contact listesine refer eden bir reference variable declare ederiz:
private List<Contact> mContacts;
Context object'e refer eden bir reference variable declare ederiz:
private Context mContext;
Adapter class'ımız için yukarıdaki 2 reference variable'a assign edeceğimiz 2 parametre alan bir constructor tanımlarız.
public ContactsAdapter(Context context, List<Contact> contacts)
{
       mContacts = contacts;
       mContext = context;
}
Sonra ise context object'i return eden bir method tanımlarız:
private Context getContext()
{
      return mContext;
} )
public class ContactsAdapter extends
   RecyclerView.Adapter<ContactsAdapter.ViewHolder> {

   // ... view holder defined above...

   // Store a member variable for the contacts
   private List<Contact> mContacts;
   // Store the context for easy access
   private Context mContext;

   // Pass in the contact array into the constructor
   public ContactsAdapter(Context context, List<Contact> contacts) {
       mContacts = contacts;
       mContext = context;
   }

   // Easy access to the context object in the recyclerview
   private Context getContext() {
      return mContext;
   }
}
Every adapter has three primary methods: onCreateViewHolder to inflate the item layout and create the holder, onBindViewHolder to set the view attributes based on the data and getItemCount to determine the number of items. We need to implement all three to finish the adapter: ( Adapter'ü tanımlama işimiz hala bitmedi. Yukarıdaki yaptıklarımıza ek olarak, adapter class'ımızda şu method'ları tanımlamalıyız, aşağıdaki gibi.
- onCreateViewHolder() : saturların layout'unu burada belirtiriz ve ViewHolder class'ından bir object yaratıp return ederiz.
- onBindViewHolder() : Verileri view'lere set ederiz. Bu method herbir satır için otomatik olarak çağırılır, method'un aldığı 2. parametreden hangi satır için çağırıldığı bilinir. Dolayısıyla örneğin 3.satır için çağırılırsa mContact List object'in 3. poziyonundaki Contact object elde edilir. Sonra ViewHolder object'in içerdiği textView elde edilir. Bu textView'e, az önce elde edilen Contact object'in içerdiği name variable'ının değeri set edilir. Benzer şekilde ViewHolder object'in içerdiği Button object elde edilip bu butona text olarak ise "Message" set edilir.
public void onBindViewHolder(ContactsAdapter.ViewHolder viewHolder, int position) {
       Contact contact = mContacts.get(position);
       TextView textView = viewHolder.nameTextView;
       textView.setText(contact.getName());
       Button button = viewHolder.messageButton;
       button.setText("Message");
   }
- getItemCount() : RecyclerView'de gösterilecek satır sayısını return eder. )
public class ContactsAdapter extends
   RecyclerView.Adapter<ContactsAdapter.ViewHolder> {

   // ... constructor and member variables

   // Usually involves inflating a layout from XML and returning the holder
   @Override
   public ContactsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       Context context = parent.getContext();
       LayoutInflater inflater = LayoutInflater.from(context);

       // Inflate the custom layout
       View contactView = inflater.inflate(R.layout.item_contact, parent, false);

       // Return a new holder instance ( Tanımladığımız inner ViewHolder class'ının constructor'ına satır layout'Unu belirten View object verilerek elde edilen object return edilir. )
       ViewHolder viewHolder = new ViewHolder(contactView);
       return viewHolder;
   }

   // Involves populating data into the item through holder
   @Override
   public void onBindViewHolder(ContactsAdapter.ViewHolder viewHolder, int position) {
       // Get the data model based on position
       Contact contact = mContacts.get(position);

       // Set item views based on your views and data model
       TextView textView = viewHolder.nameTextView;
       textView.setText(contact.getName());
       Button button = viewHolder.messageButton;
       button.setText("Message");
   }

   // Returns the total count of items in the list
   @Override
   public int getItemCount() {
       return mContacts.size();
   }
}
With the adapter completed, all that is remaining is to bind the data from the adapter into the RecyclerView. ( Adapter class'ımızı tanımlamayı bitirdik. Şimdi geriye sadece, adapter'deki verileri RecyclerView'e bind etmek kaldı. )
Binding the Adapter to the RecyclerView
In our activity, we will populate a set of sample users which should be displayed in the RecyclerView. ( RecyclerView'i hangi activity'de göstermek istiyorsak, o activity class'ının onCreate() method'una aşağıdaki kodları yazarız.Bu activity'nin layout xml'ine RecyclerView element koymuştuk, bu element'i kullanarak bir RecyclerView object elde ederiz:
RecyclerView rvContacts = (RecyclerView) findViewById(R.id.rvContacts);
20 tane Contact object içeren bir List object elde ederiz:
contacts = Contact.createContactsList(20);
Tanımladığımız adapter class'ından bir object yaratırız. Constructor'a 1. argument olarak Context object, 2.argument olarak ise data source olarak kullanılacak List object'i veririz :
ContactsAdapter adapter = new ContactsAdapter(this, contacts);
RecyclerView object'in setAdapter(adapter) method'unu çağırarak RecyclerView object ile adapter object'i birbirine bağlarız(bind ederiz).
RecyclerView'in layout'unu belirlemek için RecyclerView object'in setLayoutManager() method'unu çağırırız. Bu örnekte RecyclerView'in layout'u linear layout yapılmıştır :
        rvContacts.setLayoutManager(new LinearLayoutManager(this));
)
public class UserListActivity extends AppCompatActivity {

    ArrayList<Contact> contacts;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        // Lookup the recyclerview in activity layout
        RecyclerView rvContacts = (RecyclerView) findViewById(R.id.rvContacts);

        // Initialize contacts
        contacts = Contact.createContactsList(20);
        // Create adapter passing in the sample user data
        ContactsAdapter adapter = new ContactsAdapter(this, contacts);
        // Attach the adapter to the recyclerview to populate items
        rvContacts.setAdapter(adapter);
        // Set layout manager to position the items
        rvContacts.setLayoutManager(new LinearLayoutManager(this));
        // That's all!
    }
}
Finally, compile and run the app and you should see something like the screenshot below. If you create enough items and scroll through the list, the views will be recycled and far smoother by default than the ListView widget: ( Şimdi projemizi çalıştırdığımızda aşağıdaki gibi bir ekran görürüz. 20 item'lık(satırlık) bu RecyclerView'de scroll ettiğimizde ListView'den çok daha düzgün bir sonuç alırız. ListView'e göre çok daha hızlı bir şekilde scroll edebiliriz, view'ler çok hızlı bir şekilde recycle edilebilir. )
Şimdiye kadar öğrendiklerimizi uyguladığımızda yukarıdaki gibi bir output elde ettik. Bu projeye şuradan erişebilirsiniz :
16_9_RecyclerView

Notifying the Adapter
Unlike ListView, there is no way to add or remove items directly through the RecyclerView adapter. You need to make changes to the data source directly and notify the adapter of any changes. ( ListView'de doğrudan adapter'ü kullanarak listview'e yeni bir satır ekleyebiliriz,silebiliriz. Buna karşın, doğrudan RecyclerView adapter'ü kullanarak yeni bir satır eklemek veya varolan bir satırı silmek mümkün değildir.
RecyclerView'e satır eklemek için, doğrudan data source'da değişiklik yapmalıyız ve adapter'ü bu değişiklikten haberdar etmeliyiz(notify). )
Also, whenever adding or removing elements, always make changes to the existing list. For instance, reinitializing the list of Contacts such as the following will not affect the adapter, since it has a memory reference to the old list: ( Dikkat etmemiz gereken önemli bir nokta ise şudur, satır eklemek silmek için varolan list object üzerinde değişiklik yapmalıyız, yeni bir list object kullanmamalıyız yani. Örneğin aşağıdaki gibi yeni bir Contact list object yaratmak adapter'ü etkilemez, çünkü adapter eski list object'e refer eder. )

// do not reinitialize an existing reference used by an adapter
contacts = Contact.createContactsList(5);
Instead, you need to act directly on the existing reference: ( Yukarıdaki kod çalışmaz. Bunun yerine aşağıdaki gibi, varolan list object'e yeni elemanlar eklemeliyiz: )
// add to the existing list
contacts.addAll(Contact.createContactsList(5));
There are many methods available to use when notifying the adapter of different changes: ( Sonrasında ise adapter'ü data source'daki yani list object'deki değişiklikten haberdar etmeliyiz(notify). Adapter'ü notify etmek için kullanılabilecek method'lar aşağıda gösterilmiştir : )
Method
Description
notifyItemChanged(int pos)
Notify that item at position has changed. ( Listenin belirli bir pozisyonundaki elemanı değiştirirsek sonrasında bu method'u çağırmalıyız.  )
notifyItemInserted(int pos)
Notify that item reflected at position has been newly inserted. (Listenin belirli bir pozisyonuna eleman eklersek sonrasında bu method'u çağırmalıyız. )
notifyItemRemoved(int pos)
Notify that items previously located at position has been removed from the data set. (Listenin belirli bir pozisyonundaki elemanı silersek sonrasında bu method'u çağırmalıyız. )
notifyDataSetChanged()
Notify that the dataset has changed.
Use only as last resort. ( Listede herhangi bir değişiklik yaptıktan sonra bu method'u çağırarak da adapter'ü haberdar edebiliriz. Başka hiçbir seçeneğimiz yoksa bu method'u çağırmalıyız. )
We can use these from the activity or fragment: ( Yukarıdakileri method'ları activity veya fragment'ın içerisinde çağırabiliriz. Aşağıdaki örneği inceleyelim. contacts listesinin 0.pozisyonuna yeni bir object ekleriz, sonra ise adapter object'in notifyItemInserted(0) method'unu çağırarak, adapter'e 0.pozisyona bir eleman eklediğimizi söyleriz. )
// Add a new contact
contacts.add(0, new Contact("Barney", true));
// Notify the adapter that an item was inserted at position 0
adapter.notifyItemInserted(0);
Every time we want to add or remove items from the RecyclerView, we will need to explicitly inform to the adapter of the event. Unlike the ListView adapter, a RecyclerView adapter should not rely on notifyDataSetChanged() since the more granular actions should be used. See the API documentation for more details. ( RecyclerView'e item(satır) eklemek/silmek istediğimiz her zaman mutlaka explicit bir şekilde adapter'ü bundan haberdar etmeliyiz. ListView adapter'ü notifyDataSetChanged() method'una dayanır, RecyclerView adapter'ü ise notifyDataSetChanged() method'una dayanmaz.  )
Also, if you are intending to update an existing list, make sure to get the current count of items before making any changes. For instance, a getItemCount() on the adapter should be called to record the first index that will be changed. ( Varolan bir listeyi güncellemek istiyorsanız, önce listenin size'ını bulup bi kenara yazın. Örneğin, listedeki değştirilecek olan index'i belirtmek için getItemCount()'un return ettiği değer kullanılabilir. )
Aşağıdaki örneği inceleyelim. adapter object'in getItemCount() method'U çağırılarak data source'un yani listenin size'ı elde edilir:
int curSize = adapter.getItemCount();
20 tane Contact object içeren bir ArrayList object yaratırız:
ArrayList<Contact> newItems = Contact.createContactsList(20);
Varolan listenin sonuna yukarıda yarattığımız 20 elemanlı ArrayList object'i ekleriz:
contacts.addAll(newItems);
Adapter'e şunu haber veririz, varolan listenin 20.index'inden itibaren 20 eleman ekleriz. 20 eleman'In index'leri 0'dan 19'a kadardır. Yani listenin sonuna eklenecek ilk elemanın index'i 20 olacaktır bu yüzden notifyItemRangeInserted() method'una 1.argument olarak 20 yani ilk listenin size'ını argument olarak veririz. 2.argument olarak ise listeye eklenecek eleman sayısını veririz:
adapter.notifyItemRangeInserted(curSize, newItems.size());

// record this value before making any changes to the existing list
int curSize = adapter.getItemCount();

// replace this line with wherever you get new records
ArrayList<Contact> newItems = Contact.createContactsList(20);

// update the existing list
contacts.addAll(newItems);
// curSize should represent the first element that got added
// newItems.size() represents the itemCount
adapter.notifyItemRangeInserted(curSize, newItems.size());
Diffing Larger Changes
Often times there are cases when changes to your list are more complex (i.e. sorting an existing list) and it cannot be easily determined whether each row changed. In this cases, you would normally have to call notifyDataSetChanged() on the entire adapter to update the entire screen, which eliminates the ability to perform animation sequences to showcase what changed. ( Eğer listede çok fazla değişiklik yaparsak, örneğin varolan bir listeyi belirli bir kritere göre sort edersek, notifyDataSetChanged() method'unu çağırarak ekranın tamamının güncellenmesi sağlanır, ancak bu method'u çağırınca değişen satırlarda animasyon gerçekleşmez. Bu case'i handle etmek için support library'deki DiffUtil class'ı kullanılabilir. )
A new DiffUtil class has been added in the v24.2.0 of the support library to help compute the difference between the old and new list. This class uses the same algorithm used for computing line changes in source code (the diff utility program), so it usually fairly fast. It is recommended however for larger lists that you execute this computation in a background thread. ( DiffUtil class'ı eski ve yeni liste arasındaki farkları hesaplar. Herhangi bir source code'da değişiklik yaptığımızda satırlardaki değişiklikleri bulmak için diff utility programını kullanırdık Linux komut satırında diff komutunu vererek hatırla, işte DiffUtil class'ı da bahsettiğimiz bu diff utility programını kullandığı için çok hızlı çalışır. Büyük listeler için bu class'ı kullanacaksak, ilgili hesaplamayı arka planda çalışan bir thread'de yapmalıyız. )
To use the DiffUtil class, you need to first implement a class that implements the DiffUtil.Callback that accepts the old and new list: (DiffUtil class'ını kullanabilmek için önce, DiffUtil.Callback class'ını extend eden bir class tanımlamalıyız, bu class'ın constructor'ı parametre olarak eski listeyi ve yeni listeyi alır. Eski ve yeni listenin size'ını return eden 2 method tanımlarız. Eski ve yeni list object'ler aynı mı diye check etmek için areItemsTheSame() method'u override edilir. Eski ve yeni list object'lerin içerikleri aynı mı diye check etmek için areContentsTheSame() method'u override edilir. )
public class ContactDiffCallback extends DiffUtil.Callback {

   private List<Contact> mOldList; // Eski liste
   private List<Contact> mNewList; // Yeni liste

   public ContactDiffCallback(List<Contact> oldList, List<Contact> newList) {
       this.mOldList = oldList; // Eski listeyi kaydederiz.
       this.mNewList = newList; // Yeni listeyi kaydederiz.
   }
   @Override
   public int getOldListSize() {
       return mOldList.size();
   }

   @Override
   public int getNewListSize() {
       return mNewList.size();
   }

   @Override
   public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
       // add a unique ID property on Contact and expose a getId() method
       return mOldList.get(oldItemPosition).getId() == mNewList.get(newItemPosition).getId();
   }

   @Override
   public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
       Contact oldContact = mOldList.get(oldItemPosition);
       Contact newContact = mNewList.get(newItemPosition);

       if (oldContact.getName() == newContact.getName() && oldContact.isOnline() == newContact.isOnline()) {
           return true;
       }
       return false;
   }
}
Next, you would implement a swapItems() method on your adapter to perform the diff and then invoke dispatchUpdates() to notify the adapter whether the element was inserted, removed, moved, or changed: ( Sonra ise adapter class'ımızda swapItems() isimli bir method tanımlarız. swapItems() method'unda, yukarıda tanımladığımız ContactDiffCallback class'ının constructor'ına parametre olarak eski ve yeni liste verilerek bir object yaratırız:
final ContactDiffCallback diffCallback = new ContactDiffCallback(this.mContacts, contacts);
Sonra DiffUtil.calculateDiff() method'unu çağırarak bir DiffUtil.DiffResult object elde ederiz:
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
Sonra eski listenin tüm elemanlarını sileriz ve eski listeye yenin listenin içeriğini koyarız:
this.mContacts.clear();
this.mContacts.addAll(contacts);
Sonra DiffUtil.DiffResult object'in dispatchUpdatesTo(this) method'u çağırılarak adapter listedeki değişiklikten haberdar edilmiştir :
diffResult.dispatchUpdatesTo(this); )
public class ContactsAdapter extends
       RecyclerView.Adapter<ContactsAdapter.ViewHolder> {

 public void swapItems(List<Contact> contacts) {
       // compute diffs
       final ContactDiffCallback diffCallback = new ContactDiffCallback(this.mContacts, contacts);
       final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);

       // clear contacts and add
       this.mContacts.clear();
       this.mContacts.addAll(contacts);

       diffResult.dispatchUpdatesTo(this); // calls adapter's notify methods after diff is computed
   }
}

16_9_2_RecyclerView_SortingList
For a working example, see this sample code. Linkteki source code'u kullanarak 16_9_2_RecyclerView_SortingList isimli projeyi geliştirdim.

Output :
 
Sort by name :
Sort by rating :
Sort by Year of Birth :
Şimdi source kodları kısaca inceleyelim.

Actor.java : Model class'ımızdır.
ActorAdapter.java : Adapter class'ının sahip olduğu data source yani ArrayList, Actor class'ı type'ında element'lere sahiptir. Bu class'da daha önce açıkladığımız swapItems() method'u tanımlanmıştır. SwapItems() method'unda ActorDiffCallback class'ından bir object yaratılır, ActorDiffCallback class'ı bir sonraki sayfada incelenmiştir.
Ayrıca toolbar'a bu 3 tane buton koyacağız, swapItems() method'u bu butonlara tıklayınca çağırılacaktır:

ActorRepository.java
ActorDiffCallback :
MainActivity.java

activity_main.xml
item_actor.xml

Scrolling to New Items
If we are inserting elements to the front of the list and wish to maintain the position at the top, we can set the scroll position to the 1st element:  ( Listenin başına eleman eklemek ve ekranda scrolling alanında bu elemanın da görünür olmasını istiyorsak, önce adapter'e 0. index'e eleman eklediğimizi haber veririz sonra scroll position'ı 0.eleman olarak set ederiz aşağıdaki gibi. )
adapter.notifyItemInserted(0);
rvContacts.scrollToPosition(0);   // index 0 position
If we are adding items to the end and wish to scroll to the bottom as items are added, we can notify the adapter that an additional element has been added and can call smoothScrollToPosition() on the RecyclerView: (Listenin sonuna eleman eklemek ve ekranda scrolling alanında bu elemanın da görünür olmasını istiyorsak, önce adapter'e son index'e eleman eklediğimizi haber veririz sonra scroll position'ı son eleman olarak set ederiz aşağıdaki gibi. Aşağıdaki kodda scrollToPosition() method'unu kullandık, smoothScrollToPosition()  method'unun ne işe yaradığını ise bilmiyorum.  )

adapter.notifyItemInserted(contacts.size() - 1);  // contacts.size() - 1 is the last element position
rvContacts.scrollToPosition(mAdapter.getItemCount() - 1);  // update based on adapter

Implementing Endless Scrolling
To implement fetching more data and appending to the end of the list as the user scrolls towards the bottom, use the addOnScrollListener() from the RecyclerView and add an onLoadMore method leveraging the EndlessScrollViewScrollListener document in the guide. ( Tumblr'daki bir sayfayı hatırla, en aşağı scroll ettiğimizde yeni şey yükleniyor alta ve daha aşağı scroll edebiliyoruz. Bu böyle taa content bitene kadar devam eder biz scroll ettikçe o daha fazla content yükler. Buradaki olay endless scrolling'dir yani. Bunun için RecyclerView object'in addOnScrollListener() method'unu çağırmalıyız. Burada daha fazla ayrıntıya girmeyeceğim. Ayrıntı için şu adrese bakabilirsin : https://guidess.codepath.com/android/using-the-recyclerview#configuring-the-recyclerview   )

Configuring the RecyclerView
The RecyclerView is quite flexible and customizable. Several of the options available are shown below.
Performance
setHasFixedSize() is used to let the RecyclerView that its size will keep the same. This information will be used to optimize itself.
recyclerView.setHasFixedSize(true);
RecyclerView can perform several optimizations if it can know in advance that RecyclerView's size is not affected by the adapter contents. RecyclerView can still change its size based on other factors (e.g. its parent's size) but this size calculation cannot depend on the size of its children or contents of its adapter (except the number of items in the adapter).
If your use of RecyclerView falls into this category, set this to true.
Layouts
The positioning of the items is configured using the layout manager. By default, we can choose between
- LinearLayoutManager,
- GridLayoutManager, and
- StaggeredGridLayoutManager.
Linear displays items either vertically or horizontally:

// Also supports `LinearLayoutManager.HORIZONTAL`
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
// Optionally customize the position you want to default scroll to
layoutManager.scrollToPosition(0);
// Attach layout manager to the RecyclerView
recyclerView.setLayoutManager(layoutManager);

Displaying items in a grid or staggered grid works similarly:

// 1. parametre column sayısıdır, 1. parametre ise orientation'ıdır.
StaggeredGridLayoutManager gridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
// Attach the layout manager to the recycler view. ( RecyclerView'e layout manager'ı bağlarız: )
recyclerView.setLayoutManager(gridLayoutManager);

Grid ve staggered grid arasındaki fark nedir? Okuyalım :
Heterogenous Layouts inside RecyclerView
See this guide if you want to inflate multiple types of rows inside a single RecyclerView. This is useful for feeds which contain various different types of items within a single list.
Örneğin Facebook'un veya Tumblr'ın news feed'ini düşün. News Feed'de bir fotoğraf, video, statüs güncelleme, link paylaşma gibi paylaşımlar görebiliriz. News Feed'de bir ListView veya RecyclerView vardır ancak herbir satır aynı değildir, kimi satırda video gösterilir, kimi satırda fotoğraf kimi satırda link paylaşımı vs. gösterilir. Şimdiye kadarki RecyclerView ve ListView geliştirmelerimizde tüm satırların layout'ları aynıydı. Şimdi ise farklı satır layout'ları nasıl geliştirebiliriz, örneğin video gösterilecekse farklı bir satır layout gösterilsin,  fotoğraf gösterilecekse farklı bir satır layout gösterilsin vs. istiyoruz.
Aşağıdaki linkten ListView'de Heterogenous Layout kullanmak anlatılır ancak ListView verimsiz olduğu için bu konuyu atlayalım.
RecyclerView verimlidir şimdi RecyclerView'de Heterogenous Layout göstermeyi aşağıdaki linkten öğreneceğiz:

16_9_1_RecyclerView isimli projeyi geliştirip
16_9_1_RecyclerView2_heterogenous_layout
isimli projeyi geliştireceğiz bu derste.....

Hiç yorum yok:

Yorum Gönder