ListView Kullanımı

ListView Kullanımı
Mobil cihazlarda kullanıcılara birbiriyle ilişkili verileri listeyle vermek çok tercih edilen bir yöntemdir. Kullanıcı listedeki veriler içinde tek parmağıyla yukarı-aşağı sürükleme yaparak dolaşabilir.
Android'te listeleme işlemleri yapabilmek için ListView isimli görsel öğe kullanılır. ListView, kendi içinde satır satır TextView öğeleri bulunduran bir yapıdır. Bunun gibi birçok View'dan oluşan görsel öğeler ViewGroup olarak da sınıflandırılır.

Aşağıdaki gibi bir ListView'ı birkaç ülke ismiyle dolduracağız. Ardından listede üzerine dokunulan ülkenin adını bir diyalog penceresinde göstereceğiz.
Yerleşim dosyalarını düzenleme
Öncelikle ListView'ı üzerinde tutacak olan Activity'nin (MainActivity.java) tasarımını çıkaralım. Bunun için res/layout/ altındaki activity_main.xml dosyasını kullanıyoruz.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" >
    </ListView>

</RelativeLayout>
Gördüğünüz gibi ListView'ı bir RelativeLayout içinde tutuyoruz. Kodların kalabalık olması bu yüzden. Burada asıl önemli şey android:id="@+id/listView1"  satırı. Burada ListView'ımıza listView1 ismini veriyoruz.

XML üzerinde bir view nesnesine isim verirken o nesnenin android:id özelliğine @+id/ ön ekini ve ardından da istediğimiz ismi veriyoruz.

Bu ismi (id) daha sonra Java sınıfımız içinde ListView'a erişmek için kullanacağız.

Gösterilecek verileri ayarlama
Şimdi listemizde göstereceğimiz ülke isimlerini taşıyan bir String dizisi tanımlayalım. Bunu MainActivity sınıfımızın bir alanı olarak tanımlıyoruz. Böylece bu Activity oluşturulduğu an elimizde bir ülke listesi olacak.

//Dosya Adı    : MainActivity.java
package org.gelecegiyazanlar.ornek.listview;
import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {

    private String[] ulkeler = 
    {"Türkiye", "Almanya", "Avusturya", "Amerika","İngiltere",
        "Macaristan", "Yunanistan", "Rusya", "Suriye", "İran", "Irak",
        "Şili", "Brezilya", "Japonya", "Portekiz", "İspanya",
        "Makedonya", "Ukrayna", "İsviçre"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
Bu basit String dizi tanımlamasından sonra artık yukarıda sözünü ettiğimiz Java sınıfı içinde ListView'a erişmeyi ve onu yönetmeyi gerçekleştirelim. Bunun için şu üç adımı gerçekleştirmelisiniz.

(A) Kullanıcıya gösterilen ListView'a ulaşabilmek için onun bir referansını almak
(B) ListView'ımızı verilerle (ülke adları) buluşturacak olan Adapter'ı tanımlamak
(C) ListView'ımıza, bağlanacağı Adapter'ı belirtmek

ArrayAdapter nedir?
Adapter bir veri kaynağıyla, veriye ihtiyacı olan nesneyi birbirine bağlamaya yarayan yapılardır. ListView gibi bir dizi veriyi içinde bulunduran yapılara, bu verileri ArrayAdapter aracılığıyla veririz. ArrayAdapter bir veri kaynağındaki verileri (kısaca, veri modelini) görsel öğelerde kullanmaya uygun hale getirir. Bunun görsel anlatımı şu şekildedir:

ArrayAdapter-UI-Data Set

Prizdeki elektrik, telefonumuzun kullanacağı elektrikten farklı olduğu için bir dönüşüme ihtiyaç duyar. Bu dönüşümü adaptör aracılığıyla yaparız. Adaptör, şebekeden gelen elektriği alır ve telefonun kullanacağı türe çevirir.
Verileri gösterme
ArrayAdapter'ın veri ile görsel öğe arasındaki görevini inceledikten sonra kodumuza (MainActivity.java) geri dönelim:
//Dosya Adı: MainActivity.java

package org.gelecegiyazanlar.ornek.listview;

import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.app.Activity;

public class MainActivity extends Activity {

    private String[] ulkeler = 
        {"Türkiye", "Almanya", "Avusturya", "Amerika","İngiltere",
            "Macaristan", "Yunanistan", "Rusya", "Suriye", "İran", "Irak",
            "Şili", "Brezilya", "Japonya", "Portekiz", "İspanya",
            "Makedonya", "Ukrayna", "İsviçre"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //(A) adımı
        ListView listemiz=(ListView) findViewById(R.id.listView1);
        
        //(B) adımı
        ArrayAdapter<String> veriAdaptoru=new ArrayAdapter<String>
        (this, android.R.layout.simple_list_item_1, android.R.id.text1, ulkeler);
        
        //(C) adımı
        listemiz.setAdapter(veriAdaptoru);

    }
}
Ana kodumuzu Activity'nin onCreate() metoduna yazıyoruz. Böylelikle Activity oluşturulur oluşturulmaz verilerimizi ArrayAdapter yardımıyla ListView'da göstermiş oluyoruz. Activity'nin her onCreate() oluşunda çalışacak kodları şöyle açıklayabiliriz:

(A) adımında, önce liste öğemize (listemiz) bir referans değişkeni tayin ediyoruz ki artık o öğemizi bir değişken ile kullanabilelim. findViewById() ile XML'de android:id ile isim verdiğimiz öğelere erişiriz. 

(B) adımında, new deyimiyle bir ArrayAdapter oluşturuyoruz. Oradaki <String>, ArrayAdapter'ın içinde tutacağı verilerin türünü belirten bir deyim. ArrayAdapter'ın yapılandırıcı metodundaki parametrelerse şu anlama geliyor: 

this: Context. Yani bağlam. Bu this deyimiyle değer olarak Activity'nin kendisinin dönmesini sağlıyoruz (Activity sınıfının içindeyiz). ArrayAdapter, çalıştığı yerle ilgili bilgiyi Context'e ulaşarak bulur.
android.R.layout.simple_list_item_1: resource. Bu parametre, kullanıcının göreceği listenin yerleşim dosyasıdır. Listemiz bu layout dosyasından bina edilecektir.
android.R.id.text1: textViewResourceId Bu parametre, layout dosyasındaki TextView'ın adını (id) verdiğimiz yerdir. Her bir veri buradaki bir TextView'a basılır.
(C) adımında artık ayarlamalarını bitirdiğimiz ArrayAdapter'i listemize gösteriyoruz.

Şu an uygulamamızı çalıştırdığımızda şöyle bir görüntü elde ederiz:
device-2013-11-25-175154.png
Tıklama olaylarını yakalama
Sıra geldi listeye dokunulduğunda/tıklandığında olacaklara. Bunun için liste öğesine bir tıklama dinleyici tayin etmemiz gerekiyor. Bu tıklama dinleyici ile tıklanan öğenin sırasını alıp, aynı sırayı ulkeler isimli dizide indis olarak kullanacağız. Bu yolla ülke adını alıp ekrana diyalog olarak göstereceğiz.

NOT: Aşağıdaki kodu onCreate() metodunun içine ve listemiz.setAdapter()'dan sonra yazmalısınız.

//Dosya Adı: MainActivity.java

    listemiz.setOnItemClickListener(new OnItemClickListener() {

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {

        AlertDialog.Builder diyalogOlusturucu = 
                new AlertDialog.Builder(MainActivity.this);

        diyalogOlusturucu.setMessage(ulkeler[position])
                         .setCancelable(false)
                         .setPositiveButton("Tamam", new OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
        diyalogOlusturucu.create().show();

    }
});
NOT: Liste için tıklama/dokunma olaylarını dinlediğiniz setOnItemClickListener metodunun doğru çalışması için kodunuzun başında 
import android.widget.AdapterView.OnItemClickListener;
ifadesi bulunmalıdır.
listemiz.setOnItemClickListener() metodu, parametre olarak öğe tıklamalarını yakalamak için oluşturulmuş bir arayüz (interface) alıyor. new ile hemen anonim bir setOnItemClickListener oluşturuyoruz ve girilmesi zorunlu metotları Android Studio yardımıyla (CTRL+I ve CTRL+O tuşlarına basıp "Implement" ve "Override" özelleklerini kullanarak) ekliyoruz.

Listedeki bir öğeye tıklandığında onItemClick() metodu çalıştırılır.Bu metod bize hangi View'a (burada ListView) tıklandığını, kaçıncı sıradaki (position) öğeye tıklandığını ve o öğenin adının (id) bilgisini verir.

Kodumuzdaki önemli nokta AlertDialog oluşturucuyu tanımladıktan sonraki
diyalogOlusturucu.setMessage(ulkeler[position])

kısmı. Burada oluşturacağımız diyalogun göstereceği yazıyı ayarlıyoruz. Parametre olarak ulkeler dizisinin position indisli öğesini veriyoruz. position, onItemClick() metoduyla bize geliyor. Tıklanan öğenin listedeki sırasını elde etmiş oluyoruz.

Diyalog kutusunu oluştururken bu sefer setPositiveButton(text, listener) metodunda, gösterilecek düğmede yazacak metni ve tıklandığında olacakları yazıyoruz. İkinci parametre olarak yine bir tıklama dinleyiciye ihtiyacımız var. Bununla bu düğmeye tıklandığında (onClick) olacakları yazıyoruz. Buna göre diyalog kutumuzu kapatıyoruz.

Sonuç
Tıklama/dokunma dinleme işlemi için yazdığımız anonim sınıflardan oluşan bu kod yoğunluğu gözümüzü korkutmamalı. Genelde sık sık tekrar edilen kodlardan oluşuyor. Üstelik bu kodu yazarken editörün yardımını da alıyoruz.

Dokunma olayları için yazdığımız koddan sonra uygulamamızda bir listeye tıklandığında şöyle bir görüntü elde ediyoruz: 


Bu eğitim içeriğinde basit bir ListView gösterimini gerçekleştirdik. Ayrıntılı bir ListView örneği için Android 301 eğitimlerindeki ListView Özelleştirmesi dersine bakabilirsiniz.
ListView Özelleştirme
Daha önceki bölümlerde ListView kullanarak liste oluşturmayı ve bir diziyi (array'i) veri kaynağı olarak gösterip listenin elemanlarını doldurmayı görmüştük. Bu bölümde ListView'a biraz daha yakından bakacağız.

Bildiğiniz üzere ListView nesnesi, TextView, EditText ya da Button nesneleri gibi basit bir View nesnesi değil, LinearLayout, GridView gibi taşıyıcı olarak görev yapan bir View nesnesidir. Bunun anlamı kendi içinde başka View'ları barındırıyor olduğudur. Önceki derste değinildiği gibi ListView, her satırında bir TextView bulundurur. Bu sıradan bir ListView için geçerli. İsterseniz her satırında daha farklı bir View düzeni kurabilirsiniz.


Ana yerleşimi tasarlamak
İlk olarak yeni bir proje oluşturalım ve MainActivity.java dosyasını ve buna ait activity_main.xml isimli layout dosyasını düzenleyelim. MainActivity'nin arayüzünü bir RelativeLayout olarak gösterelim ve içine de liste id'sini taşıyan bir ListView ekleyelim:
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

<ListView
    android:id="@+id/liste"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

</RelativeLayout>
Bu kadarını tamamladığımz layout şöyle olacaktır:


Gördüğünüz gibi özelleştirilmemiş bir liste oluştu. Java kodu tarafında listeye erişebilmek için bu öğenin android:id özniteliğindeki liste değerini kullanacağız.

Satır yerleşimini tasarlamak
Android 201'de yer alan önceki örnekten farklı olarak, listedeki satırda TextView ile beraber bir ImageView göstereceğiz. Şunun gibi:

Bu yerleşim dosyasını da tıpkı activity_main.xml gibi projenizin layout/ dizininde tutabilirsiniz.

Bu görünümü sağlayacak satir_layout.xml yerleşim dosyası şöyle:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <ImageView
       android:id="@+id/simge"
       android:layout_width="48dp"
       android:layout_height="48dp" />

    <TextView
       android:id="@+id/isimsoyisim"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content" />

</LinearLayout>
Birazdan bu satir_layout.xml dosyasını, gösterilecek listenin birer satırı olacak şekilde kullanacağız. Bunun için bir inflating yani (dilimize çevirmek gerekirse) şişirme işlemine başvuracağız.

NOT: Bu dosyada LinearLayout'un android:orientation özelliğinin horizontal olmasına dikkat ediniz. Böylece yavru elementler ImageView ve TextView, LinearLayout içinde yatay sırada konumlanabileceklerdir.

Yapmak istediğimiz yerleşimin son halinin hatları kabataslak şöyle olacaktır:



Veri modelini oluşturmak
Özel ListView'lar oluşturmadaki önemli bir husus, liste satırlarında gösterilecek verilerdir. Android 201'deki örneğimizde ListView'ı doldururken sadece String tipli elemanlardan oluşan bir dizi kullanmıştık. Ayrıca ihtiyacımız basitti: verilerimizi (ulkeler) önceden belli bir listeye (android.R.layout.simple_list_item_1), önceden belli bir TextView'ın (android.R.id.text1) içine yerleştirip koymak ve kullanıcıya göstermek. İşte bu alma-gösterme işlemini tam olarak ArrayAdapter yapıyordu.

Şimdiki senaryoda verilerimizi, kendi oluşturduğumuz (activity_main.xml'deki liste) bir listede, kendi oluşturduğumuz bir düzende (Bkz: satir_layout.xml dosyası) göstereceğiz. Bu sebeple kendi Adapter'ımızı yazmak zorundayız. Bir Adapter ile uygun verileri, uygun yerleşimlere bağlayabiliyoruz. (Bkz: Adapter'ın tanımı)

Birer veri olarak ele alıp göstereceğimiz satırın tasarımına tekrar bakalım:

Sol tarafta bir cinsiyet simgesi olacak ve yanında da kişinin adı ve soyadı yazacak. Bu iki veriyi temsil eden bir sınıf yazmalıyız. Nesneye yönelik programlama yaklaşımında böyle sınıflara model sınıfları da denir. Aşağıda örnek bir sınıf görüyorsunuz:

package org.gelecegiyazanlar.customlistview;

public class Kisi {
    private String  isim;
    private boolean kadinMi;

    public Kisi(String isim, boolean kadinMi) {
        super();
        this.isim = isim;
        this.kadinMi = kadinMi;
    }

    @Override
    public String toString() {
        return isim;
    }

    public String getIsim() {
        return isim;
    }

    public void setIsim(String isim) {
        this.isim = isim;
    }

    public boolean isKadinMi() {
        return kadinMi;
    }

    public void setKadinMi(boolean kadinMi) {
        this.kadinMi = kadinMi;
    }
}
Burada isim alanı ile kullanıcını adını String tipinde tutmayı hedefliyoruz. KadinMi isimli boolean alan ile Kisi'nin cinsiyetini tutacağız. Kadınları temsil etmek için bu değeri true yapacağız. Nesne için oluşturduğumuz yapıcı (constructor) metot, isim ve cinsiyet değerlerini hızlı bir şekilde atamamızı sağlıyor. Buradaki sınıfa göre kaynak kod içinde new Kisi("Ahmet Yılmaz", false) şeklinde bir deyimle nesne oluşturursak, Ahmet Yılmaz adına sahip erkek cinsiyetinde birini temsil eden bir Kisi nesnesi oluşturmuş olacağız.

Listede gösterilecek verileri oluşturmak
Veri modelimize uygun bir sınıf oluşturduğumuza göre listemizde görüntülenecek veri bütününü-listeyi de oluşturabiliriz. Bunun için içini Kisi nesneleriyle dolduracağımız bir ArrayList yeterli gelecektir. Hemen MainActivity dosyamızı açalım ve bir ArrayList oluşturalım:
public class MainActivity extends Activity {
    final List<Kisi> kisiler=new ArrayList<Kisi>();

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

        kisiler.add(new Kisi("Ahmet Yılmaz", false));
        kisiler.add(new Kisi("Ayşe Küçük", true));
        kisiler.add(new Kisi("Fatma Bulgurcu", true));
        kisiler.add(new Kisi("İzzet Altınmeşe", false));
        kisiler.add(new Kisi("Melek Subaşı", true));
        kisiler.add(new Kisi("Selim Serdilli",false));
        kisiler.add(new Kisi("Halil İbrahim",false));
    }
}
kisiler isimli ArrayList'imizi MainActivity sınıfının bir alanı (field) şeklinde tanımladık. İsterseniz onCreate() metodunda da tanımlayabilirsiniz. final tanımlama yapmak da size kalmış. Bir kere oluşturup değer verdikten sonra yeniden bir atama (ArrayList'ten başka bir türe vs) yapmayacağımız için final olarak belirliyoruz.

Özel bir Adapter oluşturmak
Geliyoruz en önemli kısma. ListView'ı verilerle doldurabilmek için bir Adapter oluşturmamız gerekir. Kendi Adapter nesnemizi oluşturabilmek için Android SDK'de yer alan BaseAdapter sınıfını temel sınıf olarak kullanabiliriz. Bu sınıftan türetilen sınıfların sahip olması/ezmesi gereken dört metot vardır:

getCount(): int değer döner. ListView'da gösterilecek satır sayısını ifade eder. Verilerimizi barındıran ArrayList'in boyutu (size()) burada bize yarayacak.

getItem(int position): Object değer döner. position ile belirtilen satıra denk düşen nesneyi döndürür. Bu nesne satır olarak gösterilecek nesnedir. Bundan dolayı Object yerine doğrudan model sınıfınızdan oluştuğunuz nesneyi de dönüş türü olarak belirleyebilirsiniz.

getItemId(int position): long değer döndürmelidir. Veri listesinde position ile sırası belirtilen satırın kimlik numarasını (id) döndürür. Liste içeriğini veri tabanına kaydedecekseniz ya da orada eşlemeler yapacaksanız önem kazanır.

getView(int position, View convertView, ViewGroup parent): View değer döner. position ile sırası belirtilen satır için bir View döndürür. Bu metot içindeyken her satır için XML'i okuyup View haline getirme işlemi (inflating) yaparız. Bu hususta bize LayoutInflater servisi yardımcı olacaktır.

Örnek için oluşturduğumuz Adapter şöyle (MainActivity.java ile aynı dizinde yer alabilir):

İncelemeye yapılandırıcı metottan başlayalım:

Yapılandırıcı metodumuzun ilk parametresi, Adapter'ın bağlı bulunacağı uygulama parçasıyla (ListView) ilgili: Context yani Bağlam. Böylece OzelAdapter'a çalışacağı ortamla ilgili temel bilgileri bu parametreyle geçirmiş oluyoruz.

List<Kisi> kisiler parametresi de adaptörümüzün ihtiyaç duyacağı verileri alacağımız yer. <Kisi> ifadesiyle içinde sadece Kisi bulunduran List'i kabul ettiğimizi belirtiyoruz. Verileri alma işlemini mKisiListesi = kisiler ifadesiyle hallediyoruz.

mInflater değişkenine activity üzerinden bir sistem servisini referans gösteriyoruz: Layout Inflater Service. Bu servis, yerleşimleri kullanıcıya gösterebilmek için onları önce XML'den okuyup ardından View'a çevirme işlemini yapar.

getCount() ve getItemId() metotları, yukarıda tanımladığımız işlemleri yapıyor. getItem() metodu da aynı şekilde mKisiListesi'nin position indisine sahip nesneyi döndürüyor.

Önemli bir metot olan getView()'a göz atalım:

Bu metodun satır olarak gösterilecek View'ı döndürdüğüne yukarıda değinmiştik. Bu amaçla satirView adında bir View oluşturuyoruz. Sonra bu View'a

ile satir_layout.xml dosyasındaki yerleşimi veriyoruz. Böylece satirView üzerinden satir_layout.xml'deki öğelere erişebiliyoruz:

Dikkat ederseniz findViewById() metodunu satirView üzerinde çalıştıyoruz.

Ardından View olarak döndürülecek satır için Kisi nesnesini yine position indisiyle mKisiListesi üzerinden alıyoruz ve kisi değişkenine atıyoruz.

Satırdaki ImageView'da cinsiyet simgesini gösterebilmek için önce kisi referansı üzerinden nesnenin isKadinMi() metoduna erişiyoruz:

isKadinMi() true sonuç dönerse ImageView'a kadın simgesini, false dönerse erkek simgesini veriyoruz.

Tüm bu işlemlerin ardından satirView hazır oluyor ve onu return ediyoruz.

Buraya kadar oluşturduklarımızla beraber projemiz Android Studio'nun package explorer bileşeninde şöyle yer edinecektir:



Listeyle adaptörü bağlamak

Yukarıda kisiler ArrayList'ini hazırlamıştık fakat onu adaptörümüze göndermemiştik. Artık verileri kendi OzelAdapter sınıfımıza gönderebiliriz.

Önce ListView'ımızı ana yerleşim dosyasından alalım. Ardından da OzelAdapter sınıfından bir nesne oluşturalım. Sonra da listenin adaptörünü gösterelim:

OzelAdapter sınıfından nesne oluştururken parametre olarak this ve kisiler geçiyoruz. Buradaki this, MainActivity'de olduğumuz için MainActivity'yi işaret ediyor. Böylece OzelAdapter'a çalışacağı bağlam konusunda bir parametre geçmiş oluyoruz. Hatırlayın: Adapter, veri ile arayüz arasında bir köprü idi. Bu iki parametreyle ona veriye (kisiler ArrayList'i) ve arayüze (listemiz) ilişkin parametre geçmiş oluyoruz.

Özel bir ListView oluşturduk. Görselleri de bulunduran bir örnek için sayfadaki projenin kaynak kodlarını indirebilirsiniz. Yukarıda Android 4.0 üzerinde ekran görüntüsünü verdiğimiz örnek Android 2.3.5 üzerinde şöyle görünecektir:

Yorumlar

Bu blogdaki popüler yayınlar

İç İçe Döngüler

CSS Bir Elemanın Genişliği ve Yüksekliği

JavaScript Dilinde Fonksiyon Çağırma Teknikleri