Taste of Android :: Part-8


Bhaskar S 06/29/2013


Android Application Development - VII

In Part-7, we demostrated how a background thread could update the user interface using a Handler. An simpler and easier way to acheive the same is to use the Android framework class android.os.AsyncTask. The reason AsyncTask is simpler and easier is because it automatically creates the background processing thread and manages the UI updates.

In order to use AsyncTask, we need to extend the android.os.AsyncTask class and specify 3 parameter types. The 3 parameter types indicate the type of objects that will be passed to 3 callback methods we need to override in the extended subclass. The following are the 3 methods that need to be overriden:

We will now demonstrate this concept of using AsyncTask by creating a new Android application named DroidStockTicker2.

This application is essentially the same as DroidStockTicker except that it uses AsyncTask.

Without further ado, lets get started. We will not go step-by-step to show the various screens since we already did that in Part-2 for the DroidTipCalculator application.

The contents of the dimens.xml file to look like the one shown in the listing 8.1 below:

Listing-8.1
<?xml version="1.0" encoding="utf-8"?>
<resources>

<dimen name="cell_width">0dp</dimen>
<dimen name="list_padding">5dp</dimen>

</resources>

The contents of the strings.xml file to look like the one shown in the listing 8.2 below:

Listing-8.2
<?xml version="1.0" encoding="utf-8"?>
<resources>

<!-- Text references -->

<string name="app_name">DroidStockTicker2</string>
<string name="action_settings">Settings</string>

<string name="tick_image">Tick Image</string>

<!-- Array references -->

<string-array name="stocks">
<item>AAPL</item>
<item>AMZN</item>
<item>CSCO</item>
<item>GOOG</item>
<item>IBM</item>
<item>INTC</item>
<item>LNKD</item>
<item>MSFT</item>
<item>NFLX</item>
<item>ORCL</item>
</string-array>

<string-array name="opens">
<item>450</item>
<item>267</item>
<item>24</item>
<item>871</item>
<item>209</item>
<item>24</item>
<item>169</item>
<item>35</item>
<item>223</item>
<item>34</item>
</string-array>

</resources>

The contents of the main activity layout in file activity_stock_ticker.xml will look like the one shown in the listing 8.3 below:

Listing-8.3
<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:orientation="vertical"
tools:context=".StockTickerActivity" >

<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>

</LinearLayout>

The contents of each list item layout in file list_item_row.xml will look like the one shown in the listing 8.4 below:

Listing-8.4
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >

<TableRow>
<TextView
android:id="@+id/symbol"
android:layout_width="@dimen/cell_width"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="@dimen/list_padding"
android:text=""
/>

<TextView
android:id="@+id/price"
android:layout_width="@dimen/cell_width"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="@dimen/list_padding"
android:text=""
/>

<TextView
android:id="@+id/change"
android:layout_width="@dimen/cell_width"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="@dimen/list_padding"
android:text=""
/>

<ImageView
android:id="@+id/tick"
android:contentDescription="@string/tick_image"
android:layout_width="@dimen/cell_width"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="@dimen/list_padding"
/>
</TableRow>

</TableLayout>

The Java class TickSignal is shown in the listing 8.5 below:

Listing-8.5
package com.polarsparc.android.droidstockticker2;

public enum TickSignal {
UP, FLAT, DOWN;
}

The Java class StockTicker is shown in the listing 8.6 below:

Listing-8.6
package com.polarsparc.android.droidstockticker2;

public class StockTicker {
private String symbol;
private float open;
private float change;

public String getSymbol() {
return symbol;
}

public void setSymbol(String symbol) {
this.symbol = symbol;
}

public float getPrice() {
return open+change;
}

public void setOpen(float open) {
this.open = open;
}

public float getChange() {
return change;
}

public void setChange(float change) {
this.change = change;
}

public TickSignal getTickSignal() {
if (change > 0.0f) {
return TickSignal.UP;
}
else if (change < 0.0f) {
return TickSignal.DOWN;
}
return TickSignal.FLAT;
}
}

The Java class TickerArrayAdapter is shown in the listing 8.7 below:

Listing-8.7
package com.polarsparc.android.droidstockticker2;

import java.util.List;

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

public class TickerArrayAdapter extends ArrayAdapter<StockTicker> {
private List<StockTicker> items;

public TickerArrayAdapter(Context context, List<StockTicker> items) {
super(context, R.layout.list_item_row, items);
this.items = items;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);

convertView = inflater.inflate(R.layout.list_item_row, parent, false);
}

// Symbol
TextView symbol = (TextView)convertView.findViewById(R.id.symbol);
symbol.setText(items.get(position).getSymbol());
symbol.setTextColor(Color.BLUE);

// Price
TextView price = (TextView)convertView.findViewById(R.id.price);
price.setText(String.format("%.02f", items.get(position).getPrice()));
price.setTextColor(Color.BLACK);

// Change
TextView change = (TextView)convertView.findViewById(R.id.change);
change.setText(String.format("%.02f", items.get(position).getChange()));

// Tick Image
ImageView tick = (ImageView)convertView.findViewById(R.id.tick);
switch (items.get(position).getTickSignal()) {
case UP:
tick.setImageResource(R.drawable.up_tick);
change.setTextColor(Color.GREEN);
break;
case FLAT:
tick.setImageResource(R.drawable.flat_tick);
change.setTextColor(Color.GRAY);
break;
case DOWN:
tick.setImageResource(R.drawable.down_tick);
change.setTextColor(Color.RED);
break;
}

return convertView;
}
}

Finally, we have the java source file corresponding to the main stock ticker Activity called StockTickerActivity.java.

The contents of the java source file StockTickerActivity.java will look like the one shown in the listing 8.8 below:

Listing-8.8
package com.polarsparc.android.droidstockticker2;

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

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.os.AsyncTask;

public class StockTickerActivity extends Activity {
private static String TAG = "StockTickerActivity2";

private List<StockTicker> items = new ArrayList<StockTicker>();

private ArrayAdapter<StockTicker> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stock_ticker);

String[] stocks = getResources().getStringArray(R.array.stocks);
String[] opens = getResources().getStringArray(R.array.opens);
if (stocks != null) {
for (int i = 0; i < stocks.length; i++) {
StockTicker st = new StockTicker();
st.setSymbol(stocks[i]);
st.setOpen(Float.valueOf(opens[i]));
st.setChange(0.0f);

items.add(st);
}
}

adapter = new TickerArrayAdapter(this, items);

final ListView list = (ListView) findViewById(R.id.list);
list.setAdapter(adapter);

new TickerUpdateTask().execute();
}

private class TickerUpdateTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... param) {
Random rand = new Random();
for (;;) {
try {
// Sleep for 10 seconds
Thread.sleep(10000);

for (int i = 0; i < items.size(); i++) {
float change = rand.nextFloat() * 10;
if (rand.nextInt(100) % 2 == 1) {
change = change * -1.0f;
}
StockTicker st = items.get(i);
st.setChange(change);
}

// Update progress
publishProgress();
}
catch (Throwable t) {
Log.e(TAG, "TickerUpdateTask", t);
}
}
}

@Override
protected void onProgressUpdate(Void... progress) {
adapter.notifyDataSetChanged();
}

@Override
protected void onPostExecute(Void result) {
// Not used
}
}
}

From the code Listing 8.8 above, the following are some of the interesting details:

The following figure 8.1 shows how the parameter types of AsyncTask relate to the overriden method parameter types:

AsyncTask Image
Figure-8.1

We are now ready to test our DroidStockTicker2 application on the virtual Android device we created in Part-1. We will create a Run Configuration for DroidStockTicker2 as we did in Part-2 for DroidTipCalculator.

Once the run configuration for DroidStockTicker2 is ready, we will Run the application and the application will come to life as shown in the following figure 8.2 below:

Stock Ticker2 App Image
Figure-8.2

Wait about 10 seconds and the device screen will be refreshed as shown in the following figure 8.3 below:

Stock Ticker2 App Image
Figure-8.3

References

Taste of Android :: Part-1

Taste of Android :: Part-2

Taste of Android :: Part-3

Taste of Android :: Part-4

Taste of Android :: Part-5

Taste of Android :: Part-6

Taste of Android :: Part-7