Search This Blog

Friday, 16 March 2012

Chapter 17:Android and Lists


 Android and Lists

 ListView

The display of elements in a lists is a very common pattern in mobile applications. The user gets a list of items and can scroll through them. If he selects one item this usually triggers something else.
Android provides the ListView class which is capable of displaying a scrollable list of items. These items can be of any type.
For example the following provides a layout file with includes a ListView.
                                
<?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/mylist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout>

Input for the Adapter

The ArrayAdapter class can handle any Java object as input. Per default it will call the toString() method on each object for the data which should be displayed.
In the constructor of ArrayAdapter your can optionally specify a resource ID to a View to which the data should be will assigned. This optional, if nothing is specified the adapter use the android.R.id.text1 ID as default.
ArrayAdapter assigns the output of the toString() to this View.
To influence what data is displayed in which field of the row, you can create your own Adapter, e.g. by extending the ArrayAdapter class.
Your row layout can also contain Views which interact with the underlying data model. For example you can have a Checkbox in your row layout and if the Checkbox is selected you change the data which is displayed in the row.

Change data in the Adapter

The ArrayAdapter class allows to remove all elements in its underlying data structure with the clear() method call. You can then add new elements via the add() method or a Collection via the addAll() method.

Listener

To react to selections in the list set an OnItemClickListener to your ListView.
                                
listView.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {
                Toast.makeText(getApplicationContext(),
                        "Click ListItem Number " + position, Toast.LENGTH_LONG)
                        .show();
        }
});
                        

 Filtering lists

ListView supports filtering of elements via its adapter. The ArrayAdapter class implements also the Filterable interface and the ArrayFilter default implementation as inner class defined.
To filter in a ArrayAdapter based on a String, just use adapter.getFilter().filter(searchString).
Typically you want to add a a EditText field to your layout and attach a TextChangeListener to it.
                                
EditText filterEditText = (EditText) findViewById(R.id.filterText);
filterEditText.addTextChangedListener(new TextWatcher() {
        @Override
    public void onTextChanged(CharSequence s, int start, int before,
      int count) {
      adapter.getFilter().filter(s.toString());
    }
 
     @Override
     public void beforeTextChanged(CharSequence s, int start, int count,
      int after) {
     }
 
     @Override
     public void afterTextChanged(Editable s) {
     }
});
                        

Own Adapters

You can develop your own adapter by extending existing adapter implementations or by sub-classing the BaseAdapter class directly.
The getView() method is called for every line in the ListView and determines the layout and the data assignment of the row.
This method is responsible for creating the individual rows of your ListView. The getView() method returns a View for each row.
This View is typically a Layout ( ViewGroup ) and contains several other Views, e.g. an ImageView and a TextView.
Within the getView() method you would typically inflate an XML based layout and then set the values of the individual Views in the layout based on the data for the row. The individual elements in the layout can be found via the findViewById() method call. To optimize performance you would buffer data and reuse existing rows if possible. We cover that later.
To extract an layout from an XML resource in the getView() method you can use the system service LayoutInflator. This service can get accessed via the Activity or via the context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) method call.
The following shows an implementation of an own adapter. This adapter assumes that you have two png files (no.png and yes.png) in your drawable* folder. If this extract the layout for the rows, assigned the data to the text field, and if the entries starts with "IPhone" is will display the no.png image.
                                
package de.vogella.android.listactivity;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class MySimpleArrayAdapter extends ArrayAdapter<String> {
        private final Context context;
        private final String[] values;

        public MySimpleArrayAdapter(Context context, String[] values) {
                super(context, R.layout.rowlayout, values);
                this.context = context;
                this.values = values;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                LayoutInflater inflater = (LayoutInflater) context
                                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                View rowView = inflater.inflate(R.layout.rowlayout, parent, false);
                TextView textView = (TextView) rowView.findViewById(R.id.label);
                ImageView imageView = (ImageView) rowView.findViewById(R.id.icon);
                textView.setText(values[position]);
                // Change the icon for Windows and iPhone
                String s = values[position];
                if (s.startsWith("iPhone")) {
                        imageView.setImageResource(R.drawable.no);
                } else {
                        imageView.setImageResource(R.drawable.ok);
                }

                return rowView;
        }
}

                        

 ListActivity

Overview

The ListActivity class which extends the Activity class was designed to simplify the handling of ListViews.
It you don't assign a layout to a ListActivity it contains a default ListView and defines the onListItemClick() method for handling selection of list items. Internally the ListActivity registers an OnItemClickListener on the ListView.
ListActivity allows to set the adapter to the ListView via the setListAdapter() method.
                                
package de.vogella.android.listactivity;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;

public class MyListActivity extends ListActivity {
        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
                                "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
                                "Linux", "OS/2" };
                ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                                android.R.layout.simple_list_item_1, values);
                setListAdapter(adapter);
        }
}
                        

ListActivity and Layout




An ListActivity has per default a layout available which includes a default ListView. Therefore you are not required to use the setContentView() to assign a layout to your Activity method.
In case you need more the just a ListView in your Activity, you can use you own layout for ListActivity.
In this case your layout must have an ListView element with the android:id attribute set to @android:id/list. For example:
                                
<ListView
  android:id="@android:id/list"
  android:layout_width="match_parent"
  android:layout_height="wrap_content" >
</ListView>
                        
You can also use a view with the id @android:id/empty. This view is displayed if the list is empty. For example you could display here an error message.

ListViews and performance

ConvertView

In a ListView not all rows are visible at the same time. If the user scrolls the list then certain rows (and their associated views) will not be visible anymore.
Every View which get inflated from the XML resources will result in a Java object. Creating Java objects is expensive with regards to time and memory consumption.
Not all rows are displayed at the same time, therefore Android recycles rows ( Views ) which are not displayed anymore and allows that these rows are reused. If it has a recycled row, it passes these rows to getView() method as convertView parameter.
A performance optimized adapter assigns the new content to the recycled row. Only updating the content of the row avoids loading the XML layout. That is a big performance saver, as reading XML files is an expensive operation.
Your adapter implementation can re-use this view and can avoid inflating a layout for the current row. This saves memory and CPU consumption. It just need to set the new values into the existing layout.
convertView may be NULL if there is no row to recycle, your implementation need to check this.
Via the parameter convertView in the getView() method you can re-use an existing row and fill the Views of this row with new data. If this convertView is not null you can re-use it.

Holder Pattern

The findViewById() method is an expensive operation, therefore we should avoid doing this operation if not necessary. You can use the "View Holder"" pattern for this.
A ViewHolder class is a static class in your adapter which allows to hold references to the fields in your layout so that you can avoid using findViewById() on your layout.
The ViewHolder stores a reference to the required views in a row. This ViewHolder is then attached to the row via the setTag() method. Every view can get a tag assigned. If the row is recycled we can get the ViewHolder via getTag() method.
This is much faster then the repetitive call of the findViewById() method.

 ArrayAdapter

The default Android adapter like ArrayAdapter is already performance optimized, if you are using this class you don't have to do anything. But if you write your own Adapter you have to take
that into consideration.

 Example

                                



package de.vogella.android.listactivity;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

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;
                if (rowView == null) {
                        LayoutInflater inflater = context.getLayoutInflater();
                        rowView = inflater.inflate(R.layout.rowlayout, null);
                        ViewHolder viewHolder = new ViewHolder();
                        viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01);
                        viewHolder.image = (ImageView) rowView
                                        .findViewById(R.id.ImageView01);
                        rowView.setTag(viewHolder);
                }

                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;
        }
}

                        

Create ListActivity

The following will demonstrate how to use a ListView in an ListActivity the ArrayAdapter class and a predefined layout from Android.
Create a new Android project called de.vogella.android.listactivity with the Activity called MyListActivity.
Change MyListActivity to the following. Note that the setContentView() method is not used.
                        
package de.vogella.android.listactivity;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class MyListActivity extends ListActivity {
        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
                                "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
                                "Linux", "OS/2" };
                ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                                android.R.layout.simple_list_item_1, values);
                setListAdapter(adapter);
        }

        @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
                String item = (String) getListAdapter().getItem(position);
                Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show();
        }
}
                

ListActivity with own layout

In our example your will define your layout for the rows and use it in your adapter.
Create the "rowlayout.xml" layout file in the "res/layout" folder of the "de.vogella.android.listactivity" project.
                        
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="22px"
        android:layout_height="22px"
        android:layout_marginLeft="4px"
        android:layout_marginRight="10px"
        android:layout_marginTop="4px"
        android:src="@drawable/ic_launcher" >
    </ImageView>

    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@+id/label"
        android:textSize="20px" >
    </TextView>

</LinearLayout>
                
Change your Activity so that is using the new layout. You use a different constructor to identify the View to which the ArrayAdapter assigns the text. If this ID is not provides Android searches for an element with the @android:id/text1 ID in the layout of the row.
                        
package de.vogella.android.listactivity;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class MyListActivity extends ListActivity {
        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
                                "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
                                "Linux", "OS/2" };
                // Use your own layout
                ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                                R.layout.rowlayout, R.id.label, values);
                setListAdapter(adapter);
        }

        @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
                String item = (String) getListAdapter().getItem(position);
                Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show();
        }
}
                

Implementing your own adapter

Defining a simple Adapter




The following uses two images "no.png" and "ok.png". I placed it in the "res/drawable-mdpi" folder. You must create your own icons. In case you don't find any icons just copy "icon.png" and use a drawing program to change it a little bit.
Create the class MySimpleArrayAdapter which will serve as our adapter.
                                
package de.vogella.android.listactivity;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class MySimpleArrayAdapter extends ArrayAdapter<String> {
        private final Context context;
        private final String[] values;

        public MySimpleArrayAdapter(Context context, String[] values) {
                super(context, R.layout.rowlayout, values);
                this.context = context;
                this.values = values;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                LayoutInflater inflater = (LayoutInflater) context
                                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                View rowView = inflater.inflate(R.layout.rowlayout, parent, false);
                TextView textView = (TextView) rowView.findViewById(R.id.label);
                ImageView imageView = (ImageView) rowView.findViewById(R.id.icon);
                textView.setText(values[position]);
                // Change the icon for Windows and iPhone
                String s = values[position];
                if (s.startsWith("Windows7") || s.startsWith("iPhone")
                                || s.startsWith("Solaris")) {
                        imageView.setImageResource(R.drawable.no);
                } else {
                        imageView.setImageResource(R.drawable.ok);
                }

                return rowView;
        }
}

                        
To use this adapter, change the Activity to the following.
                                
package de.vogella.android.listactivity;

import android.app.ListActivity;
import android.os.Bundle;

public class MyListActivity extends ListActivity {
        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
                                "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
                                "Linux", "OS/2" };
                MySimpleArrayAdapter adapter = new MySimpleArrayAdapter(this, values);
                setListAdapter(adapter);
        }

}
                        
If you run this example you should get a list with different icons for the certain elements.

Performance Optimization

The following will implement a performance optimized version of the adapter from the previous example.
Create the following MyPerformanceArrayAdapter class.
                                
package de.vogella.android.listactivity;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

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;
                if (rowView == null) {
                        LayoutInflater inflater = context.getLayoutInflater();
                        rowView = inflater.inflate(R.layout.rowlayout, null);
                        ViewHolder viewHolder = new ViewHolder();
                        viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01);
                        viewHolder.image = (ImageView) rowView
                                        .findViewById(R.id.ImageView01);
                        rowView.setTag(viewHolder);
                }

                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;
        }
}

                        
Use your new adapter in your Activity. If you run the application it should look the same but it will be much faster, especially for large datasets.
                                
package de.vogella.android.listactivity;

import android.app.ListActivity;
import android.os.Bundle;

public class MyListActivity extends ListActivity {
        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
                                "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
                                "Linux", "OS/2" };
                setListAdapter(new MyPerformanceArrayAdapter(this, values));
        }

}
                        

How to display two items in a ListView

You can use the SimpleAdapter class to show the data of two elements. This class expects a Array of Strings ( from data) in which the fields of the input data are defined. It also requires a Array of ints which defines the ID's of the widgets in the layout for the row to which these fields are mapped.
The actual data is then a list of Maps. The Map defines for each field in the from data a value.
The following shows an example which reuses an predefined layout from Android for the row.
                        
package de.vogella.android.listactivity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.SimpleAdapter;

public class MyTwoListItemsActivity extends ListActivity {
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                ArrayList<Map<String, String>> list = buildData();
                String[] from = { "name", "purpose" };
                int[] to = { android.R.id.text1, android.R.id.text2 };

                SimpleAdapter adapter = new SimpleAdapter(this, list,
                                android.R.layout.simple_list_item_2, from, to);
                setListAdapter(adapter);
        }

        private ArrayList<Map<String, String>> buildData() {
                ArrayList<Map<String, String>> list = new ArrayList<Map<String, String>>();
                list.add(putData("Android", "Mobile"));
                list.add(putData("Windows7", "Windows7"));
                list.add(putData("iPhone", "iPhone"));
                return list;
        }

        private HashMap<String, String> putData(String name, String purpose) {
                HashMap<String, String> item = new HashMap<String, String>();
                item.put("name", name);
                item.put("purpose", purpose);
                return item;
        }
        
}

                

 Domain Model and Rows interaction

The following example will demonstrate how to use standard Java object and how to interact from the Views of the row with the model.
We still use the same project.
Create the following Model which hold the name and the information if this element is currently selected.
                        
package de.vogella.android.listactivity;

public class Model {

        private String name;
        private boolean selected;

        public Model(String name) {
                this.name = name;
                selected = false;
        }

        public String getName() {
                return name;
        }

        public void setName(String name) {
                this.name = name;
        }

        public boolean isSelected() {
                return selected;
        }

        public void setSelected(boolean selected) {
                this.selected = selected;
        }

}

                
Create a new layout file called "rowbuttonlayout.xml".
                        
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@+id/label"
        android:textSize="30px" >
    </TextView>

    <CheckBox
        android:id="@+id/check"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginLeft="4px"
        android:layout_marginRight="10px" >
    </CheckBox>

</RelativeLayout>
                
Create the following Adapter. This adapter adds a listener on the Checkbox. If Checkbox is selected the underlying data of the model is changed. Checkbox gets the corresponding model element assigned via the setTag() method.
                        
package de.vogella.android.listactivity;

import java.util.List;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;

public class InteractiveArrayAdapter extends ArrayAdapter<Model> {

        private final List<Model> list;
        private final Activity context;

        public InteractiveArrayAdapter(Activity context, List<Model> list) {
                super(context, R.layout.rowbuttonlayout, list);
                this.context = context;
                this.list = list;
        }

        static class ViewHolder {
                protected TextView text;
                protected CheckBox checkbox;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                View view = null;
                if (convertView == null) {
                        LayoutInflater inflator = context.getLayoutInflater();
                        view = inflator.inflate(R.layout.rowbuttonlayout, null);
                        final ViewHolder viewHolder = new ViewHolder();
                        viewHolder.text = (TextView) view.findViewById(R.id.label);
                        viewHolder.checkbox = (CheckBox) view.findViewById(R.id.check);
                        viewHolder.checkbox
                                        .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

                                                @Override
                                                public void onCheckedChanged(CompoundButton buttonView,
                                                                boolean isChecked) {
                                                        Model element = (Model) viewHolder.checkbox
                                                                        .getTag();
                                                        element.setSelected(buttonView.isChecked());

                                                }
                                        });
                        view.setTag(viewHolder);
                        viewHolder.checkbox.setTag(list.get(position));
                } else {
                        view = convertView;
                        ((ViewHolder) view.getTag()).checkbox.setTag(list.get(position));
                }
                ViewHolder holder = (ViewHolder) view.getTag();
                holder.text.setText(list.get(position).getName());
                holder.checkbox.setChecked(list.get(position).isSelected());
                return view;
        }
}

                
Finally change your Activity to the following.
                        
package de.vogella.android.listactivity;

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

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;

public class MyList extends ListActivity {

        
/** Called when the activity is first created. */

        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                // Create an array of Strings, that will be put to our ListActivity
                ArrayAdapter<Model> adapter = new InteractiveArrayAdapter(this,
                                getModel());
                setListAdapter(adapter);
        }

        private List<Model> getModel() {
                List<Model> list = new ArrayList<Model>();
                list.add(get("Linux"));
                list.add(get("Windows7"));
                list.add(get("Suse"));
                list.add(get("Eclipse"));
                list.add(get("Ubuntu"));
                list.add(get("Solaris"));
                list.add(get("Android"));
                list.add(get("iPhone"));
                // Initially select one of the items
                list.get(1).setSelected(true);
                return list;
        }

        private Model get(String s) {
                return new Model(s);
        }

}
                
If you start your app you should be able to flag items. These changes will be reflected in your model.




Miscellaneous

 Adding a longclick listener to the list items

You can also add a LongItemClickListener to the View. For this receive the ListView via the getListVIew() method and set the LongItemClickListener via the setOnItemLongClickListener() method.
                                
package de.vogella.android.listactivity;

mport android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class MyList extends ListActivity {

        
/** Called when the activity is first created. */

        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                // Create an array of Strings, that will be put to our ListActivity
                String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse",
                                "Ubuntu", "Solaris", "Android", "iPhone" };
                ArrayAdapter<String> adapter = new MyPerformanceArrayAdapter(this, names);
                setListAdapter(adapter);
                ListView list = getListView();
                list.setOnItemLongClickListener(new OnItemLongClickListener() {

                        @Override
                        public boolean onItemLongClick(AdapterView<?> parent, View view,
                                        int position, long id) {
                                Toast.makeText(MyList.this,
                                                "Item in position " + position + " clicked",
                                                Toast.LENGTH_LONG).show();
                                // Return true to consume the click event. In this case the
                                // onListItemClick listener is not called anymore.
                                return true;
                        }
                });
        }

        @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
                super.onListItemClick(l, v, position, id);
                // Get the item that was clicked
                Object o = this.getListAdapter().getItem(position);
                String keyword = o.toString();
                Toast.makeText(this, "You selected: " + keyword, Toast.LENGTH_SHORT)
                                .show();

        }

}
                        

Single vs. Multiselection

You can also support single and multi selection. See the following snippets for examples. To get the selected item(s) use listView.getCheckedItemPosition() or listView.getCheckedItemPositions(). If you have stable ID you could also use listView.getCheckedItemIds() to get the selected ids.
                                
package de.vogella.android.listactivity;

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

public class MyList extends ListActivity {

        
/** Called when the activity is first created. */

        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                // Create an array of Strings, that will be put to our ListActivity
                String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse",
                                "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7",
                                "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" };
                setListAdapter(new ArrayAdapter<String>(this,
                                android.R.layout.simple_list_item_multiple_choice,
                                android.R.id.text1, names));
                ListView listView = getListView();
                listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
        }

}
                        
                                
package de.vogella.android.listactivity;

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

public class MyList extends ListActivity {

        
/** Called when the activity is first created. */

        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                // Create an array of Strings, that will be put to our ListActivity
                String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse",
                                "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7",
                                "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" };
                setListAdapter(new ArrayAdapter<String>(this,
                                android.R.layout.simple_list_item_single_choice,
                                android.R.id.text1, names));
                ListView listView = getListView();
                listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        }

}
                        

Header and Footer

You can of course put arbitray elements around your ListView. For example you can define a layout with two TextViews and a ListView between them. If you do this, you must assign the id "@android:id/list" to the ListView, as the ListActivity searches for a view with this id. If you do this then one TextView will always be visible above the List (header) and the other will be visible below the ListView. If you want to display the header / footer view only if see the beginning / end of the list you can use view.setHeaderView() or view.setFooterView(). For example:
                                
package de.vogella.android.listactivity;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MyList extends ListActivity {

        
/** Called when the activity is first created. */

        public void onCreate(Bundle icicle) {
                super.onCreate(icicle);
                // Create an array of Strings, that will be put to our ListActivity
                String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse",
                                "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7",
                                "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" };
                View header = getLayoutInflater().inflate(R.layout.header, null);
                View footer = getLayoutInflater().inflate(R.layout.footer, null);
                ListView listView = getListView();
                listView.addHeaderView(header);
                listView.addFooterView(footer);
                setListAdapter(new ArrayAdapter<String>(this,
                                android.R.layout.simple_list_item_single_choice,
                                android.R.id.text1, names));

        }
}

                        

SimpleCursorAdapter

In case you work with a content provider or directly with the database you can use the SimpleCursorAdapter to define the data for your ListView. The following will demonstrates how to access the Contacts ContentProvider.
Create a new Android project called "de.vogella.android.listactivity.cursor" with the Activity called "MyListActivity". Change MyListActivity to the following.
                        
package de.vogella.android.listactivity.cursor;

import android.app.ListActivity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.ListAdapter;
import android.widget.SimpleCursorAdapter;

public class MyListActivity extends ListActivity {
        
/** Called when the activity is first created. */

        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                Cursor mCursor = getContacts();
                startManagingCursor(mCursor);
                // Now create a new list adapter bound to the cursor.
                // SimpleListAdapter is designed for binding to a Cursor.
                ListAdapter adapter = new SimpleCursorAdapter(this, // Context.
                                android.R.layout.two_line_list_item, // Specify the row template
                                                                                                                // to use (here, two
                                                                                                                // columns bound to the
                                                                                                                // two retrieved cursor
                                                                                                                // rows).
                                mCursor, // Pass in the cursor to bind to.
                                // Array of cursor columns to bind to.
                                new String[] { ContactsContract.Contacts._ID,
                                                ContactsContract.Contacts.DISPLAY_NAME },
                                // Parallel array of which template objects to bind to those
                                // columns.
                                new int[] { android.R.id.text1, android.R.id.text2 });

                // Bind to our new adapter.
                setListAdapter(adapter);
        }

        private Cursor getContacts() {
                // Run query
                Uri uri = ContactsContract.Contacts.CONTENT_URI;
                String[] projection = new String[] { ContactsContract.Contacts._ID,
                                ContactsContract.Contacts.DISPLAY_NAME };
                String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '"
                                + ("1") + "'";
                String[] selectionArgs = null;
                String sortOrder = ContactsContract.Contacts.DISPLAY_NAME
                                + " COLLATE LOCALIZED ASC";

                return managedQuery(uri, projection, selection, selectionArgs,
                                sortOrder);
        }

}





Example
Using ArrayAdapter and ListView in Android Applications 

Adding views

First create an empty android project. Then edit the main.xml layout file to add a ListView. Then create another layout xml file which will contain the TextView (or any component) that will be displayed within the ListView.

Editing Activity

The next step is to change the generated activity class to extend from ListActivity. This is very important because only a ListActivity will be able to display the ListView.

Binding the adapter

The next step is to bind the ArrayAdapter to the ListActivity. We can do this by calling the setListAdapter() method.
To this method we have to pass an object of type ArrayAdapter. You can pass an object reference to this method or we can even create a new anonymous method like below.

setListAdapter(new ArrayAdapter<string>(this, R.layout.list_item, strings) {
    @Override
       public View getView(int position, View convertView, ViewGroup parent) {
         View row;
 
              if (null == convertView) {
                      row = mInflater.inflate(R.layout.list_item, null);
              } else {
                        row = convertView;
              }
 
              TextView tv = (TextView) row.findViewById(android.R.id.text1);
          tv.setText(getItem(position));
 
         return row;
     }
});

MainActivity
package com.sudarmuthu.android.homework.week2;


public class MainActivity extends ListActivity {
private List<String> strings;
private LayoutInflater mInflater;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
strings = new ArrayList<String>();
strings.add("This");
strings.add("is");
strings.add("a");
strings.add("long");
strings.add("list");
strings.add("which");
strings.add("will");
strings.add("be");
strings.add("displayed");
strings.add("in");
strings.add("the");
strings.add("screen");
strings.add("with");
strings.add("one");
strings.add("word");
strings.add("in");
strings.add("a");
strings.add("line");
setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, strings) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row;
if (null == convertView) {
row = mInflater.inflate(R.layout.list_item, null);
} else {
row = convertView;
}
TextView tv = (TextView) row.findViewById(android.R.id.text1);
tv.setText(getItem(position));
return row;
}
});
}
}
List-layout.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">
<TextView
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:gravity="center_vertical"
android:layout_height="wrap_content"
android:textSize="16dip"
>
</TextView>
</LinearLayout>
Main.xml
<?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"
>
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@android:id/list"></ListView>
</LinearLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sudarmuthu.android.homework.week2"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
<uses-sdk android:minSdkVersion="4" />

</manifest>





http://kahdev.wordpress.com/2010/02/16/using-an-arrayadapter-to-control-a-listviews-data/













No comments:

Post a Comment