11.6 - Single Selection and Multiple Selection mode for ListView in Android
http://wptrafficanalyzer.in/blog/single-selection-mode-for-listview-in-android/ and Adapters_Excerpt.pdf
By default, ListView is set up simply to collect clicks on list entries. Sometimes,though, you want a list that tracks a user’s selection, or possibly multiple selections. ListView can handle that as well, but it requires a few changes.
First, you will need to call setChoiceMode() on the ListView in Java code to set the choice mode, supplying either CHOICE_MODE_SINGLE or CHOICE_MODE_MULTIPLE as the value. You can get your ListView from a ListActivity via getListView(). You can also declare this via the android:choiceMode attribute in your layout XML.
Then, rather than use android.R.layout.simple_list_item_1 as the layout for the list rows in your ArrayAdapter constructor, you will need to use either android.R.layout.simple_list_item_single_choice or android.R.layout.simple_list_item_multiple_choice for single-choice or multiple-choice lists, respectively.
main.xml(main layout'dur, listview'in id'sinin list olduğuna dikkat.)
<?xml version="1.0" encoding="utf-8"?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false"
android:choiceMode="multipleChoice"
/>
It is a ListView, with the android:choiceMode="multipleChoice"
attribute to indicate that we want multiple choice support. Our activity just uses a standard ArrayAdapter on our list of nonsense words, but uses android.R.layout.simple_list_item_multiple_choice as the row layout:
public class ChecklistDemo extends ListActivity {
private static final String[] items={"lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_multiple_choice,
items));
}
}
What the user sees is the list of words with checkboxes down the right edge:
If we wanted, we could call methods like getCheckedItemPositions() on our ListView to find out which items the user checked, or setItemChecked() if we wanted to check (or un-check) a specific entry ourselves. (getCheckedItemPositions() method'unu çağırarak hangi satırların seçildiğini öğrenebiliriz. setItemChecked() method'unu çağırarak, istediğimize satıra check koyabiliriz, seçili yaparız.)
If the user clicks a row in a ListView, a click event is registered, triggering things like onListItemClick() in an OnItemClickListener. If the user uses a pointing device to change a selection (e.g., pressing up and down arrows to move a highlight bar in the ListView), that triggers onItemSelected() in an OnItemSelectedListener. ( Kullanıcı listedeki bir satıra tıklayınca onListItemClick() method'u trigger edilir, yani tetiklenir, yani çağırılır. Kullanıcı seçimi belirlemek için yukarı aşağı tuşlarına bastığı bir ortamdaysa, bu da onItemSelected() method'unu tetikler. onItemSelected() method'unu anlamadım araştır. )
Example :
In this application, we are creating a listview with single selection mode. That is only one item can be selected at a time. Listview'deki herbir satırda bir radiobutton gösterdik. Aancak implementation'da radioButton View'ini kullanmadık dikkat.
/res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:choiceMode="singleChoice"
/>
</LinearLayout>
MainActivity.java
public class MainActivity extends Activity {
String[] countries = new String[] { "India", "Pakistan", "Sri Lanka", "China", "Bangladesh", "Nepal", "Afghanistan", "North Korea", "South Korea", "Japan" };
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Getting object reference to listview of main.xml
ListView listView = (ListView) findViewById(R.id.listview);
/* Instantiating array adapter to populate the listView. The layout android.R.layout.simple_list_item_single_choice creates radio button for each listview item*/
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_single_choice,countries);
listView.setAdapter(adapter);
}
}
Output :
Aşağıdaki derste de bu konudan bahsedilmiştir bu başlığı da okuyabilirsin.
11.7 - Adapters in overall context
http://gtware.blogspot.com.tr/2012/06/android-building-lists-in-with.html
android.widget.ListView in Android is one of the most popular widgets and used to display a list of items in an array, list or a database query in a scrollable fashion.
The easiest way to create a ListView is by extending a android.app.ListActivity instead of the regular Activity class if the list we need will be the major focus of our activity. A ListActivity will create a ListView for you, so you don't even need any layout XMLs, and it mirrors some of the ListView's methods so you can call them directly from the activity. ( Bir ListView yaratmanın en basit yolu ListActivity class'ını extend eden bir class tanımlamaktır. ListActivity, bizim için default olarak bir ListView yaratacaktır, yani main layout xml yazmaya gerek yoktur. ListActivity class'ında getListView() method'unu çağırarak ListView object'i elde edebiliriz, sonra ise ListView object'in method'larını çağırabiliriz. )
The easiest way to create a ListView is by extending a android.app.ListActivity instead of the regular Activity class if the list we need will be the major focus of our activity. A ListActivity will create a ListView for you, so you don't even need any layout XMLs, and it mirrors some of the ListView's methods so you can call them directly from the activity. ( Bir ListView yaratmanın en basit yolu ListActivity class'ını extend eden bir class tanımlamaktır. ListActivity, bizim için default olarak bir ListView yaratacaktır, yani main layout xml yazmaya gerek yoktur. ListActivity class'ında getListView() method'unu çağırarak ListView object'i elde edebiliriz, sonra ise ListView object'in method'larını çağırabiliriz. )
Creating a simple ListView
Below is the code for a simple ListActivity which displays some words and pops up a simple Toast if an item in the list is clicked:
public class SampleActivity extends ListActivity {
private static final String[] data = { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae" };
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
setListAdapter(adapter);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Toast.makeText(this, "You clicked on: " + data[position], Toast.LENGTH_LONG).show();
}
}Output :
You need to call getListView() method if you want a reference to the ListView created in the background by the ListActivity. Here, android.R.layout.simple_list_item_1 references a layout XML packaged with the Android open source project, along with several others. It includes a simple TextView with some of its attributes set by the Android style currently in use: ( ListActivity, arka planda bizim için default olarak bir ListView yaratacaktır, yani main layout xml yazmaya gerek yoktur. ListActivity class'ında getListView() method'unu çağırarak ListView object'i elde edebiliriz. ArrayAdapter constructor'ı çağırılırken, Android ile birlikte default olarak gelen layout xml olan simple_list_item_1.xml kullanılmıştır.
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
simple_list_item_1.xml sadece aşağıda gösterilen textView element'i içerir, bu layout listView'deki row'ların layout'ları için kullanılır,
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
... /> )
Using your own layout files
If you do want to customize the layout and define your own XML but still use it with a ListActivity, you can do so as long as you use android:id="@android:id/list" for your ListView so that the ListActivity can identify the ListView to use: ( ListActivity kullanıp main layout'u kendimiz yazmak istiyorsak, yazdığımız layout dosyasındaki listview element'İn id'si list olmalıdır. ListActivity'nin refer ettiği layout'daki listview'İn id'si default olarak list'dir çünkü. Örneğin aşağıdaki gibi bir layout dosyası yazıp listactivity class'ında bu layout'u main layout olarak set edebiliriz. )
main.xml:
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#0000CC"
android:dividerHeight="4dp"
/>
Important: match_parent was introduced with Android 2.2, for older versions, you need to use fill_parent instead.
Activity'de setContentView(R.layout.main); method'Unu çağırırız aşağıdaki gibi :
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
data);
setListAdapter(adapter);
}
This code is exactly same as the previous one, with the only difference of calling setContentView(R.layout.main); in onCreate(), which sets the activity's view to the layout we have defined in main.xml. The output is again a list of items with a custom blue divider we had specified in main.xml:
Lists with a choice mode
If you want a list where items in your list should be checkable, you need to specify a choice mode for your ListView either in your layout XML or in your Java code and use a compatible resource in the constructor of your Adapter. To define the choice mode in the layout, you need to declare the attribute android:choiceMode="singleChoice" or android:choiceMode="multipleChoice" for your ListView, depending on your wish for the user to be able to select one item only in the list, or multiple items simultaneously, respectively. You can do the same in your Java code as follows: ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
( ListView'de gösterilen satırları tıklanabilir olmak yerine seçilebilir hale getirmek mümkündür.
Bunun için xml layout'da tanımladığımız listview element'in choicemode'unu singleChoice veya multipleChoice olarak set etmeliyiz aşağıdaki gibi :
android:choiceMode="singleChoice" android:choiceMode="multipleChoice"
Bunu XML'de yapmak yerine Java'da da yapabiliriz aşağıdaki gibi :
ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
singleChoice seçersek, listview'deki satırların yanında bir radiobutton gösterilir ve bu satırlardan sadece bir tanesi seçilebilir.
multipleChoice seçersek, listview'deki satırların yanında bir checkbox gösterilir ve birden fazla satır seçilebilir.
)
Assuming that you're creating your list with a ListActivity. Below is the layout, code and the output for a single choice list with minor modifications to our previous code: ( ListActivity kullandığımız varsayalım. Bu durumda id'si list olan bir listview element içeren bir main layout dosyası yazabiliriz aşağıdaki gibi. )
Assuming that you're creating your list with a ListActivity. Below is the layout, code and the output for a single choice list with minor modifications to our previous code: ( ListActivity kullandığımız varsayalım. Bu durumda id'si list olan bir listview element içeren bir main layout dosyası yazabiliriz aşağıdaki gibi. )
main.xml
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#0000CC"
android:dividerHeight="4dp"
android:choiceMode="singleChoice"
/>
Listactivity class'ının onCreate() method'u aşağıda gösterilmiştir:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_single_choice, data);
setListAdapter(adapter);
}
Here we have added android:choiceMode="singleChoice" to our layout file, and also used a different row layout, specifically android.R.layout.simple_list_item_single_choice for our adapter's constructor. This is another packaged layout included with the Android SDK which uses a CheckedTextView instead of a regular TextView: (simple_list_item_single_choice.xml dosyası android ile birlikte default olarak gelen bir layout dosyasıdır, bu dosyayı kullanabilmek için, listview element'in choicemode'u singlechoice olmak zorundadır. Bu dosyanın içeriği aşağıda gösterilmiştir, textView'İn farklı bir versiyonu olan CheckedTextView element içerir: )
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightSmall"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
/>
The output of our single choice layout and code is a TextView with a radio button to show the current selection: ( Single choice layout ve kod'un output'U aşağıdaki gibidir. )
Figure 3: ListActivity with a ListView using android:choiceMode="singleChoice" and android.R.layout.simple_list_item_single_choice (android:choiceMode="singleChoice" attribute'ü içeren bir listview, ve activity'de adapter yaratılırken row layout olarak android.R.layout.simple_list_item_single_choice kullanılan bir listactivity'nin çıktısı yukarıdaki gibidir. )
If you want to enable multiple selection, you will need to use android:choiceMode="multipleChoice" along with a resource in your adapter that will support multiple selection, such as another pre-packaged resource android.R.layout.simple_list_item_multiple_choice. The output will be similar, with the possibility of multiple selections this time: ( Birden fazla satırın seçilebilir olmasını istiyorsak, main layout'daki listview element'e şu attribute'ü eklemeliyiz : android:choiceMode="multipleChoice" . Ayrıca adapter'ümüzün row layout'U olarak çoklu seçimi destekleyen bir layout kullanmasını sağlamalıyız, örneğin android ile default gelen android.R.layout.simple_list_item_multiple_choice layout'Unu kullanabiliriz. )
If you want to enable multiple selection, you will need to use android:choiceMode="multipleChoice" along with a resource in your adapter that will support multiple selection, such as another pre-packaged resource android.R.layout.simple_list_item_multiple_choice. The output will be similar, with the possibility of multiple selections this time: ( Birden fazla satırın seçilebilir olmasını istiyorsak, main layout'daki listview element'e şu attribute'ü eklemeliyiz : android:choiceMode="multipleChoice" . Ayrıca adapter'ümüzün row layout'U olarak çoklu seçimi destekleyen bir layout kullanmasını sağlamalıyız, örneğin android ile default gelen android.R.layout.simple_list_item_multiple_choice layout'Unu kullanabiliriz. )
Figure 4: A multiple-choice ListView using android.R.layout.simple_list_item_multiple_choice
With both selection modes, you can use methods such as getCheckedItemPosition() and setItemChecked() from your ListView to manipulate the items and get user selections. ( Her iki selection moduyla da getCheckedItemPosition() veya setItemChecked() method'larını kullanabiliriz. )
Using a custom layout for ListView items
Row layout'u olarak custom bir layout tanımlamak da mümkündür. Örneğin aşağıdaki gibi bir row layout xml tanımlayalım.
row.xml:
row.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:orientation="horizontal">
<ImageView android:id="@+id/icon"
android:src="@drawable/rating_good"
/>
<TextView
android:id="@+id/text"/>
</LinearLayout>
code:
public class SampleActivity extends ListActivity {
private static final String[] data = { "lorem", "ipsum", "dolor", "sit",
"amet", "consectetuer", "adipiscing", "elit", "morbi", "vel",
"ligula", "vitae" };
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
R.layout.row, R.id.text, data);
setListAdapter(adapter);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
ImageView icon = (ImageView) v.findViewById(R.id.icon);
icon.setImageResource(R.drawable.rating_bad);
}
}
In our ArrayAdapter constructor, we supply our custom layout file (row.xml) and our TextView's ID (R.id.text). And here onListItemClick() method is overridden and switches the icon when an item on the list is selected. (ArrayAdapter constructor'ına, custom layout dosyası (row.xml)'nı parametre olarak verdik(R.layout.row) ve row layout'daki textview'in id'sini(R.id.text) parametre olarak verdik ancak bunu neden yaptık anlamadım? Listedeki bir satıra tıklayınca imageview'deki image değiştirilecektir.)
Notice that we're not providing any activity layouts here and so we don't call setContentView() inside onCreate(), so our ListActivity creates a default one for us. (However, we could also change the activity layout and customize the ListView itself as we wanted as shown in the previous example). The output for our custom row is as we would expect:
( Ayrıca, main layout set etmediğimize yani listactivity'nin default main layout'unu kullandığımıza dikkat et. Sonuç olarak aşağıdaki output'u elde ederiz : )
Figure 5: A default ListView with a custom row layout
Creating a custom adapter
Although we can handle most of the ListView use cases with the examples provided above, if you really need dynamic lists with heavy customization and contents determined dynamically, you can create your own adapter by extending the ArrayAdapter. You will only need to override getView(), which is called for each row in the list and needs to return a View. Using the previous example, let's create our own custom adapter and override the getView() method so that it will return a "rating_good" icon for rows with a single position, and a "rating_bad" for double ones. You will only need to modify the parts of your Java code given below, our row.xml does not change, and we again do not provide a layout for the activity: ( Şimdiye kadar gördüğümüz örneklerde hep AarrayAdapter'ü kullandık. Kendi custom adapter'ümüz implement etmek de mümkündür. Bu sayede daha fazla kontrole sahip olabiliriz. aynı row.xml i kullandığımızı aynı listactivity kodunu kullandığımızı ve main layout kullanmadığımızı düşünelim. ListActivit class'ının içersinde bir inner class olarak bir CustomAdapter isimli adapter class'ı tanımlayalım. onCreate() method'unda CustomAdapter class'ından bir object yaratıp bunu setListAdapter method'una veririz. )
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new CustomAdapter(this, R.layout.row, R.id.text, data));
}
private class CustomAdapter extends ArrayAdapter<String> {
public CustomAdapter(Context context, int resource,int textViewResourceId, String[] objects) {
super(context, resource, textViewResourceId, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (position % 2 == 0) {
View row = super.getView(position, convertView, parent);
ImageView img = (ImageView) row.findViewById(R.id.icon);
img.setImageResource(R.drawable.rating_bad);
return row;
} else {
return super.getView(position, convertView, parent);
}
}
}
In our onCreate(), we instantiate our CustomAdapter and set it as the list adapter with the TextView's resource ID and row layout XML. Our CustomAdapter has the same constructor as the previous ArrayAdapter, and propagates the TextView's ID and row layout's ID to its superclass, ArrayAdapter, so that the ArrayAdapter creates a view for each row and sets the TextView's text.
Afterwards, we're checking if our row position is double, and if yes, we're using the View handed by the method parameters, which we know is an instance of our row.xml with an ImageView and a TextView, and changing the resource for the ImageView. If position is single, then we return the super.getView() as is. The output looks like this:
( getView() method'unu şunu implement ettik, tek ve çift sayılı pozisyonlardaki satırlarda gösterilen image'lar farklı olacak. )
If we did not override the getView() method in our CustomAdapter or returned the View prepared by the ArrayAdapter for us by using:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return super.getView(position, convertView, parent);
} then the output would be exactly the same as the previous example: a thumbs-up icon with text set from our data. Since we are only changing the icon for each row and our rows are still pretty similar looking, we can still rely on super.getView() method to supply us with a View which will be set-up by the parent ArrayAdapter implementation. ( Eğer getView method'unu implement etmeseydik, veya yukarıdaki gibi arrrayadapter'ün view'ini return etseydik, önceki örnekle aynı output'u alırdık yani tüm satırlarda aynı resim gösterilirdi, bu örnekten farkı tekli çiftli satırlarda farklı resim olmaması olurdu. getview() method'unda super.getView() diyerek parent view elde edilir, yani getview()'İn return ettiği default view kullanılır(we use the views created by the default getView() implementation.). Önceki örneklerde ise row layout xml'i inflate ederek elde ettiğimiz view object'i return ediyorduk bu örnekte ise bu adapter'ün parent view'ini return ediyoruz dikkat!! )
An even more customized adapter with Layout Inflation
If you come across a requirement to create your own rows by yourself and you don't want to use any of the constructors provided by ArrayAdapter, then you again need to create and return a View for each row item in the getView() method in your custom adapter implementation. If you do not need to create different set of widgets dynamically for each row, then you can define your base layout in a layout XML and use it as a starting point. Using the same row.xml, update the following parts in your Java code: ( Yukarıdaki örnekte, parent class'In view'İni elde edip return ettik. Bu örnekte ise row layout xml yazıp bunu inflate ederiz getView() method'unda. )
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new CustomAdapter());
}
...
private class CustomAdapter extends ArrayAdapter<String> {
public CustomAdapter() {
super(SampleActivity.this, R.layout.row, data);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = getLayoutInflater().inflate(R.layout.row, parent, false);
TextView tw = (TextView) row.findViewById(R.id.text);
tw.setText(data[position]);
if (position % 2 == 0) {
ImageView img = (ImageView) row.findViewById(R.id.icon);
img.setImageResource(R.drawable.rating_bad);
return row;
} else {
return row;
}
}
}
Artık CustomAdapter 0-argument bir constructor'a sahiptir. The output for this example is exactly the same as the previous one:
Figure 7: Same output, this time by inflating the rows ourselves
Add a little bit of cream on top: Performance Tweak
Working in a mobile environment, our applications generally has less CPU and memory power available. And every CPU instruction set drains the battery. So if you want your applications to be be "good citizens" of the Android ecosystem, you need to be careful not to consume more resources then you ever need to. Plus; your users will be thankful that your UI is more swift, and they don't have to charge their phones just because their favorite app's developer did not take additional steps to tweak the code. ( Mobil ortamda çalıştığımız için uygulamalarımızın kullanabileceği CPU ve memory azdır sınırlıdır dolayısıyla bunları verimli kullanmalıyız ki kullanıcılarımız memnun olsunlar. )
In Android, every widget and container consumes valuable memory (around 2KB each), and every call to the LayoutManager.inflate() method requires the Android system to traverse your whole XML trees, and instantiate each widget and thus allocate memory; so you consume both memory and CPU for each row you inflate in the previous example. As you may already have noticed, getView() method gives you a View called convertView as one of its parameters, which is actually one of the rows created previously. When the user scrolls through the rows in the ListView, we could actually use this View and update its attributes for the current row instead of creating new rows each time. This is called "recycling" and Google statistics show that you will get up to %50 speed increase if you do "recycle" in your adapter implementation. You only need to change the parts shown in bold in your code from the previous example to enable recycling in this project: ( getView() method'unun aldığı parametre olan View object, önceki yaratılmış olan satır layout'Una karşılık gelen biew object'dir. Kullanıcı scroll down yaparak listenin aşağısındaki elemanları görmek istediğinde, her defasında yeni satırlar yaratmaktansa, getView() method'unun 2.paremtersi olan view object'i update edebiliriz. Buna recycling denir, ve yüzde 50 verimlilik sağlar. )
In Android, every widget and container consumes valuable memory (around 2KB each), and every call to the LayoutManager.inflate() method requires the Android system to traverse your whole XML trees, and instantiate each widget and thus allocate memory; so you consume both memory and CPU for each row you inflate in the previous example. As you may already have noticed, getView() method gives you a View called convertView as one of its parameters, which is actually one of the rows created previously. When the user scrolls through the rows in the ListView, we could actually use this View and update its attributes for the current row instead of creating new rows each time. This is called "recycling" and Google statistics show that you will get up to %50 speed increase if you do "recycle" in your adapter implementation. You only need to change the parts shown in bold in your code from the previous example to enable recycling in this project: ( getView() method'unun aldığı parametre olan View object, önceki yaratılmış olan satır layout'Una karşılık gelen biew object'dir. Kullanıcı scroll down yaparak listenin aşağısındaki elemanları görmek istediğinde, her defasında yeni satırlar yaratmaktansa, getView() method'unun 2.paremtersi olan view object'i update edebiliriz. Buna recycling denir, ve yüzde 50 verimlilik sağlar. )
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
row = getLayoutInflater().inflate(R.layout.row, parent, false);
}
TextView tw = (TextView) row.findViewById(R.id.text);
tw.setText(data[position]);
ImageView img = (ImageView) row.findViewById(R.id.icon);
if (position % 2 == 0) {
img.setImageResource(R.drawable.rating_bad);
} else {
img.setImageResource(R.drawable.rating_good);
}
return row;
}
We need to check if the convertView is not null before using it since our list could be creating the rows for the first time. But if we do get a non-null one, this is just one of the previous rows we had inflated, and we can use it for our row by updating its text and icon without having to inflate a new View first. Since we are now using our previous recycled rows, we need to set our icon each time as well as the text displayed. You can experiment with recycling and see it on action yourself, though you will probably need a larger number of rows to scroll through to see any visible difference. The output of this code will be the same, but it will run more efficiently, make the user happy, and us proud. ( Örneğin ekranda 9 tane satır gösteriliyor diyelim. Ekranı aşağı doğru kaydırdığımızda gösterilen satırlar sıfırdan yaratılmaktansa, ekranda halihazırda varolan satırlar güncellenir recycling sayesinde. Yani getView() method'unun aldığı parametre olan View object null ise satır ilk kez yaratılmaktadır. Null değilse, bu satır zaten yaratılmıştır, bilgilerini güncellemek gerekir. Output'umuz aynıdır ancak kodumuz daha verimli çalışır. )
Tag property of View object ,getTag() & setTag() methods and the Holder Pattern
Unlike IDs, tags are not used to identify views. Tags are essentially an extra piece of information that can be associated with a view. Tags are most often used as a convenience to store data related to views in the views themselves rather than by putting them in a separate structure. (Tag ve ID'yi birbirine karıştırma bunlar birbirinden tamamen farklı şeylerdir. View object'in sahip olduğu ID isimli attribute unique olmak zorundadır. View object'in sahip olduğu tag attribute ise, view ile ilişkili bilgilerdir, unique olmak zorunda değildir, bir view object birden fazla tag'a sahip olabilir. Facebook,twitter,instagram'da bir fotoğrafı tag'ladığını düşün. View'lerin tag'larını da bu şekilde düşünebilirsin. View object ile ilgili ilişki olan bilgiler yani tag'lar, view object'lerde tutulur. )
Tags may be specified with character sequence values in layout XML as either a single tag using the android:tag attribute or multiple tags using the
<tag>
child element:( Bir view object'e tag nasıl koyulur?
1 - xml dosyasında android:tag attribute'Ünü veya <tag> element'ini kullanarak. android:tag attribute'Ünü kullanarak bir string belirtebiliriz. <tag> element'ini kullanarak birden fazla string belirtebiliriz. )
<View ... android:tag="@string/mytag_value" /> <View ...> <tag android:id="@+id/mytag" android:value="@string/mytag_value" /> </View>
2 - Tags may also be specified with arbitrary objects from code using setTag(Object) or setTag(int, Object). And it can be obtained using getTag (), getTag(int) . getTag returns the Object stored in this view object as a tag, or null if not set (Java kodunda bir view object'in setTag() method'u çağırılarak da, view object ile bir tag ilişkilendirilebilir. Aayrıca java koduyla view object'e koyabileceğimiz tag string de olabilir object de olabilir.
View clas'ının setTag (Object tag), setTag (int key, Object tag) method'ları kullanılarak view object ile bir object ilişkilendilir. Birden fazla object ilişkilendireceksek setTag (int key, Object tag) method'Unu kullanarak ilişkilendirdiğimiz tag için bir de key set etmeliyiz. Daha sonra getTag(int) method'una doğru key'i vererek ilgili tag object'i elde ederiz.
Bir tane object ilişkilendireceksek setTag(Object) ve getTag() method'larını kullanmalıyız.
Birden fazla object ilişkilendireceksek setTag(int,Object) ve getTag(int) method'larını kullanmalıyız.
There's yet one more thing we could do to make our code better. We're still calling findViewById() once for each row in the list with a single position, and twice for all rows with a double position to update the icon as well. This method is also a resource consumer and we could use something called a Holder Pattern. ( Kodumuzu hala geliştirebiliriz. Listedeki herbir satır için findViewById() method'unu çağırıyoruz. Bu method kaynakları hunharca harcar. Bunu önlemek için Holder Pattern kullanabiliriz. )
Have you ever read anything about the Java Design Patterns? If not, now would be a good time. The Holder Pattern is all about creating a class with package-protected members which will be the widgets you are accessing often through findViewById() calls and then accessing them through this custom class. Best done than told, let me show you with an example using our previous project: ( Bir Holder class tanımlanır, bu class'da package içerisinden erişilebilir olan yani public olan variable'lar tanımlanır. Aslında findViewById() method'unu çağırarak elde ettiğimiz object'lere refer edecek reference variable'lar tanımlanır. Söylemektense yapalım, aşağıdaki örneği inceleyelim. )
CustomHolder.java
Have you ever read anything about the Java Design Patterns? If not, now would be a good time. The Holder Pattern is all about creating a class with package-protected members which will be the widgets you are accessing often through findViewById() calls and then accessing them through this custom class. Best done than told, let me show you with an example using our previous project: ( Bir Holder class tanımlanır, bu class'da package içerisinden erişilebilir olan yani public olan variable'lar tanımlanır. Aslında findViewById() method'unu çağırarak elde ettiğimiz object'lere refer edecek reference variable'lar tanımlanır. Söylemektense yapalım, aşağıdaki örneği inceleyelim. )
CustomHolder.java
Hiçbir şeyi extend etmeyen normal bir class tanımlanmıştır. Bu class 2 tane reference variable içerir, findViewById() method'Unu çağırarak elde edeceğimiz object'lerin type'ı ImageView ve TextView'dir, holder class bu yüzden bu reference type'da 2 variable içerir. Tek constructor'ı vardır, View object alır parametre olarak, sonra findViewById() method'unu çağırarak ImageView ve TextView object'leri elde eder. Bu object'lere ihtiyacımız olduğunda, findViewById() methoD'Unu çağırmamıza gerek yok artık, CustomHolder class'ının içerdiği variable'lara erişmek yeterli olacak.
class CustomHolder {
ImageView icon;
TextView text;
CustomHolder(View row) {
this.icon = (ImageView) row.findViewById(R.id.icon);
this.text = (TextView) row.findViewById(R.id.text);
}
}
Every android.view.View object has a setTag(Object obj) method that you can use to attach any objects to this view, and an Object getTag() method which will return the object attached using the previous one. ( Tüm View object'ler setTag(Object obj) ve getTag() method'una sahiptir. Bir view'e setTag(Object obj) methoD'u çağırılarak ilişkilendirilen object getTag() methoD'u çağırılarak elde edilir. )
We could set our TextView objects using setTag() method to our rows, but since we're also changing ImageView resources dynamically, we need to instantiate and use a Holder class which will hold both and tag it to our rows. Here's what needs to be changed in our Java code to switch to using the Holder pattern: ( getView method'unda, convertView'in null olup olmamasına bakarak satırın illk kez yaratılıp yaratılmadığını anlarız bu verimlilik için yaptığımızı hatırla. Satır ilk kez yaratılıyorsa, satır layout'unu inflate ederek bir view object elde ederiz:
row = getLayoutInflater().inflate(R.layout.row, parent, false);
Sonra customHolder class'ının constructor'ına bu view'i vererek, bir customHolder object yaratırız. Bu object'in constructor'ında, findViewById() method'ları çağırılarak, row layout'undaki view element'ler elde edilir. Artık holder object'imiz hazırdır.
CustomHolder holder = new CustomHolder(row);
Sonra ise view object'in setTag() method'una argument olarak holder object'i veririz. Böylece view object ile holder object'i ilişkilendirmiş oluruz.
row.setTag(holder);
convertView null değilse, yani satır zaten yaratılmışsa, if block'a girilmez.
row layout'a karşılık gelen view olan row isimli object'in getTag() method'u çağırılarak, az önce bu view'e eklediğimiz tag, yani bu view ile ilişkilendirdiğimiz holder object'i elde edilir. Bu object'in public variable'larına erişerek, TextView ve ImageView object'e erişmiş olduk. Holder pattern kullanmasaydık findViewById() method'u çağırarak TextView ve ImageView object'lere erişmek zorunda kalacaktık.
row isimli object'i incele. Önce convertView assign edilmiştir. Eğer satır ilk kez yaratılıyorsa, row isimli object'in, row.xml'in inflate edilip elde edilen object'e refer etmesi sağlanır. )
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View row = convertView;
CustomHolder holder;
if (row == null) {
row = getLayoutInflater().inflate(R.layout.row, parent, false);
holder = new CustomHolder(row);
row.setTag(holder);
}
holder = (CustomHolder) row.getTag();
TextView tw = holder.text;
ImageView img = holder.icon;
tw.setText(data[position]);
if (position % 2 == 0) {
img.setImageResource(R.drawable.rating_bad);
} else {
img.setImageResource(R.drawable.rating_good);
}
return row;
}
We access the widgets through the Holder class, instead of calling findViewById() method every time. This is again another reduction to our CPU consumption (and a plus for the speed of our application - around %25 according to Google stats) without any changes to the UI. We will have every right to boost ourselves as a top Android developer once we make it a habit to utilize this pattern with our adapters whenever it makes sense to do so. ( Bu sayede uygulamamızın verimini %25 oranında arttırdık süperiz ! )
----
View class'ının getTag() setTag() method'ları başka amaçlarla da kullanılabilir ancak biz View Holder pattern ile kullanmaya devam edelim. Farklı bir örnek görelim.
The View Holder pattern allows to avoid the findViewById() method in the adapter.
A ViewHolder class is a static inner class in your adapter which holds references to the relevant views in your layout. ( Adapter class'ınızın içinde bir static inner class olarak tanımlarsınız ViewHolder class'ını. Bu class'da, layout'unuzdaki view'lere reder eden reference variable'lar tanımlarsınız.)
If we receive a convertView object, we can get the instance of the ViewHolder via the getTag() method and assign the new attributes to the views via the ViewHolder reference.
While this sounds complex this is approximately 15 % faster then using the findViewById() method.
Example :
The following code shows a performance optimized adapter implementation which reuses existing views and implements the holder pattern. ( Varolan view'leri tekrar kullanan ve holder pattern'i implement eden bir adapter class'ı tanımlanmıştır aşağıda. Önceki örneğimize çok benzemektedir. Dolayısıyla açıklamaya gerek yok. )
public class MyPerformanceArrayAdapter extends ArrayAdapter<String> {
private final Activity context;
private final String[] names;
static class ViewHolder {
public TextView text;
public ImageView image;
}
public MyPerformanceArrayAdapter(Activity context, String[] names) {
super(context, R.layout.rowlayout, names);
this.context = context;
this.names = names;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
// reuse views
if (rowView == null) {
LayoutInflater inflater = context.getLayoutInflater();
rowView = inflater.inflate(R.layout.rowlayout, null);
// configure view holder
ViewHolder viewHolder = new ViewHolder();
viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01);
viewHolder.image = (ImageView) rowView.findViewById(R.id.ImageView01);
rowView.setTag(viewHolder);
}
// fill data
ViewHolder holder = (ViewHolder) rowView.getTag();
String s = names[position];
holder.text.setText(s);
if (s.startsWith("Windows7") || s.startsWith("iPhone")
|| s.startsWith("Solaris")) {
holder.image.setImageResource(R.drawable.no);
} else {
holder.image.setImageResource(R.drawable.ok);
}
return rowView;
}
}
---
http://stackoverflow.com/questions/25966689/what-is-the-working-of-settag-and-gettag-in-viewholder-pattern
tag
is a mechanism to make your views
remember something, that could be an object
an integer
a string
or anything you like. ( tag, view object'lerin hatırlayacağı bir string, integer veya object'dir gibi düşün. )
So when your
ListView
is going to create for the first time your convertView
is null
. So you create a new convertView
and put all of your references
of the objects
of that row
in a viewHolder
. Then save your viewHolder
into the memory of that convertView
(setTag). Android
takes your convertView
and puts it in its pool
to recycle
it and passes
it again to you. but its pool
may not have enough convertViews
so it again passes a new convertView
thats null
. so again the story is repeated till the pool
of android
is filled up. after that android
takes a convertView
from its pool and passes it to you. you will find that its not null
so you ask it where are my object references
that I gave to you for the first time? (getTag) so you will get those and do whatever you like. ( ListView ilk kez yaratılırken ekran dolana kadar yaratılan herbir satır için getView() method'u çağırılırken, convertView null'dır, dolayısıyla burada layout row'u inflate ederiz, sonra holder class'dan bir object yaratırız, sonra bu object'in variable'larını set ederiz findViewById() method'unu çağırarak. Sonra ise view object'in setTag() method'unu çağırarak, view object ile holder object'i ilişkilnediririz.
Ekran dolduktan sonra, listview'i yukarı aşağı kaydırdığımızda gördüğümüz satırlar sıfırdan yaratılmaz, bu satırlar için getView() method'u çağırıldığında findViewById() method'u tekrar çağırılmayacaktır bu da verimlilik sağlayacaktır. rowView'in getTag() method'unu çağırarak holder object'i elde ederiz. holder object'in variable'larına erişerek ise, row layout'daki ImageView ve TextView object'lere erişiriz. )
More elaboration on below line
But its pool may not have enough convertViews so it again passes a new convertView thats null
android pool
is empty when your listView
is going to create. so for the first item of your listView
it sends you a convertView
that must be displayed. after that android
saves it in its pool
, so its pool
now contains just one convertView
. for your second item of your listView
that is going to create android can not use its pool because it is actually has one element and that element is your first item and it is being shown right now so it has to pass another convertView
. this process repeates until android
found a convertView
in its pool
thats not being displayed now and passes it to you.
Android inflates each row till the screen filled up after that when you scroll the list it uses holder. ( Android ekran dolana kadar row layout inflate eder, holder object yaratır, ve view ile holder'I ilişkilendirir. Ekran dolduktan sonra ise listeyi aşağı yukarı hareket ettiridğimizde, daha önceden yaratılan holder object'leri elde ederek row layout'daki view'lere erişip bu view'lerin içeriğini değiştirir. )
Saving row states
Apart from the 5th example where our thumbs-up icon changed to a thumbs-down one when clicked, widgets in our rows did not change and were pretty much static. However, we could have other widgets such as an EditText, RatingBar, ToggleButton, etc. inside our rows which would change state based on the user input. Our rows do not yet care about the state of the widgets inside them, and if there was any, then these would be lost while new rows are created and the old ones recycled. ( Daha önce yaptığımız şu örneği hatırlayalım, listview'de tüm satırlarda thumbps-up icon gösteriliyordu bu listedeki satırlardan birine tıklayınca icon resmi değiştirilip thumbs-down icon yapılıyordu. Şimdiye kadar yaptığımız örneklerde, bu widget dışındaki tüm widget'ler static idi ve satırlar değiştirilimiyordu. Ancak satırlara kullanıcıdan gelen input'a göre değişebilecek widget'ler de koyabiliriz, örneğin EditText, RatingBar, ToggleButton. Recycle sağlayan kodu yazdığımızda, eğer widget'lerin state'ini kaydetmezsek, yeni satırlar yaratılıp eskileri recycle edilirken widget'lerde yaptığımız değişiklikler kaybolur. )
To see it for yourself, just change the TextView in your row.xml layout to the one below, replacing it with an EditText widget. Run your project again, and change the contents of any EditText and scroll until the row you've changed is out of view, and scroll back to it. You will see that any changes you have done are lost, and the row is again in its initial state. ( Örneğin, daha önceki örnekteki row.xml'deki textview element'ini silip bunun yerine aşağıdaki edittext element'ini yazalım, android:inputType="text" attribute'üne dikkat edelim. Sonra projemizi çalıştıralım. Herhangi bir satırdaki edittext alanındaki içeriği değiştirelim. Sonra değişiklik yaptığımız satır gözden kaybolana kadar listeyi aşağı doğru kaydıralım. Sonra listeyi yukarı kaydırarak az önce değişiklik yaptığımız satıra gelelim. İlgili satırda yaptığımız değişikliklerin kaybolduğunu, satırın içeriğinin ilk başlangıçtaki içerik olduğunu görürüz. Yani satır gözden kaybolduğunuda satır recycle edilir ve satırın içeriğinde yaptığımız değişiklik kaydedilmez. Bizim bunu manuel olarak kaydetmemiz gerekmektedir. )
<EditText
android:id="@+id/text"
...
android:inputType="text"
android:imeOptions="actionDone"
/>
As EditText is a subclass of TextView, our holder class can still do its job. To be able to save widget states though, we need to teach our widgets which row they represent in the list. Continuing from our previous example, update your CustomHolder.java and SampleActivity.java as follows to save the text inside EditText widgets: ( Holder class'ımızı yazalım. Widget'lerin state'lerini kaydetmek için, widget'lerimize listede kaçıncı satırda olduklarını öğretmeliyiz. EditText içeriğinde kullanıcı bir değişikilk yapıp klavyedeki done(enter) tuşuna bastığında değişikliği kaydetmeliyiz. )
CustomHolder.java
CustomHolder.java
class CustomHolder {
ImageView icon;
EditText text;
CustomHolder(View row)
{
this.icon = (ImageView) row.findViewById(R.id.icon);
this.text = (EditText) row.findViewById(R.id.text);
}
}
SampleActivity.java
@Override
SampleActivity.java
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
CustomHolder holder;
if (row == null) {
row = getLayoutInflater().inflate(R.layout.row, parent, false);
row.setTag(new CustomHolder(row));
}
holder = (CustomHolder) row.getTag();
EditText text = holder.text;
text.setText(data[position]);
text.setTag(new Integer(position));
text.setOnEditorActionListener(new TextView.OnEditorActionListener()
{
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
int index = (Integer) v.getTag();
data[index] = v.getText().toString();
return true;
}
return false;
}
});
ImageView img = holder.icon;
if (position % 2 == 0) {
img.setImageResource(R.drawable.rating_bad);
}
else
{
img.setImageResource(R.drawable.rating_good);
}
return row;
}
getView() method'unda, holder pattern'ı kullanarak, EditText object'i elde ederiz. Sonra EditText'in içeriğine data array'inin position'ıncı index'indeki elemanı set ederiz :
EditText text = holder.text;
text.setText(data[position]);
Sonra EditText view object ile position'ı ilişkilendiririz, int ilişkilendiremeyeceğimiz için int'i Integer object'e dönüştürüp Integer object'i ilişkilendirdik.
text.setTag(new Integer(position));
Sonra EditText object'in setOnEditorActionListener() method'unu çağırarak, edittext alanında yapılacak değişiklikleri dinleriz, yani editText'in içeriğinde bir değişiklik yapıldığında bu method'un tetiklenmesini sağlarız, değişiklik yapıldıktan sonra enter(done) tuşuna basılırsa actionId == EditorInfo.IME_ACTION_DONE doğru olduğu için if block'a girilir. if block'da TextView view object ile ilişkilendirilen şey yani position elde edilir, yani satırın listedeki pozisyonu elde edilir. Sonra EditText alanındaki güncel içerik elde edilir ve satır listenin kaçıncı index'indeyse, EditText'in içeriği data array'inin o index'inci elemanı olarak set edilir. En son true return edilir. IME_ACTION_DONE gerçekleşmezse, false return edilir.
text.setOnEditorActionListener(new TextView.OnEditorActionListener()
{
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
int index = (Integer) v.getTag();
data[index] = v.getText().toString();
return true;
}
return false;
}
});
Once you compile and run the project, change the text of any of the EditText widgets, click on Done in your soft keyboard, and again try scrolling back and forth. You will now see that the widget does keep its text indeed: ( Herhangi bir satırdaki EditText içeriğinde bir değişiklik yapıp klavyedeki done(enter) tuşuna bastığımızda değişikliği kaydedilmiş olur. Artık satır gözden kaybolana kadar listeyi aşağı yukarı kaydırsak da bu değişiklik kaybolmaz. )
As you can see, we are now teaching our EditText widgets which position they will be representing in the list (with the line text.setTag(new Integer(position));) and registering a listener on them which updates our String array once their contents have changed. ( Bu örnekte, EditText widget'lere listenin kaçıncı satırında olduklarını şöyle öğrettik. text.setTag(new Integer(position));
Sonra ise EditText'e bir listener register ettik, EditText'in içeriği değişince String array'imizin içeriğinin de değiştirilmesini sağladık.)
Bu arada şunu da hatırlatayım, bu örnekteki IME_ACTION_DONE event'ini yakalayabilmek için layout xml dosyasında EditText element'inin android:imeOptions="actionDone" attrbiute'ü tanımlanmak zorundadır.
Using custom row models
This will be our last and probably the most complex pattern for creating lists in Android. In complex scenarios where your data is not as simple as an array/list of Strings and you need to keep track of multiple widgets' data as well as their states, you can create a row model class which will serve as a data model for your list, and use it as the generics argument to the array adapter: ( Bu dersimiz Android'de liste yaratmak için olan en complex ders olacak. Verilerinizin String array değil de object array'i olduğunu düşünün. Ve daha önce öğrendiğimiz her şeyi biraraya getirip bu örnekte kullanalım. Row model class yaratalım, row model class hiçbir şeyi extend etmeyen herhangi bir class'dır, tutmak istediğimiz variable'ları tanımlarız bu class'da. Yani aslında kendi adapter class'ımızı tanımlamak yerine, arrayadapter kullanıyoruz ancak arrayadapter'de kendi tanımladığımız class'ın type'ındaki object'leri tutuyoruz. )
ArrayAdapter<MyCustomRowModel> aa = new ArrayAdapter<MyCustomRowModel>(...);
Aynı row.xml ve CustomHolder.java dosyasını kullanmaya devam edelim. Aşağıda bir ListActivity class'ı tanımlayalım. Bu activity class'ında bir string array tanımlayalım. Sonra RowModel class'ı type'ında elemanlar içeren bir ArrayList'e refer eden bir reference variable tanımlayalım. onCreate() method'unda bir ArrayList object yaratalım. Sonra array'deki elemanları arraylist'e koyarız. Sonra CustomAdapter class'ından bir object yaratırız, CustomAdapter class'ının constructor'ı argument almaz, daha önceki örnekleri hatırlayalım, bir adapter'in constructor'ına 1.argument olarak context object, 2.argument olarak row layout xml, 3.argument olarak data source olarak kullanılacak array'i veririz, constructor'da super() method'unu çağırırız, super method'una bu argument'leri veririz. Bu örnekte ise constructor argument almaz, constructor'ın içerisinde ise super method'una ise ilgili argument'ler verilmez. Sonra setListAdapter(new CustomAdapter()) method'u çağırılır:
ArrayAdapter<MyCustomRowModel> aa = new ArrayAdapter<MyCustomRowModel>(...);
Aynı row.xml ve CustomHolder.java dosyasını kullanmaya devam edelim. Aşağıda bir ListActivity class'ı tanımlayalım. Bu activity class'ında bir string array tanımlayalım. Sonra RowModel class'ı type'ında elemanlar içeren bir ArrayList'e refer eden bir reference variable tanımlayalım. onCreate() method'unda bir ArrayList object yaratalım. Sonra array'deki elemanları arraylist'e koyarız. Sonra CustomAdapter class'ından bir object yaratırız, CustomAdapter class'ının constructor'ı argument almaz, daha önceki örnekleri hatırlayalım, bir adapter'in constructor'ına 1.argument olarak context object, 2.argument olarak row layout xml, 3.argument olarak data source olarak kullanılacak array'i veririz, constructor'da super() method'unu çağırırız, super method'una bu argument'leri veririz. Bu örnekte ise constructor argument almaz, constructor'ın içerisinde ise super method'una ise ilgili argument'ler verilmez. Sonra setListAdapter(new CustomAdapter()) method'u çağırılır:
list = new ArrayList<RowModel>();
for (String s : data) {
list.add(new RowModel(s));
}
setListAdapter(new CustomAdapter());
Sonra getModel() isimli bir method tanımlanır. Bu method'da önce getListAdapter() method'u çağırılarak ListAdapter object elde edilir, sonra bu ListAdapter object'in getItem(position) method'u çağırılarak, adapter'in ilişkili olduğu yani listview'e bağlı olan data source olan array return edilir. Yani getListAdapter().getItem(position) method'u çağırılarak, adapter'in bağlandığı array'in position'ıncı indexindeki eleman olan RowModel object return edilir.
private RowModel getModel(int position)
{
return ((ArrayAdapter<RowModel>)getListAdapter()).getItem(position);
}
Sonra ArrayAdapter<RowModel> class'ını extend eden bir custom adapter class'I tanımlanır inner class olarak. CustomAdapter class'ının constrcutor'ını inceleyelim.
private class CustomAdapter extends ArrayAdapter<RowModel>
{
public CustomAdapter()
{
super(SampleActivity.this, R.layout.row, list);
}
}
getView() method'unu inceleyelim. adapter'ün source olan array'in herhangi bir elemanına erişmek için artık getModel(index) method'Unu çağırırız. Örneğin, getModel(position) method'u çağırılarak array'in position'ıncı elemanı olan Model object return edilir. Bu model object'in str isimli variable'ı edittext alanında set edilir:
EditText text = holder.text;
text.setText(getModel(position).str);
Diğer kısımları daha önce işlemiştik o yüzden tüm detaya girmiyorum. edittext alanında bir değişiklik yapıldıktan sonra bunu array'e kaydediyorduk daha önceki örnekte bu örnekte de aynısı yapacağız ancak farklı olarak şunu yapacağız : önce değiştireceğimiz array elemanını elde edeceğiz : getModel(index) diyerek. Sonra bu object'in str variable'ına erişip buna edittext alanındaki yazıyı set edeceğiz:
getModel(index).str = v.getText().toString();
public class SampleActivity extends ListActivity {
private static final String[] data = { "lorem", "ipsum", "dolor", "sit",
"amet", "consectetur", "adipiscing", "elit", "nullam", "tempus",
"suscipit", "nunc", "a", "fringilla", "aliquam", "sed", "nisi",
"tempus", "pellentesque", "tincidunt", "nisl" };
private ArrayList<RowModel> list;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
list = new ArrayList<RowModel>();
for (String s : data) {
list.add(new RowModel(s));
}
setListAdapter(new CustomAdapter());
}
private RowModel getModel(int position) {
return ((ArrayAdapter<RowModel>)getListAdapter()).getItem(position);
}
private class CustomAdapter extends ArrayAdapter<RowModel>
{
public CustomAdapter()
{
super(SampleActivity.this, R.layout.row, list);
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
CustomHolder holder;
if (row == null)
{
row = getLayoutInflater().inflate(R.layout.row, parent, false);
row.setTag(new CustomHolder(row));
}
holder = (CustomHolder) row.getTag();
EditText text = holder.text;
text.setText(getModel(position).str);
text.setTag(new Integer(position));
text.setOnEditorActionListener(new TextView.OnEditorActionListener()
{
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
{
if (actionId == EditorInfo.IME_ACTION_DONE)
{
int index = (Integer) v.getTag();
getModel(index).str = v.getText().toString();
return true;
}
return false;
}
});
ImageView img = holder.icon;
if (position % 2 == 0)
{
img.setImageResource(R.drawable.rating_bad);
}
else
{
img.setImageResource(R.drawable.rating_good);
}
return row;
}
}
private class RowModel {
private String str;
private RowModel(String str)
{
this.str = str;
}
@Override
public String toString()
{
return str;
}
}
} The output does not change, but now we have a more sophisticated data model than a plain array/list of String objects which we could add other widgets very easily. ( Önceki örnek ve bu örnekte Output aynıdır. Ancak önceki örnekte, string ArrayList'i kullanmıştık. Ancak bu örnekte, RowModel class'ının type'ında elemanlar içeren bir ArrayList kullandık. )
-*-*
Spin Control
In Android, the Spinner is the equivalent of the drop-down selector you might find in other toolkits (e.g., JComboBox in Java/Swing). Pressing the center button on the D-pad pops up a selection dialog for the user to choose an item from. You basically get the ability to select from a list without taking up all the screen space of a ListView, at the cost of an extra click or screen tap to make a change.
As with ListView, you provide the adapter for data and child views via setAdapter() and hook in a listener object for selections via
setOnItemSelectedListener().
If you want to tailor the view used when displaying the drop-down perspective, you need to configure the adapter, not the Spinner widget. Use the setDropDownViewResource() method to supply the resource ID of the view to use. For example, culled from the Selection/Spinner sample project, here is an XML layout for a simple view with a Spinner:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/selection"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Spinner android:id="@+id/spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
This is the same view as shown in a previous section, just with a Spinner instead of a ListView. The Spinner property android:drawSelectorOnTop controls whether the arrows are drawn on the selector button on the right side of the Spinner UI.
To populate and use the Spinner, we need some Java code:
public class SpinnerDemo extends Activity
implements AdapterView.OnItemSelectedListener {
private TextView selection;
private static final String[] items={"lorem", "ipsum", "dolor",
"sit", "amet",
"consectetuer", "adipiscing", "elit", "morbi", "vel",
"ligula", "vitae", "arcu", "aliquet", "mollis",
"etiam", "vel", "erat", "placerat", "ante",
"porttitor", "sodales", "pellentesque", "augue", "purus"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
selection=(TextView)findViewById(R.id.selection);
Spinner spin=(Spinner)findViewById(R.id.spinner);
spin.setOnItemSelectedListener(this);
ArrayAdapter<String> aa=new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item,
items);
aa.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item);
spin.setAdapter(aa);
}
@Override
public void onItemSelected(AdapterView<?> parent,
View v, int position, long id) {
selection.setText(items[position]);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
selection.setText("");
}
}
Here, we attach the activity itself as the selection listener
(spin.setOnItemSelectedListener(this)), as Spinner widgets only support
selection events, not click events This works because the activity implements the OnItemSelectedListener interface. We configure the adapter not only with the list of fake words, but also with a specific resource to use for the drop-down view (via aa.setDropDownViewResource()). Also note the use of
android.R.layout.simple_spinner_item as the built-in View for showing items in the spinner itself. Finally, we implement the callbacks required by
OnItemSelectedListener to adjust the selection label based on user input.
What we get is:
Hiç yorum yok:
Yorum Gönder