Android Lists: ListActivity and ListView II – Custom Adapter and List Item View
Continued from: Android Lists: ListActivity and ListView I – Basic Usage
You’ve got a functioning list of stock ticker symbols now; however, it’s just a list of symbols and nothing more. Let’s explore how to add a static stock quote to each of the ticker symbols.
First, you can design the layout of how the stock ticker and quote should appear in the list. This is accomplished by creating a new layout file under res\layout. The following shows a simple row layout with two TextViews placed side-by-side horizontally:
<?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:layout_width="wrap_content" android:layout_height="fill_parent" android:id="@+id/ticker_symbol" /> <TextView android:layout_width="wrap_content" android:layout_height="fill_parent" android:id="@+id/ticker_price" /> </LinearLayout>
While you used the ArrayAdapter in the previous part, you can’t use that adapter here since it only accepts a layout that contains a single TextView. One of the easiest and most flexible ways to use the layout that was just created is to create your own adapter by making a new class that extends from ArrayAdapter.
To do this, you can start by making a data class that will hold the stock quote information. This class will be used as the type parameter to ArrayAdapter when you create your new adapter class. I’ve provided a basic class below that holds the stock ticker and quote, along with getters and setters.
package com.austinrasmussen.stockviewer; public class StockQuote { private String tickerSymbol; private double quote; public StockQuote(String tickerSymbol, double quote) { this.tickerSymbol = tickerSymbol; this.quote = quote; } public void setTickerSymbol(String tickerSymbol) { this.tickerSymbol = tickerSymbol; } public String getTickerSymbol() { return tickerSymbol; } public void setQuote(double quote) { this.quote = quote; } public double getQuote() { return quote; } }
Now that you have a class to hold the stock quote data, you can create the custom adapter that will use that class. The key behind extending ArrayAdapter is to override the ArrayAdapter.getView(…) method. If this isn’t done, you will run into the limitation of the ArrayAdapter class that requires you to provide it with a layout that only contains a single TextView (i.e. java.lang.IllegalStateException: ArrayAdapter requires the resource ID to be a TextView). I’ve put together an adapter class that will fulfill the requirements for this part of the series.
package com.austinrasmussen.stockviewer; 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.TextView; public class StockQuoteAdapter extends ArrayAdapter { private final Activity activity; private final List stocks; public StockQuoteAdapter(Activity activity, List objects) { super(activity, R.layout.stock_quote_list_item , objects); this.activity = activity; this.stocks = objects; } @Override public View getView(int position, View convertView, ViewGroup parent) { View rowView = convertView; StockQuoteView sqView = null; if(rowView == null) { // Get a new instance of the row layout view LayoutInflater inflater = activity.getLayoutInflater(); rowView = inflater.inflate(R.layout.stock_quote_list_item, null); // Hold the view objects in an object, // so they don't need to be re-fetched sqView = new StockQuoteView(); sqView.ticker = (TextView) rowView.findViewById(R.id.ticker_symbol); sqView.quote = (TextView) rowView.findViewById(R.id.ticker_price); // Cache the view objects in the tag, // so they can be re-accessed later rowView.setTag(sqView); } else { sqView = (StockQuoteView) rowView.getTag(); } // Transfer the stock data from the data object // to the view objects StockQuote currentStock = stocks.get(position); sqView.ticker.setText(currentStock.getTickerSymbol()); sqView.quote.setText(currentStock.getQuote().toString()); return rowView; } protected static class StockQuoteView { protected TextView ticker; protected TextView quote; } }
The only step left to do is combine the stock quote data with the newly created StockQuoteAdapter class. This will be done in the main StockList activity class that we started in the previous part of this series. All that needs to be done here is to create an ArrayList of StockQuote objects, which will be used in constructing the StockQuoteAdapter. The String array creation and list adapter code is now replaced with the following:
List stocks = new ArrayList(); stocks.add(new StockQuote("MSFT", 24.78)); stocks.add(new StockQuote("ORCL", 34.02)); stocks.add(new StockQuote("AMZN", 180.13)); stocks.add(new StockQuote("ERTS", 19.73)); setListAdapter(new StockQuoteAdapter(this, stocks));
You can now run the application and are presented with the following:
Well, that looks… quite ugly. You’ll also notice that we’ve lost the font formatting when using the simple_list_item_1 layout. This is because the simple_list_item_1 had some simple formatting applied to the single TextView in the layout.
In the next part I’ll go over layout and formatting in some more detail. This will assist in readability and help clean up the view.
Final Code
package com.austinrasmussen.stockviewer; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; import android.os.Bundle; public class StockList extends ListActivity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); List stocks = new ArrayList(); stocks.add(new StockQuote("MSFT", 24.78)); stocks.add(new StockQuote("ORCL", 34.02)); stocks.add(new StockQuote("AMZN", 180.13)); stocks.add(new StockQuote("ERTS", 19.73)); setListAdapter(new StockQuoteAdapter(this, stocks)); } }
package com.austinrasmussen.stockviewer; public class StockQuote { private String tickerSymbol; private Double quote; public StockQuote(String tickerSymbol, Double quote) { this.tickerSymbol = tickerSymbol; this.quote = quote; } public void setTickerSymbol(String tickerSymbol) { this.tickerSymbol = tickerSymbol; } public String getTickerSymbol() { return tickerSymbol; } public void setQuote(double quote) { this.quote = quote; } public Double getQuote() { return quote; } }
package com.austinrasmussen.stockviewer; 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.TextView; public class StockQuoteAdapter extends ArrayAdapter { private final Activity activity; private final List stocks; public StockQuoteAdapter(Activity activity, List objects) { super(activity, R.layout.stock_quote_list_item , objects); this.activity = activity; this.stocks = objects; } @Override public View getView(int position, View convertView, ViewGroup parent) { View rowView = convertView; StockQuoteView sqView = null; if(rowView == null) { // Get a new instance of the row layout view LayoutInflater inflater = activity.getLayoutInflater(); rowView = inflater.inflate(R.layout.stock_quote_list_item, null); // Hold the view objects in an object, // so they don't need to be re-fetched sqView = new StockQuoteView(); sqView.ticker = (TextView) rowView.findViewById(R.id.ticker_symbol); sqView.quote = (TextView) rowView.findViewById(R.id.ticker_price); // Cache the view objects in the tag, // so they can be re-accessed later rowView.setTag(sqView); } else { sqView = (StockQuoteView) rowView.getTag(); } // Transfer the stock data from the data object // to the view objects StockQuote currentStock = stocks.get(position); sqView.ticker.setText(currentStock.getTickerSymbol()); sqView.quote.setText(currentStock.getQuote().toString()); return rowView; } protected static class StockQuoteView { protected TextView ticker; protected TextView quote; } }
<?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:layout_width="wrap_content" android:layout_height="fill_parent" android:id="@+id/ticker_symbol" /> <TextView android:layout_width="wrap_content" android:layout_height="fill_parent" android:id="@+id/ticker_price" /> </LinearLayout>
<?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="wrap_content" android:id="@android:id/list" /> </LinearLayout>
This entry was posted on Saturday, April 2nd, 2011 at 10:18 pm and is filed under Android. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
viki April 6th, 2012 at 12:25 am
Fantastic! neat & clear, thanks a lot for this post