Download In this lab, you will learn the basics of the ListView widget and how

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
LAB FRAGMENTS LISTS AND OTHER
THINGS
Abstract
In this lab, you will learn the basics of the ListView widget and how to work with the Adapter class.
You will create a custom adapter that derives from the Adapter class.

Key Terms

The Adapter design pattern makes incompatible things compatible. Android uses adapters to,
among other things, wire the ListView and GridView widgets to and underlying data
source.

The Android Cursor object provides random read-write access to the results of a database
query.

The CursorAdapter object provides the ability to wire a Cursor to a widget such as a
ListView or GridView.
Introduction to Lists and the ListView Widget
The ListView and ExpandableListView classes (Lists) are used to display a scrollable list of
repeating items. In this lab, you will ultimately a daily weather forecast as an item in a list. However,
you will first go through a simple example to see the process by which a list is populated.
Lists are populated via what are called adapters. Android supports various categories of adapters.

The ArrayAdapter populates a single TextView with the results of the toString()
method in the array.

By overriding the toString() method, you can render custom objects. To display each list
item in a layout containing multiple widgets, you must create a custom layout. You override
getView(int, View, ViewGroup)

The CursorAdapter exposes a Cursor to a ListView. A Cursor is an object that
provides access to the results of a database query.

The SimpleCursorAdapter simplifies the process of mapping columns in a cursor to
TextViews or ImageViews.
1
To illustrate, suppose that you wanted to display a scrollable list of items. When the user clicks on one of
the list items, something should happen. In this example, you will display a simple toast but you could
display another activity or fragment. Just about anything. As you progress through the lab, you will
display the current weather forecast. This is the same data that you worked with in the previous lab.
In this first example, you will display an array of strings as follows:
String values = new String[] { “a”, “b”, “c”};
All list items in a ListView are created (bound) via an Adapter. In Android, there is no way to
statically wire the list items with text as you can do in Visual Basic. The connection between the
ListView and the data to be rendered is connected through the ArrayAdapter class. Through the
ArrayAdapter, you tell the ListView the following:

The data type of the object being bound.

The context. This is the same Context object that you have been using throughout the course.

The resource ID of the layout file used to populate the list items. As you will see later in the lab,
you can use layouts provided by the global Android layout file, or you can create custom layouts
for each list item, header, and footer.

The ID of the TextView widget within the layout. There must be a TextView widget within
the layout.

The list of objects to be populated.
To illustrate, look at the following statement:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, android.R.id.text1, values);
The generic ArrayAdapter class requires a type: the data type of objects in the list to be bound. In
this first example, the data type of the list objects is String. However, as you will see later, it’s
possible to work with complex data types.
The constructor used in this example takes 4 arguments as described in the previous list.

The first is the Context. Again, the context is provided through the Activity because the
Activity class is derived from the Context class.

The second argument requires some explanation. As you will see later in the lab, you can render
list data in custom layouts. However, here you are using a default list layout provided by
Android itself. The fragment android.R. refers to the android global R Android file. The
global Android R.Layout file stores a number of constants for simple predefined Android
layouts as shown in the following figure.
2
That R file also contains the following declaration for the TextView named text1.
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/listItemFirstLineStyle"
android:paddingTop="2dip"
android:paddingBottom="3dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
You will see later in this tutorial how to create and reference your own custom layout files.

The values argument contains a reference to the array (or other complex type) that will be
bound to the ListView.
Next, you call the setAdapter() method to connect the ArrayAdapter with the data source, and
thereby populate the list.
listView.setAdapter(adapter);
The setAdapter() method apples to the ListView widget. It accepts one argument, an instance of
the ArrayAdpter or a class that derives from the ArrrayAdapter class.
HANDS-ON ACTIVTY: Populating a first ListView control
In this set of steps you will create the template for a multi-activity application. You will then declare an
array and a ListView. You will configure the ArrayAdapter to populate the ListView with the
data from the array using the predefined layouts.
3
1. Create a new Android Application project. Name the startup activity MainActivity.java as usual.
2. Create two more activities named SimpleAdapterActivity and ComplexAdapterActivity. On the
main activity’s layout file (activity_main.xml), modify the layout as follows so that it contains
two buttons:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.fragmentproject.MainActivity" >
<Button
android:id="@+id/btnSimpleAdapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/icon"
android:layout_alignParentTop="true"
android:text="Simple Adapter" />
<Button
android:id="@+id/btnComplexAdapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/icon"
android:layout_alignParentTop="true"
android:text="ComplexAdapter Adapter" />
</LinearLayout>
3. In the MainActivity.java class, write the following code for the two buttons so that they will
display the second and third activity. Create the following classes to implement the button’s
OnClickListener class.
private class btnSimpleAdapter implements Button.OnClickListener
{
@Override
public void onClick(View v)
{
Intent intent = new
Intent(MainActivity.this,SimpleAdapterActivity.class);
startActivity(intent);
}
}
private class btnComplexAdapter implements Button.OnClickListener
{
@Override
public void onClick(View v)
4
{
Intent intent = new
Intent(MainActivity.this,ComplexAdapterActivity.class);
startActivity(intent);
}
}
4. Write the following code at the beginning of the class to declare the necessary references to the
Button widgets.
public class MainActivity extends Activity {
private Button btnSimpleAdapter;
private Button btnComplexAdapter;
5. Write the following statements in the onCreate() method to wire the event listeners.
btnSimpleAdapter = (Button) findViewById(R.id.btnSimpleAdapter);
btnSimpleAdapter.setOnClickListener(new btnSimpleAdapter());
btnComplexAdapter = (Button) findViewById(R.id.btnComplexAdapter);
btnComplexAdapter.setOnClickListener(new btnComplexAdapter());
6. On the layout page named activity_simple_adapter.xml, modify the code as follows adding the
ListView widget to the end of the layout file as follows:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.fragmentproject.SimpleAdapterActivity" >
<ListView
android:id="@+id/list"
android:layout_height="wrap_content"
android:layout_width="match_parent">
</ListView>
</RelativeLayout>
7. Compile the program to regenerate the R file.
8. In the SimpleAdapterActivity.java file, enter the following code to declare a reference to the
ListView widget as follows. Enter the statement immediately after the class declaration but
5
before the onCreate() method.
ListView listView
9. Write the following statement to declare a reference to the ListView control and to declare
the the String array that will appear in the list. This code should appear in the Activity’s
onCreate() method immediately after the call to setContentView().
listView = (ListView) findViewById(R.id.list);
String[] values = new String[] {
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"};
10. Next, declare the ArrayAdapter and assign it to the ListView as shown in the following
statements: This code should appear immediately after the code that you just wrote.
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
android.R.id.text1, values);
listView.setAdapter(adapter);
11. Test the application. Click the Simple Adapter button. You should see the list items appear as
shown in the following figure:
6
Wiring a ListView Event Handler and getting the Selected Item
The ListView widget supports the OnItemClickListener interface which requires that you
handle the onItemClick() event. However, the onItemClick() event is a bit more complicated
than a button’s onClick() event. The event handler has 4 arguments as follows:

The first argument named parent has a data type of AdapterView and contains the
AdapterView where the click happened. The <?> syntax indicates that the argument is a
generic.

The second argument named view contains the View within the adapter that was clicked. This
View is supplied by the Adapter.

The third argument is an integer containing the position of the View within the Adapter.

The final argument named id contains the row id of the item that was clicked. The row id is 0based.
The following code segment shows the event handler:
@Override
public void onItemClick(AdapterView<?> parent, View view, int
position, long id)
{
int itemPosition = position;
String itemValue = (String) listView.getItemAtPosition(position);
Toast.makeText(getApplicationContext(), "Position :" +
itemPosition + " ListItem : " + itemValue,
Toast.LENGTH_LONG).show();
}
The above code segment handles the onItemClick() event for the OnItemClickListener()
interface. The statements get the position of the item in the list. The following statement is the most
critical.
String itemValue = (String) listView.getItemAtPosition(position);
The getItemAtPosition() method of the ListView class gets a reference to the list item. In this
case, the data type of each list item is a String – hence the cast. However, as you will see later in the
lab, it’s possible to work with complex data types. The method requires one argument, the integer
position of the item to retrieve.
HANDS-ON ACTIVTY: Handling a ListView control event
7
In this sequence of steps, you will write the code to handle the onItemClick() event for the
ListView widget. In this example, you will use the event wiring technique of implementing an
interface rather than using an anonymous class.
1. Activate the Code Editor for the SimpleAdapterActivity.java class. Enter the following code at
the end of the class to create the event listener.
private class lstItemClick implements ListView.OnItemClickListener
{
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
// ListView Clicked item index
int itemPosition = position;
// ListView Clicked item value
String itemValue = (String) listView.getItemAtPosition(position);
// Show Alert
Toast.makeText(getApplicationContext(), "Position :" +
itemPosition + " "
+ "ListItem : " + itemValue,
Toast.LENGTH_LONG).show();
}
}
2. Write the following statement at the end of the onCreate() event to register the event listener
that you just created:
listView.setOnItemClickListener(new lstItemClick())
3. Test the application. The list items should appear. Click one of the list items. The toast should
appear displaying the item value and the item text.
Creating a List of Complex Objects
In the previous code, you created a simple Adapter from the predefined ArrayAdapter class. In
this part of the lab, you will create an Adapter from a custom class that you create. In general, you
will perform the following tasks:

Create a new Layout that will display a ListView header row.

Create a new Layout that will display a ListView detail row.

Create a simple class named Weather that will store an icon, and the current weather.

Create another class named WeatherAdapter that will extend the ArrayAdapter class.
8

Override the getView() method.

Wire this custom Adapter to the ListView() widget.
Creating Custom Layouts for a ListView
So far, you have seen how to create layouts that pertain to an activity. Layouts have other purposes too.
As you will see in the next lab, layouts are used for parts of an activity called Fragments. In addition, you
can create custom layouts and associate them with the rows in a ListView. In this lab, you will create
two such layouts.

The first named lv_header_row.xml will apply to the ListView’s header row. This view will
contain a single TextView widget.

The second named lv_item_row will be used to render each item in the list. This view will
contain both an ImageView and a TextView widget.
HANDS-ON ACTIVITY: To create custom views
1. Make sure that the FragmentProject is selected.
2. Click File, New, Other to display the following dialog box:
3. Select Android XML Layout File, and then click Next to display the following dialog box:
9
4. Select a LinerLayout. Name the layout lv_item_row. Click finish to create the layout. The layout
will appear in the res/layout folder.
5. Modify the code in the XML layout file (lv_item_row.xml).
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp">
<ImageView android:id="@+id/imgIcon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="15dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp" />
<TextView android:id="@+id/txtTitle"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:textStyle="bold"
android:textSize="22dp"
10
android:textColor="#000000"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp" />
</LinearLayout>
6. Create a second layout file named lv_header_row.xml. Again, use a LinearLayout. Enter the
following code in the xml file.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="@+id/txtHeader"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:textStyle="bold"
android:textSize="22dp"
android:textColor="#FFFFFF"
android:padding="10dp"
android:text="Weather Photos"
android:background="#336699" />
</LinearLayout>
7. Recompile the program so as to regenerate the R. file.
Note the following about the code that you just wrote.

Neither of these layouts has a corresponding Java file. As you will see later in the lab, you will
programmatically associate these layouts with the ListView widget.

The first of the layout files defines a list item (detail row). Each list item contains an icon
(ImageView) and text (TextView).

The second layout file describes the item that will appear in the header row. This item contains a
single <TextView>.
Creating a Custom Adapter Class
Instead of using an existing data type (array of String) for the Adapter, you will create a custom
class named Weather as follows. This class is fairly simple – it stores two objects. An icon and a string.
As you progress through this lab, you will expand the class to store more complex data. The following
code segment illustrates the Weather class.
11
public class Weather {
public int icon;
public String title;
public Weather(){
super();
}
public Weather(int icon, String title) {
super();
this.icon = icon;
this.title = title;
}
}
The above class is made up of two public properties named icon and title. The first will point to an
icon resource (in the R file), and the second a String intended to store the weather forecast.
Following these two declarations are two constructors. Later in the lab, you will call the second
constructor. The second constructor stores the icon and title in the two public properties of the same
name.
In addition to the above class, a second class will be used to store a reference to the <ImageView>
and <TextView> that will be rendered in the View as follows:
static class ViewHolder
{
ImageView imgIcon;
TextView txtTitle;
}
The next and most complicated part of the process is to create a custom class that inherits from the
ArrayAdapter class as follows. The creation of this class will be discussed in segments.
public class WeatherAdapter extends ArrayAdapter<Weather>
{
}
The above syntax should look familiar. The WeatherAdapter class inherits from (extends) the
ArrayAdapter class. Note that the Adapter class in this case is bound to an object of type
Weather rather than a String. Thus <Weather> appears in the generic declaration.
Next, the WeatherAdapter constructor must be overridden as shown in the following code segment.
Context context;
int layoutResourceId;
Weather data[] = null;
public WeatherAdapter(Context context, int layoutResourceId,
Weather[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
12
this.context = context;
this.data = data;
}
The arguments to WeatherAdapter constructor are the same as the arguments to the base class
(ArrayAdapter). The first statement calls the base class constructor. The remaining statements store
the constructor’s arguments into the class’ variables.
Most of the work happens in the overridden getView() method of the Adapter class. The
getView() method gets a View that displays the data at a specified position (row) in the DataSet.
In this case, View (position) is the current row. The following code segment shows the declaration of
the getView() method.
@Override
public View getView(int position, View convertView, ViewGroup parent) {

The first argument (position), contains the position of the item within the Adapter that we
want to retrieve. The position is 0-based.

The second argument (convertView) contains the View.

The third argument (parent) contains the parent View to which this View will be attached.
Next, we examine the code in the getView() callback, which requires some careful analysis. The
following code segment shows the shell of the code for the getView() method:
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ViewHolder holder = null;
if(row == null)
{
}
else
{
}
}

The above code segment declares two variables. The first (row) stores a reference to the View.
The second (holder) stores a reference to the ViewHolder object (discussed in a moment).

The if statement tests whether the variable row is null or not.

The getView() method fires for the Adapter for each row that is bound. Recall that Android
performs garbage collection in the background. So, a View might be recycled if there is
inadequate memory. If the View has been recycled, then it will need to be recreated. Also, if
the Activity has not yet been rendered, the View must be created for the first time. If the
View has not been recycled, then the View need not be recreated.
13
The following code segment shows the code to create the View:
if(row == null)
{
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ViewHolder();
holder.imgIcon = (ImageView)row.findViewById(R.id.imgIcon);
holder.txtTitle = (TextView)row.findViewById(R.id.txtTitle);
row.setTag(holder);
}
You have seen briefly how the getMenuInflator().inflate() method inflates a menu causing
the menu items to be added to the action bar. A similar process needs to be performed to inflate each
View in the Adapter.

The first of the preceding statements inflate the current View (row). It calls the
getLayoutInflator() method of the current Context to get the inflator.

The next statement inflates (renders) the View hierarchy from the XML resource and stores a
reference in the variable row. The first argument to the inflate method contains the
resource to load. The variable layoutResourceID is an integer containing the layout
resource. This data was passed to the WeatherAdapter constructor. You will see how to call
the constructor in a moment.

The next group of statements create an instance of the ViewHolder class. The following code
segment shows this class:
static class ViewHolder
{
ImageView imgIcon;
TextView txtTitle;
}
The only purpose of this class is to store a reference to the ImageView and the TextView that are
rendered for each list item.
holder = new ViewHolder();
holder.imgIcon = (ImageView)row.findViewById(R.id.imgIcon);
holder.txtTitle = (TextView)row.findViewById(R.id.txtTitle);
row.setTag(holder);

The first of the preceding statements creates an instance of the ViewHolder class. The next
two statements get a reference to the Image and TextView controls for the current row. The
setTag() method (of the View class), stores additional information about an object. In this
case, the additional information is the Image and TextView.
14

If the row is not null, then the following statement executes.
holder = (ViewHolder)row.getTag();
If the current row is not null (because the ListView has already been populated and the item
as not been recycled), the above statement executes. This statement gets the tag and stores it
into the variable holder.

The next statements form the final part of the event handler.
Weather weather = data[position];
holder.txtTitle.setText(weather.title);
holder.imgIcon.setImageResource(weather.icon);
return row;

The first of the above statements gets the current data row data[position] and stores the
current row into the variable named weather.

Next the setText() method of the TextView stores the text in the holder. The
setImageResource() method stores the image in the holder.

The final statement returns the current row.
Displaying the Complex View
In this final sequence of steps, you will write the code to populate the view from custom
ArrayAdapter.
1. First, create a class named Weather.java. Remember, to create a simple Java class, click File,
New, Class. In the New Java Class dialog, name the class Weather. Make sure that the Modifier
is set to Public. Do not create a static void main method. Click Finish to create the class.
2. Enter the following code in the class.
public class Weather {
public int icon;
public String title;
public Weather(){
super();
}
public Weather(int icon, String title) {
super();
this.icon = icon;
this.title = title;
}
15
}
3. Create a second class named WeatherAdpater.java, and enter the following code in the class:
public class WeatherAdapter extends ArrayAdapter<Weather>{
Context context;
int layoutResourceId;
Weather data[] = null;
public WeatherAdapter(Context context, int layoutResourceId,
Weather[] data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ViewHolder holder = null;
if(row == null)
{
LayoutInflater inflater =
((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ViewHolder();
holder.imgIcon = (ImageView)row.findViewById(R.id.imgIcon);
holder.txtTitle = (TextView)row.findViewById(R.id.txtTitle);
row.setTag(holder);
}
else
{
holder = (ViewHolder)row.getTag();
}
Weather weather = data[position];
holder.txtTitle.setText(weather.title);
holder.imgIcon.setImageResource(weather.icon);
return row;
}
static class ViewHolder
{
ImageView imgIcon;
TextView txtTitle;
}
16
4. In the next sequence of steps, you will write the code to populate the adapter. Activate the
Code Editor for the file named ComplextAdapterActivity.java. Enter the following code in the
onCreate() method immediately after the call to the setContentView() method.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_complex_adapter);
Weather weather_data[] = new Weather[]
{
new Weather(R.drawable.ic_launcher,
new Weather(R.drawable.ic_launcher,
new Weather(R.drawable.ic_launcher,
new Weather(R.drawable.ic_launcher,
new Weather(R.drawable.ic_launcher,
};
"Cloudy"),
"Rain"),
"Rain"),
"Snow"),
"Snow")
WeatherAdapter adapter = new WeatherAdapter(this,
R.layout.lv_item_row, weather_data);
listView1 = (ListView)findViewById(R.id.listView1);
View header =
(View)getLayoutInflater().inflate(R.layout.lv_header_row, null);
listView1.addHeaderView(header);
listView1.setAdapter(adapter);
}

The first of the preceding statement declares the array (weather_data[]) that will be bound
to the custom adapter.

The next statement creates an instance of the custom adapter.

The following statements inflate the view for the header row (lv_header_row), and add the
view as the header row.

The final statement associates the custom adapter with the ListView thereby binding it.
1. Try out the application. Click the 2nd button. You should see the bound ListView widget.
17