您的位置:首页 > 移动开发 > Android开发

android tab with interacting map and list views

2011-06-16 08:16 916 查看
from:http://joshclemm.com/blog/?p=86



Last tutorial, we wrote a simple app that displays two interacting list views in a
TabActivity
. In this tutorial, we will up the ante and add a
MapView
as the content of one of the tabs. Why again are we using multiple views in an activity instead of using a separate activity for each tab content? Remember, we want our tabs to be able to easily interact with one another, and keeping them as views allows us to handle the logic and interaction within one activity.

So, our goal in this tutorial is to have a list of geo coordinates and when we click on an item in the list, our map view goes to that location.

First off, let's create an XML layout that contains a
TabHost
,
TabWidget
, a
ListView
, and a
MapView
.

01
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
02
<
TabHost
xmlns:android
=
"http://schemas.android.com/apk/res/android"
03
android:id
=
"@android:id/tabhost"
android:layout_width
=
"fill_parent"
04
android:layout_height
=
"fill_parent"
>
05
<
LinearLayout
android:orientation
=
"vertical"
06
android:layout_width
=
"fill_parent"
android:layout_height
=
"fill_parent"
>
07
<
TabWidget
android:id
=
"@android:id/tabs"
08
android:layout_width
=
"fill_parent"
android:layout_height
=
"wrap_content"
/>
09
<
FrameLayout
android:id
=
"@android:id/tabcontent"
10
android:layout_width
=
"fill_parent"
android:layout_height
=
"fill_parent"
>
11
<
LinearLayout
android:orientation
=
"vertical"
12
android:layout_width
=
"fill_parent"
android:layout_height
=
"fill_parent"
13
android:id
=
"@+id/main"
>
14
<
ListView
android:id
=
"@+id/list"
android:layout_width
=
"fill_parent"
15
android:layout_height
=
"0dip"
android:layout_weight
=
"1"
/>
16
<
TextView
android:id
=
"@+id/empty"
android:layout_width
=
"wrap_content"
17
android:layout_height
=
"wrap_content"
android:gravity
=
"center"
18
android:text
=
"Loading"
/>
19
</
LinearLayout
>
20
<
RelativeLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
21
android:id
=
"@+id/mainlayout"
android:orientation
=
"vertical"
22
android:layout_width
=
"fill_parent"
android:layout_height
=
"fill_parent"
>
23
<
com.google.android.maps.MapView
24
android:id
=
"@+id/mapview"
android:layout_width
=
"fill_parent"
25
android:layout_height
=
"fill_parent"
android:clickable
=
"true"
26
android:apiKey
=
"[your MAPS API KEY here]"
/>
27
</
RelativeLayout
>
28
</
FrameLayout
>
29
</
LinearLayout
>
30
</
TabHost
>

Once we have a layout, we need to create our main Activity. While last tutorial our
Activity
was of type
TabActivity
, in this tutorial our
Activity
has to be of type
MapActivity
.

Why is this? Using a MapView in Android must be in a
MapActivity
class, otherwise your app with throw an Exception (take a look at the activities source code if you're interested in more details). As a result of this, we will have to perform the functions of
TabActivity
ourselves in the
MapActivity
class (you'll see that below).

Let's create a class called
TabbedListMapActivity
and have it extend
MapActivity
.

In the
onCreate()
method, we need to set the content to our XML layout, extract the
TabHost
object, and call
setup()
on the
TabHost
(we need to do this because our Activity is not a TabActivity).

1
setContentView(R.layout.main);
2
3
tabHost = (TabHost) findViewById(android.R.id.tabhost);
4
5
// setup must be called if you are not inflating the tabhost from XML
6
tabHost.setup();
Next, we want to extract our
ListView
from the XML, set it to a member variable, and add some initial coordinates to its list adapter.

1
// setup list view
2
listView = (ListView) findViewById(R.id.list);
3
listView.setEmptyView((TextView) findViewById(R.id.empty));
4
5
// create some dummy coordinates to add to the list
6
List<GeoPoint> pointsList =
new
ArrayList<GeoPoint>();
7
pointsList.add(
new
GeoPoint((
int
)(
32.864
*1E6), (
int
)(-
117.2353
*1E6)));
8
pointsList.add(
new
GeoPoint((
int
)(
37.441
*1E6), (
int
)(-
122.1419
*1E6)));
9
listView.setAdapter(
new
ArrayAdapter(
this
, android.R.layout.simple_list_item_1, pointsList));
Then, we want to extract our
MapView
from the XML and set it to a member variable.

1
// setup map view
2
mapView = (MapView) findViewById(R.id.mapview);
3
mapView.setBuiltInZoomControls(
true
);
4
mapView.postInvalidate();
Once we have our
MapView
, we can add a listener to the
ListView
for selection events. Let's add a listener that when an item is clicked, we set the map to the coordinates of the selected item.

01
// add an onclicklistener to see point on the map
02
listView.setOnItemClickListener(
new
OnItemClickListener() {
03
public
void
onItemClick(AdapterView parent, View view,
int
position,
long
id) {
04
GeoPoint geoPoint = (GeoPoint) listView.getAdapter().getItem(position);
05
if
(geoPoint !=
null
) {
06
// have map view moved to this point
07
setMapZoomPoint(geoPoint,
12
);
08
// programmatically switch tabs to the map view
09
tabHost.setCurrentTab(
1
);
10
}
11
}
12
});
The
setMapZoomPoint
method is implemented like this:

1
private
void
setMapZoomPoint(GeoPoint geoPoint,
int
zoomLevel) {
2
mapView.getController().setCenter(geoPoint);
3
mapView.getController().setZoom(zoomLevel);
4
mapView.postInvalidate();
5
}
The final step is to add our two views (
MapView
and
ListView
) as content to our tab host.

01
// add views to tab host
02
tabHost.addTab(tabHost.newTabSpec(
"List"
).setIndicator(
"List"
).setContent(
new
TabContentFactory() {
03
public
View createTabContent(String arg0) {
04
return
listView;
05
}
06
}));
07
tabHost.addTab(tabHost.newTabSpec(
"Map"
).setIndicator(
"Map"
).setContent(
new
TabContentFactory() {
08
public
View createTabContent(String arg0) {
09
return
mapView;
10
}
11
}));

Now if you were to run this app now, you may notice strange behavior (might just be on my phone). You should see our two tabs starting with the list view tab. Instead you may see the
MapView
"bleeding" through the
ListView
. I'm not exactly sure what causes this, but a workaround is to manually set the tab view to the map, then manually set the tab view back to the list. This causes the activity to redraw the tabs correctly. Here's my workaround in code:

1
//HACK to get the list view to show up first,
2
// otherwise the mapview would be bleeding through and visible
3
tabHost.setCurrentTab(
1
);
//the mapview
4
tabHost.setCurrentTab(
0
);
//the listview
Now everything should look right and you should be able to click a coordinate on the list view, and the map view tab goes to that spot on the earth.

If you are having issues with seeing the Map, remember you need to use the Google-Apis instead of the standard Android apis, you need to generate a Maps API key, you need to add interest permissions to the manifest, and you need to make sure the uses-library line is inside your application tag in the manifest.

Here's my manifest for reference:

01
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
02
<
manifest
xmlns:android
=
"http://schemas.android.com/apk/res/android"
03
package
=
"com.joshclemm.android.tutorial"
android:versionCode
=
"1"
04
android:versionName
=
"1.0"
>
05
<
application
android:icon
=
"@drawable/icon"
android:label
=
"@string/app_name"
>
06
<
activity
android:name
=
".TabbedListMapActivity"
android:label
=
"@string/app_name"
>
07
<
intent-filter
>
08
<
action
android:name
=
"android.intent.action.MAIN"
/>
09
<
category
android:name
=
"android.intent.category.LAUNCHER"
/>
10
</
intent-filter
>
11
</
activity
>
12
<
uses-library
android:name
=
"com.google.android.maps"
/>
13
</
application
>
14
<
uses-sdk
android:minSdkVersion
=
"4"
></
uses-sdk
>
15
<
uses-permission
android:name
=
"android.permission.INTERNET"
/>
16
</
manifest
>
To see an activity with a map and list view in action, check out my Earthquake Alert! Android App

Here's the full Java source code:

001
package
com.joshclemm.android.tutorial;
002
003
import
java.util.ArrayList;
004
import
java.util.List;
005
006
import
android.os.Bundle;
007
import
android.view.View;
008
import
android.widget.AdapterView;
009
import
android.widget.ArrayAdapter;
010
import
android.widget.ListView;
011
import
android.widget.TabHost;
012
import
android.widget.TextView;
013
import
android.widget.AdapterView.OnItemClickListener;
014
import
android.widget.TabHost.OnTabChangeListener;
015
import
android.widget.TabHost.TabContentFactory;
016
017
import
com.google.android.maps.GeoPoint;
018
import
com.google.android.maps.MapActivity;
019
import
com.google.android.maps.MapView;
020
021
public
class
TabbedListMapActivity
extends
MapActivity
implements
OnTabChangeListener {
022
023
private
static
final
String LIST_TAB_TAG =
"List"
;
024
private
static
final
String MAP_TAB_TAG =
"Map"
;
025
026
private
TabHost tabHost;
027
028
private
ListView listView;
029
private
MapView mapView;
030
031
@Override
032
public
void
onCreate(Bundle savedInstanceState) {
033
super
.onCreate(savedInstanceState);
034
setContentView(R.layout.main);
035
036
tabHost = (TabHost) findViewById(android.R.id.tabhost);
037
038
// setup must be called if you are not inflating the tabhost from XML
039
tabHost.setup();
040
tabHost.setOnTabChangedListener(
this
);
041
042
// setup list view
043
listView = (ListView) findViewById(R.id.list);
044
listView.setEmptyView((TextView) findViewById(R.id.empty));
045
046
// create some dummy coordinates to add to the list
047
List<GeoPoint> pointsList =
new
ArrayList<GeoPoint>();
048
pointsList.add(
new
GeoPoint((
int
)(
32.864
*1E6), (
int
)(-
117.2353
*1E6)));
049
pointsList.add(
new
GeoPoint((
int
)(
37.441
*1E6), (
int
)(-
122.1419
*1E6)));
050
listView.setAdapter(
new
ArrayAdapter(
this
, android.R.layout.simple_list_item_1, pointsList));
051
052
// add an onclicklistener to see point on the map
053
listView.setOnItemClickListener(
new
OnItemClickListener() {
054
public
void
onItemClick(AdapterView parent, View view,
int
position,
long
id) {
055
GeoPoint geoPoint = (GeoPoint) listView.getAdapter().getItem(position);
056
if
(geoPoint !=
null
) {
057
// have map view moved to this point
058
setMapZoomPoint(geoPoint,
12
);
059
// programmatically switch tabs to the map view
060
tabHost.setCurrentTab(
1
);
061
}
062
}
063
});
064
065
// setup map view
066
mapView = (MapView) findViewById(R.id.mapview);
067
mapView.setBuiltInZoomControls(
true
);
068
mapView.postInvalidate();
069
070
// add views to tab host
071
tabHost.addTab(tabHost.newTabSpec(LIST_TAB_TAG).setIndicator(
"List"
).setContent(
new
TabContentFactory() {
072
public
View createTabContent(String arg0) {
073
return
listView;
074
}
075
}));
076
tabHost.addTab(tabHost.newTabSpec(MAP_TAB_TAG).setIndicator(
"Map"
).setContent(
new
TabContentFactory() {
077
public
View createTabContent(String arg0) {
078
return
mapView;
079
}
080
}));
081
082
//HACK to get the list view to show up first,
083
// otherwise the mapview would be bleeding through and visible
084
tabHost.setCurrentTab(
1
);
085
tabHost.setCurrentTab(
0
);
086
}
087
088
/**
089
 
* Instructs the map view to navigate to the point and zoom level specified.
090
 
* @param geoPoint
091
 
* @param zoomLevel
092
 
*/
093
private
void
setMapZoomPoint(GeoPoint geoPoint,
int
zoomLevel) {
094
mapView.getController().setCenter(geoPoint);
095
mapView.getController().setZoom(zoomLevel);
096
mapView.postInvalidate();
097
}
098
099
/**
100
 
* From MapActivity, we ignore it for this demo
101
 
*/
102
@Override
103
protected
boolean
isRouteDisplayed() {
104
return
false
;
105
}
106
107
/**
108
 
* Implement logic here when a tab is selected
109
 
*/
110
public
void
onTabChanged(String tabName) {
111
if
(tabName.equals(MAP_TAB_TAG)) {
112
//do something on the map
113
}
114
else
if
(tabName.equals(LIST_TAB_TAG)) {
115
//do something on the list
116
}
117
}
118
}





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: