Liste Tarzı View Elemanları

Belirli bir veri grubunu listelemekte kullanılan veri elemanlarıdır.
Bu elemanların Android'de uygulama geliştirme ortamında diğer GUI elemanlarının geliştirilmesinden biraz daha farklıdır.

Bu kontroller tipik olarak içerisindeki veriden bağımsızdır.
Yani bu elemanlar verilere ilişkin Collection'ı kendi içerisinde tutmazlar.
Yalnızca ilgili Collection'ın referansını tutarlar.Bu yaklaşım modelin yani verinin gösterilişinden ayrılmasıdır.
Modelin gösterilişinden tamamen ayrılması gibi bir durum yoktur.
Yine model içerisinde verinin nasıl gösterileceğine ilişkin bir takım bilgilerde verilebilmektedir.
Bu işlem ilk bakışta kullanışsız görülebilir.Ancak modele ilişkin referans kullanılarak veriler üzerinde işlem yapılması programcının işini kolaylaştırabilir.

Bazı programcılar bu tarz kullanımı sevmeyebilirler.Bu durumda ilgili kontrol sınıfından türetme yaparak verilerin yani modelin kontrol içerisinde kontrolle beraber kullanımını sağlayabilirler.
Bu işlem ileride ele alınacaktır.

Verilerin kontrolü aktarılacağı Collection sınıflar için tipik olarak Adapter sınıfları kullanılmaktadır.En çok kullanılan Adapeter sınıf ArrayAdapter isimli Generic bir sınıftır.
Bu sınıf tüm diğer Adapter sınıflarda olduğu gibi android.widget.BaseAdapter isimli bir sınıftan türetilmiştir. ArrayAdapter sınıfı da Android.Widget sınıfından türemiştir.

ArrayAdapter sınıfının 6 tane başlangıç metodu bulunmaktadır.
Tüm metodların ortak özellikleri Context parametreli eleman istemesidir.

ArrayAdapter sınıfı tutacağı verileri çeşitli şekillerde alabilmektedir.Örneğin verileri proje içerisinde belirtilmiş olan kaynaktan(Resources) alabilir.Ya da bir veriyi doğrudan bir dizinden alabilir.Ya da bir veri grubunu Liste tarzı bir Collection grubundan alabilir.

Programcı ArrayAdapter sınıfının herhangi bir başlangıç metodu ile nesne yaratabilir.

ArrayAdapter sınıfının başlangıç metodlarının hepsinde bulunan ortak int türden Resoruce isimli bir parametre bulunmaktadır.
Bu parametre projeyi geliştirenlerin belirleyeceği yani proje içerisinde belirlenmiş olan bir kaynağa ait değer olabilir.Ya da Android SDK içerisinde Android paketi altındaki R isimli sınıfın içerisinde bildirilen kaynak isimleri olabilir.
Bu kaynaklar Andorid Sdk içerisinde belirlenmiştir.Ve çoğu zaman bunların kullanılması yeterli olmaktadır.

Sınıfın kullanımı dinamik büyüyen dizi Collectionlarına çok benzemektedir.

  • AddxxxaddX metodlarıyla bir nesne ya da nesnelerden oluşan başka bir Collection ya da bir dizi eklenebilmektedir.
  • getCount metoduyla ile kaç eleman tutulduğu alınabilir.
  • clear metodu ile tüm Collection temizlenir.

Sınıfın diğer metodları kullanıldıkça ele alınacaktır.

ListView View Elemanı

Listeleme işi için kullanılan tipik bir View elemanıdır.bir çok uygulamada kullanılmaktadır.Görünüm olarak tuttuğu elemanları yukarıdan aşağı gösteren bir View elemanıdır.

Anahtar Notlar
Liste Tarzı View elemanları , Container tipi Viewlar içerisindedir.Bir Container olduğu için liste tarzı View elemanları da Android.View.ViewGroup'dan türemiştir.


Notlarım:
ListView Ekleme

  • ArrayAdapter içerisine bu diziyi ver Consturtor da (this , android.R.layout.simle_list1 , products) diyerek hazır bir görünümden alıntı yapılır.
  • listView.setAdapter(adapter) diyerek list'e Adapter eklenir.
  • Yeni ürün eklemede product içerisinde ya da Adapter'e ekleme yapılabilir.
  • Buton ve bir editText ekle editText'ten girilen ürünü butona tıklayınca ListView'e ekle(String diziyi ArrayList'e çevir)
  • clickListener setOnItemClickListener(new AdapterView.OnItemClickListener())
  • onItemClick parametrelerinden position listview.getItemAtPosition(position) diyerek listeden seçilen eleman alınabilir.

Örnek Kod :[Dropbox]022-ListViewArrayAdapter

Programcı açısından iyi bir yöntem Adapter sınıfını ilişkin referansı sınıfın veri elemanı olarak tutmaktır.

Burada dikkat edilmesi gereken önemli nokta eğer Adapter sınıfına bir dizi eklenirse , bir diziyi dinamik olarak büyütmek mümkün olmadığından dinamik büyüyen bir Collection kullanılması gereklidir.

Şüphesiz programcı Adapter'a verdiği Collection referansını da kullanabilir.Ancak Adapter referansını kullanması programlama tekniği açısından daha uygundur.

ListView Kontrolünün üzerinde herhangi bir elemana (listedeki elemana) click yapıldığında onItemClick adında bir metod çağrılır. Programcı setOnItemClickListener metoduyla onItemClick metodunu override ederek bu işlemi yapabilir.

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

onItemClick metodunun ,
position parametresi ilki 0 olmak üzere ListView üzerindeki elemanın index numarasını göstermektedir.Android.R.layout.simple_list_item_1 değeriyle verilen ArrayAdapter nesnelerinde ListView için position ve id elemanları aynı değeri göstermektedir.Id elemanının long türden olduğuna dikkat edilmedilir.

Eğer programcı adapter sınıfı ile ListView'ı birlikte kullanmak isterse, adeta Listview'ın kendi Adapter'i olmasını isterse bu durumda ListView sınıfından türetme yapabilir.

Örnek Kod : [Dropbox]023-ListViewWithAdapter

Özetle programcı adapter referansını kullanıyorsa ilgili Adapter sınıfının çağrılıp çağrılmadığına göre notifyDateSetChanged metodunu çağırmalıdır.Eğer Adapter'a eklenen Collection'la ekleme yapılıyorsa kesinlikle çağrılmalıdır.

Anahtar Notlar:
Her ne kadar dökümanlarda yazmasa da ArrayAdapter sınıfı için notifyDateSetChanged metodunun çağrılması tavsiye edilmektedir.

Anahtar Notlar:
Bazı Adapter sınıfları ListView'e üzerinde yapılan değişiklikleri bildirmez.Bunun için Adapter sınıflarının notifyDateSetChanged metodu çağrılabilir.Zaten Adapter sınıfına verilen Collection referansı ile ekleme yapılırsa bu metod kesinlikle çağrılmalıdır.

Anahtar Notlar:
Adapter ve AdapterView ilişkisi observer tasarım kalıbıyla implemente edilmiştir bu konu bu kursta ele alınmayacaktır.

27 Şubat

Tüm listeleme View Sınıfları adapterView sınıfından türemişlerdir.
Aslında bu elemanlarda birer ViewGroup olduklarından kendi içlerinde başka Viewları da barındırabilirler.

Şuana kadar ki örneklerimizde ListViewın elemanı olarak String türünden değerler ekledik.Aslında AdapterView sınıfından türemiş tüm sınıflar herhangi türden elemanları da barındırabilirler.

Örnek Kod [Dropbox]024-SimpleProductList

AdapterVıew Sınıfından türetilen sınıflar örneğin ListView sınıfı eklenen elemanları göstermek için toString metodunu çağırır. ve bu yazıyı eleman olarak gösterir.Yani aslında içeride saklanan eklenen türe ilişkin referans değerleridir.Programcı setOnItemClickListener metoduyla istenilen product bilgilerini eklemsi mümkünddür

@Override
public void onItemClick(AdapterView<?> parent , View view, int position, long id)
{
    Product p = (Product)m_listViewProducts.getItemAtPosition(position);
    Toast.makeText(this , p.toString() , Toast.LENGTH_LONG).show();
}

Anahtar Notlar Adapter kalıbı genel olarak içerisindeki veriye doğrudan erişimi engeller. Bu sebeple programcı adapter içerisindeki veriyi modifiye etmek isterse yani var olan veride değişiklik yapmak isterse bunu ana liste üzerinden yapması daha verimli bir yöntemdir.İstenirse aşağıdaki gibi bir sınıf yazılabilir.Ancak bu sınıf silme ve insert işlemi dolayısıyla performansı düşük bir şekilde bu işlemi yapabilir.

package csd.samples.listview;

import android.content.Context;
import android.widget.ArrayAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by oguz on 27.02.2016.
 */
public class MyAdapter extends ArrayAdapter<Product> 
{
    public MyAdapter(Context context, int resId)
    {
        super(context, resId, new ArrayList<Product>());
    }
    public void addItem(Product p)
    {
        this.add(p);
        this.notifyDataSetChanged();
    }

    public void set(int position, Product p)
    {
        this.remove(this.getItem(position));
        this.insert(p, position);
        this.notifyDataSetChanged();
    }
}

Bu işleme bir başka yöntem olarak toplamda bellek olarak fazla yer kaplasa da ArrayAdapter<HashTable<Integer, Product>> m_hashAdapter ile çalışmak biraz daha kolaylaştırabilir

Mutlaka adaptera yaptırmak istenirse adapter ve listeyi sarmalayan aşağıdaki gibi bır sınıf tercih edilebilir.

package csd.samples.listview;

import android.content.Context;
import android.widget.Adapter;
import android.widget.ArrayAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by oguz on 27.02.2016.
 */
public class MyProductAdapter 
{
    private ArrayAdapter<Product> m_adapter;
    private List<Product> m_list;

    public  MyProductAdapter(Context context, int resId)
    {
        m_list = new ArrayList<>();
        m_adapter = new ArrayAdapter<Product>(context, resId, m_list);
    }


    public ArrayAdapter<Product> getAdapter()
    {
        return m_adapter;
    }

    public  void add(Product p)
    {
        m_adapter.add(p);
        m_adapter.notifyDataSetChanged();
    }


    public void set(int position, Product p)
    {
        m_list.set(position, p);
        m_adapter.notifyDataSetChanged();
    }
    //...
}

Bu sarmalama tekniği Generic hale getirilerek daha kolay kullanılabilir hale getirilebilir.

Örnek Kod: [Dropbox]025-ProductListAdapterWrapper


Checkable ListView Kontrolü

Listview kontrölüne herbir elemanı checkable olacak şekilde özellikler eklenebilimektedir.Bunun için adapter sınıfında resource parametresine android.R.layout.simple_list_item_checked geçilecektir.Herhangi bir Abstract View kontrolünün seçim durumları listView'ın setChoiceOption metoduyla sırasıyla kullanıcı tarafından seçilir.

  • CHOICE_MODE_NONE
  • CHOICE_MODE_SINGLE tek biri seçilebilir,
  • CHOICE_MODE_MULTIPLE birden fazla seçilebilir olarak belirlenebilir.

Hiç bir seçim yapılamayan elemanların seçim işlemleri ancak programlamayla yapılabilir.

Örnek Kod : [Dropbox]026-CheckableListView

Checkable ListView kontrolünde seçili elemanını almanın bir kaç yöntemi vardır.

  • SparseBooleanArray Sınıfı Kullanılarak : Bu sınıf türünden referans getCheckedItemPosition metoduyla elde edilebilir.Aslında bu Collection içinde her bir indekse karşılık true ya da false değerleri tutulmaktadır.SparseBooleanArray sınıfının get metodu ile o konumdaki elemanın seçili olma durumu elde edilebilir.Aşağıdaki gibi bir kodla elde edilebilir.
    public void onTestButtonClicked(View v)
    {
        SparseBooleanArray array = m_listViewCities.getCheckedItemPositions();
        int count = m_listViewCities.getCount();

        String str = "";

        for (int i = 0; i < count; ++i) {
            if (!array.get(i))
                continue;

            if (!str.isEmpty())
                str += "-";

            str += m_listViewCities.getItemAtPosition(i);
        }

        Toast.makeText(this, str, Toast.LENGTH_LONG).show();
    }
  • getCheckedItemIds getCheckItemIds Metodlarıyla: bu metodlar ile seçili olan elemanların idleri long türden bir dizi şeklinden elde edilebilmketedir.Andorid 2.2 den itibaren getCheckItemIds metod depricated olmuştur.getCheckedItemIds metodu kullanılmalıdır.
    public void onTest2ButtonClicked(View v)
    {
        long [] ids = m_listViewCities.getCheckItemIds();

        String str = "";

        for (long i = 0; i < ids.length; ++i) {
            if (!str.isEmpty())
                str += "--";

            str += m_listViewCities.getItemAtPosition((int)ids[i]);
        }

        Toast.makeText(this, str, Toast.LENGTH_LONG).show();
    }

getCheckItemIds'in hatalı çalışma durumu

Anahtar Notlar:
Bir adapter üzerindeki datanın stabil olup olmaması durumu vardır.Stable olma durumu herhangi bir datanın Unic(tekil) olmasıyla alakalıdır.Bir adapter sınıfının tekil data içerip içermediği nasıl anlaşılacaktır?

Bunun için baseAdapter sınıfının hasStableIds metodu çağrılabilir.

StableId olup olmaması programcı için önemli değilse, her durumda seçili elemanları görmek istersek SparseBooleanArray sınıfının kullanımı daha uygundur.Çünkü deprecated bir metodu çağırmak tercih edilmemektedir.
Bu metodları kullanmak istiyorsa programcı dataların stabilliğini sağlamak zorundadır. Stabilliği sağlamak için ilk yöntem olarak basit bir adapter sınıfı türetilir ve o adapter sınıfının hasStableIDs metodu true değer ile döndürülür.
Bu durumda listView'a adapter olarak türemiş sınıf verilirse listView açısından bu adapter stabil Id'leri olan bir adapter olacaktır.Ve getCheckedItemIds metodu seçili metodun idlerine ilişkin elemanları döndürecektir.

Örnek Kod: [Dropbox]027-CheckableListViewStableID

MyAdapter.java

package csd.samples.checkablelistview;

import android.content.Context;
import android.widget.ArrayAdapter;

import java.util.List;

/**
 * Created by oguz on 28.02.2016.
 */
public class MyAdaptor extends ArrayAdapter<String> {
    public MyAdaptor(Context context, int resource, List<String> list)
    {
        super(context, resource, list);
    }

    @Override
    public boolean hasStableIds()
    {
        return true;
    }
    //...
}

Idlerin stabil olması tamamen adapter sınıfına bağlıdır programcı bunu ancak kendi Adapter nesnesini yazarsa(türeterek) bu işlemi stabil olarak düşünebilir.

NOTLARIM

026-CheckableListView Ne yapar?

  • ListView , 2 Buton ekle
  • Butonlara tıklayınca ListView'da seçilen elemanları farklı yollarla gösterir.

027-CheckableListViewStableID Ne yapar?

026-CheckableListView'dan farkı MyAdapter sınıfı yazılarak hasStableId metodu true döndürecek şekilde override edildi.