ListView için Custom Adapter Yazımı ve Kullanımı
Android programlamaya başladığımda karşılaştığım ilk sorun sanırım Custom Adapter yazımı olmuştu. İnternetten takip ettiğim eğitim videoları dizisinde eğitmen Android Kütüphanesi’nde bulunan Basic Adapter yardımı ile programını geliştiriyordu. Basic adapter ekranda sadece text tipinde veri göstermek için ideal ama design yapısı karmaşıklaştığı zaman neredeyse her zaman Custom Adapter yazmak zorundayız. Şimdi isterseniz öncelikle adapter nedir ve neden gereksinim duyarız ondan bahsedelim…
Şimdi sizin aklınızda iki tane soru var: Bir adapter nedir? İki neden kullanılır ?
Bir uygulama geliştirdiğimizde, ekranı aşağı doğru kaydırdıkça yeni item’ların sıralı bir şekilde gelmesi için bir ListView kullanmamız gerekir. Bu ListView içerisinde programımızın arka tarafında gelen verilerin ekranda hangi düzende ve nasıl görüneceğini belirleyen yapı ise adapter’dür. Yani adapter, elimizdeki verilerin uygulamanın arayüzünde nasıl görüntüleneceğinden sorumlu olan yapıdır.
Yukarıdaki şekilde görüldüğü gibi bizim elimizde bir data source’umuz olsun, bu data bir api aracılığı ile network üzerinden de gelebilir, local’deki bir database’den de, birazdan bizim de örnek üzerinde göstereceğimiz gibi kendi oluşturduğumuz dummy bir data olarakta gelebilir. Elimizde içerisinde obje barındıran bir ArrayList olsun.Bizim adapter kullanırken ki amacımız bu ArrayList’in içerisindeki her bir objenin View item olarak ListView’da görüntülenmesidir. İşte bu dönüştürme işlemini gerçekleştiren yapıya adapter denir. Yani adapter, elimizdeki veriyi, görsel arayüzde o veriye ihtiyaç duyan View’a bağlar.
Peki ya performans ?
Eveettt, şimdi Android işletim sisteminin bize sunduğu nimetlerden bahsedebiliriz. Elimizdeki ListView’a 1000 tane farklı item eklemek zorunda kalsaydık sizce ne olurdu? ListView’ın 1000 tane satır yaratması mı gerekirdi? Peki bu durum memory kullanımı açısından ciddi bir sorun yaratmaz mıydı ?
Tabiki de böyle bir durum hiçbir zaman gerçekleşmiyor. ListView’ın içerisinde bir geri dönüşüm (Recycling) yapısı mevcuttur. ListView’ı bir adapter’e bağladığımız zaman ListView’ın ekranda görünen kısmına ne kadar satır sığıyorsa sadece o kadar satır yaratılır. Biz ListView’ı aşağı doğru kaydırdığımız zaman (scrolling) ekranın görüntü alanından çıkan satır’ın yerine alttan yeni gelen eleman için yeni bir satır eklenir.
Yani yukarıdaki resimde de görüldüğü gibi elimizde 1000 tane farklı item dahi olsa sadece 7 tane satır yaratarak bu sistem döndürülebilir. Buraya kadar teknik bilgi vermenin yeterli olduğunu düşünüyorum, artık kod kısmına geçebiliriz.
Öncelikle bu basit bir örnek olacak onu belirtmek isterim. Gerekli yerlerde açıklamaları yapacağım.Özelleştirmeleri yaparak yapınızı kendi istediğiniz gibi değiştirebilirsiniz.İlk olarak ListView’ımızın içerisinde bulunduğu activity_main.xml layout’unu tanımlayarak işe başlıyabiliriz.
Bir RelativeLayout içerisinde ListView tanımlayarak işe koyulduk. Burada unutulmaması gereken şey ListView’a bir id vermek. Daha sonra Java kod kısmında bu id yardımıyla ListView’a erişeceğiz.
Şunu unutmamak gerek; ListView sadece bizim istediğimiz içerisideki her bir satıra dikey sırada elemanlar ekleyip, parmağımızla kaydırarak bu elemanları görüntülememizi sağlayan yapıdır. Yani henüz yukarıda ListView içerisinde her bir satırda ne gibi elemanlar olacak ve bu elemanlar nasıl bir görünüme sahip olacak bunu belirtmedik. Bunun için bizim aşağıda görüldüğü gibi bir list_view_item.xml dosyası tanımlamamız gerekmektedir.
Yine ListView’ı tanımlarken olduğu için erişmek istediğimiz ve daha sonrasında o View’lara herhangi bir değer atayacağımız View’lara birer id veriyoruz. Bu yukarıda gördüğünüz tasarımda ben sadece sol tarafta bir 56*56 dp’lik bir image dosyası kullandım. Onun yanında da alt alta sıralanacak şekilde kişinin adının ve adresinin yazdığı iki tane TextView kullandım. Artık java sınıfı olarak bir Person class’ı oluşturabiliriz.
Evet basit bir Person class’ı oluşturduk. Field olarak name, address ve benim önceden drawable klasörü altına attığım bir image dosyasının id’sini bulunduruyor. Constructor metodumuzu ve getter metodumuzu da yazdıktan sonra (Getter metodu adapter içerisinde gerekli objeden gerekli attribute çekilirken gerekecek.) artık adapter’ümüzü yazabiliriz. Tamamını tek kod olarak değil de parça parça gidersek daha anlaşılır olacak sanırım.
Yukarıda görüldüğü gibi bir CustomListViewAdapter yaratıyoruz ve bunu ArrayAdapter sınıfından extend ediyoruz. Bu sayede üst sınıf olan ArrayAdapter’ün super metodunu kullanabileceğiz ve üst sınıfın implement ettiği metodları biz de Override ederek yazacağız. Böylece adapter yapımız işletim sisteminin istediği gibi kusursuz çalışmış olacak.
Yukarıda field olarak tanımladığım bölümlerin ne yaptığını daha sonrasında detaylıca anlatacağım. Yapımızın constructor metodu context ve ArrayList<Person> alıyor. Android Studio kullanan arkadaşlar Ctrl+P tuşlarına basarak başka hangi constructor’ları aldığına bakabilirler, bu örnek için en ideali bu olduğu için ben bu constructor’ı kullandım. Bizim context’i kullanarak bir infilater yaratmamız gerek. Daha sonrasında bu infilater sayesinde list_view_item.xml layout‘umuzu doldurulması için hazırlayacağız.
Yukarıda Override etmemiz gereken üst sınıfın metodları var. Bizim için burada önemli olanlar getCoun() ve getItem(), getCount persons ArrayList’inde kaç tane eleman olduğunu döndürür, getItem() ise hangi satırdaki item için işlem yapıyorsak o satırdaki item’ı döndürür.
Bu yukarıda anlattığım metodların dışında birde bütün bu list_view_item.xml’i doldurma işleminin gerçekleştiği getView() metodu var. Ama ona geçmeden önce ViewHolder’dan bahsetmek istiyorum. Bu sıradaki kodu getView() metodundan önce anlatıcam ama siz yazarken getView’dan sonra yazın daha düzenli bir görüntüsü olur kodunuzun.
ViewHolder ListView kullanımının performans açısından iyileştirilmesini sağlayan bir pattern’dır. Biz getView() metodumuzu ViewHolder kullanmadan yazarsak eğer her eleman için getView metodu çağırıldığında view injection dediğimiz xml dosyamızdaki view’ların id’leri üzerinden bulunarak arka planda casting iş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. Şimdi artık getView() metodumuzu yazarsak:
Görüldüğü gibi başlangıçtaki if komutumuz convertView’ın sadece null olması koşulunda, yani sadece ilk seferde inflater tarafından doldurulur. list_view.item.xml dosyamız içeriği doldurulmak üzere kod kısmına getirildi. Şimdi list_view_item içerisindeki 1 ImageView ve 2 TextView’ın id’leri üzerinden bulunup java tarafındaki değişkenlere atanmasını sağlayabiliriz. Dikkat ederseniz eğer holder’lar bu oluşturulan view’ları tutmaktalar. Artık getView metodu tekrar çalıştığında biz else komutu içerisine girip sadece önceden oluşturulan bu tag’i alacağız. Persons ArrayList’in get(position) metodu sayesinde ilgili datayı person nesnesine atıyoruz ve burada artık data source’dan gelen data’yı view’larımıza set edebiliyoruz. Kodun tam hali şu şekilde:
Küçük ama önemli bir uyarı. Yukarıdaki kod da Override ettiğimiz getItem metodu zaten
Person person = person.get(position); satırının yaptığı işi yapıyor. Onu benim gibi field’da tutmanız gerekmiyor. Performans kaybı yaşamazsınız ama iyi bir alışkanlık değil. Düzeltme için Said Tahsin Dane’ye teşekkürler.
Eveeettt sonunda elimizde bir adapter var artık bu adapter’ü bir ListView’e set edip ekranda elimizdeki dataları görüntüleyebiliriz. Bunun için MainActivity.java class’ımız içerisinde field’larımızı tanımlayarak işe başlayabiliriz.
Bundan sonra onCreate metodumuz içerisinde gerekli ilkleme, dummy data oluşturma ve ListView’a bir adapter set etme işlemlerini gerçekleştirebiliriz.
Kod yazım alışkanlığı gereği Refactor işlemleri yaparak aynı işi yapan kod kalabalıklarını metoda çıkarmanız kodunuzun okunabilirliğini arttırır.
Evet artık uygulamamızı telefonumuza yükleyebiliriz.
(Not: fillArrayList metodu içerisinde dikkat ederseniz drawable’a attığım bir image dosyasını çektim, siz de kendi image dosyanızı koymayı unutmayın.)
Artık list_view_item.xml ‘i özelleştirip orijinal tasarımlar ortaya çıkartmak sizin göreviniz. Aşağıda benim özellikle yararlandığım linkleri bulabilirsiniz. Sevdiğiniz tasarımları gerçekleştirmeye çalışmaktan kaçınmayın. Bir sonraki konuda görüşmek üzere….
Projenin kaynak kodları için :
Design Material’leri için: