Wednesday, January 12, 2011

Create List without extending ListActivity

Creating list view in Android is an easy task.
All we have to do is extending ListActivity, implement the methods and we are good to go.
But there are times when it is not an option, because we need to extend another class.
Let say....MapActivity? So we need to create a two tabbed view for user to be able to change from map view to list view. Our intended application may look like this.

When user click on an entry in list view, user will be redirected to the entry location in map view.
In this tutorial, we will create only a ListView then, we will add Tabs and MapView Tab.
We will need to create our implementation of ArrayAdapter and override its getView() method to accomodate our own view. We will create a title, a description and an icon for each row.

Dont forget to add uses library and internet permission in AndroidManifest.xml

First, we need to create a model class. Let say Restaurant.java

package com.tukangandroid.tutorial;
public class Restaurant {
private String name;
private String specialMenu;
private int latitude;
private int longitude;
public Restaurant(String name, String specialMenu, int latitude, int longitude) {
this.name = name;
this.specialMenu = specialMenu;
this.latitude = latitude;
this.longitude = longitude;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSpecialMenu() {
return specialMenu;
}
public void setSpecialMenu(String specialMenu) {
this.specialMenu = specialMenu;
}
public int getLatitude() {
return latitude;
}
public void setLatitude(int latitude) {
this.latitude = latitude;
}
public int getLongitude() {
return longitude;
}
public void setLongitude(int longitude) {
this.longitude = longitude;
}
}
view raw Restaurant.java hosted with ❤ by GitHub


After that, we will create our view: 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:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
view raw main.xml hosted with ❤ by GitHub


Next, create our layout for each row in our list view: row.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="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"
android:src="@drawable/icon" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="@+id/toptext"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center_vertical"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:id="@+id/bottomtext"
android:singleLine="true"
android:ellipsize="marquee"
/>
</LinearLayout>
</LinearLayout>
view raw row.xml hosted with ❤ by GitHub


Next step, our main application view: DoubleTabView.java

package com.tukangandroid.tutorial;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class DoubleTabView extends Activity {
private ListView lv;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv = (ListView)findViewById(R.id.list);
// Adding dummy data
List<Restaurant> restaurants = new ArrayList<Restaurant>();
restaurants.add(new Restaurant("Indonesian Restaurant", "babi guling", 35100000, 129100000));
restaurants.add(new Restaurant("Chinese Restaurant", "cap cay", 35110000, 129110000));
restaurants.add(new Restaurant("Korean Restaurant", "kimchi jige", 35120000, 129120000));
lv.setAdapter(new RestaurantAdapter(this,android.R.layout.simple_list_item_1, restaurants));
}
// Our array adapter, in our view, we will create a title, a description and an icon for each row
private class RestaurantAdapter extends ArrayAdapter<Restaurant> {
private List<Restaurant> items;
public RestaurantAdapter(Context context, int textViewResourceId, List<Restaurant> items) {
super(context, textViewResourceId, items);
this.items = items;
}
// Create a title and detail, icon is created in the xml file
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
Restaurant o = items.get(position);
if (o != null) {
TextView tt = (TextView) v.findViewById(R.id.toptext);
TextView bt = (TextView) v.findViewById(R.id.bottomtext);
if (tt != null) {
tt.setText("Name : "+o.getName());
}
if(bt != null){
bt.setText("Special menu: "+ o.getSpecialMenu());
}
}
return v;
}
}
}


Execute the application and we will see our generic list view application



Next, we will create a tab bar for user to choose List View or Map View and we will add the feature to navigate user to the map view whenever he/she click on a row in the List View

First, we should change our main.xml. We add TabHost, TabWidget and MapView

<?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">
<TabHost android:id="@+id/tabhost"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TabWidget android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<FrameLayout android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="62px">
<ListView android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<com.google.android.maps.MapView
android:id="@+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:apiKey="0Tmz2clOfrJGA0_c_d17-gSC4We41u7Onmx9W_Q"
/>
</FrameLayout>
</TabHost>
</LinearLayout>
view raw main.xml hosted with ❤ by GitHub


Next, we add the tab name programatically, then we add navigateToLocation method to navigate and zoom to the restaurant location

package com.tukangandroid.tutorial;
import java.util.ArrayList;
import java.util.List;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class DoubleTabView extends MapActivity {
private ListView lv;
private TabHost tabs;
private MapView mapView;
private List<Overlay> mapOverlays;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tabs=(TabHost)findViewById(R.id.tabhost);
tabs.setup();
TabHost.TabSpec spec=tabs.newTabSpec("list");
spec.setContent(R.id.list);
spec.setIndicator("List View");
tabs.addTab(spec);
spec=tabs.newTabSpec("mapview");
spec.setContent(R.id.mapview);
spec.setIndicator("Map View");
tabs.addTab(spec);
tabs.setCurrentTab(0);
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapOverlays = mapView.getOverlays();
lv = (ListView)findViewById(R.id.list);
// Adding dummy data
final List<Restaurant> restaurants = new ArrayList<Restaurant>();
restaurants.add(new Restaurant("Indonesian Restaurant", "babi guling", 35100000, 129100000));
restaurants.add(new Restaurant("Chinese Restaurant", "cap cay", 35110000, 129110000));
restaurants.add(new Restaurant("Korean Restaurant", "kimchi jige", 35120000, 129120000));
lv.setAdapter(new RestaurantAdapter(this,android.R.layout.simple_list_item_1, restaurants));
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int position,
long arg3) {
// When user click a row, we get the row coordinate and navigate to that location
navigateToLocation(restaurants.get(position), mapView, false);
tabs.setCurrentTab(1);
}
});
}
public void navigateToLocation(Restaurant restaurant, MapView mv, boolean isDefault) {
mapOverlays.clear();
Drawable drawable = this.getResources().getDrawable(R.drawable.icon);
HelloItemizedOverlay itemizedOverlay = new HelloItemizedOverlay(drawable);
GeoPoint point = new GeoPoint(restaurant.getLatitude(), restaurant.getLongitude());
OverlayItem overlayitem = new OverlayItem(point, "", "");
itemizedOverlay.addOverlay(overlayitem);
mapOverlays.add(itemizedOverlay);
mv.displayZoomControls(true);
MapController mc = mv.getController();
mc.animateTo(point); // move map to the given point
int zoomlevel = mv.getMaxZoomLevel() - 1;
mc.setZoom(zoomlevel); // zoom
mv.setSatellite(false); // display only "normal" mapview
}
// Our array adapter, in our view, we will create a title, a description and an icon for each row
private class RestaurantAdapter extends ArrayAdapter<Restaurant> {
private List<Restaurant> items;
public RestaurantAdapter(Context context, int textViewResourceId, List<Restaurant> items) {
super(context, textViewResourceId, items);
this.items = items;
}
// Create a title and detail, icon is created in the xml file
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
Restaurant o = items.get(position);
if (o != null) {
TextView tt = (TextView) v.findViewById(R.id.toptext);
TextView bt = (TextView) v.findViewById(R.id.bottomtext);
if (tt != null) {
tt.setText("Name : "+o.getName());
}
if(bt != null){
bt.setText("Special menu: "+ o.getSpecialMenu());
}
}
return v;
}
}
@Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}
}


Build and run.
Our final application should look like this
Click one restaurant, and the application will navigate and zoom to the location

That's it! Have fun!





4 comments: