1.简介
尽管Google Maps的标准功能非常有用,但有时您还需要做更多的事情。 幸运的是,Google创建了一个开放源代码库,其中包含一组实用程序,Android开发人员可以使用这些实用程序通过增强的地图使他们的应用程序更加完善。
在本教程中,您将学习如何使用该实用程序库为数据添加热图可视化效果,如何对大量标记进行聚类以便于查看,以及如何使用各种实用程序方法来处理地球的球形特性或在道路上绘制路线。
2.设定
在本系列的第一个教程中 ,我介绍了如何使用Google Developer Console设置项目并将API密钥添加到清单中。 对于本教程,您需要获取一个API密钥并按照清单中的描述设置清单。
接下来,打开build.gradle并添加两个新的依赖项,一个依赖于Play服务以使用Google Maps,另一个依赖于Google Maps Utils库。
compile 'com.google.android.gms:play-services-maps:7.8.0'
compile 'com.google.maps.android:android-maps-utils:0.4'
我应该注意,尽管Google Maps Utils库已在过去两年中可用,但从技术上讲它仍处于测试阶段。 导入这些库并同步项目后,您需要更新MainActivity.java的布局文件,以便它使用下面显示的自定义片段。
<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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<fragment
android:id="@+id/list_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.tutsplus.mapsdemo.fragment.UtilsListFragment" />
</RelativeLayout>
接下来,创建上面使用的UtilsListFragment
类,以便显示一个简单的项目列表,这些项目代表您将在本教程中学习的库的各个部分。
public class UtilsListFragment extends ListFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayAdapter<String> adapter = new ArrayAdapter<String>( getActivity(), android.R.layout.simple_list_item_1 );
String[] items = getResources().getStringArray( R.array.list_items );
adapter.addAll( new ArrayList( Arrays.asList(items) ) );
setListAdapter( adapter );
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
String item = ( (TextView) v ).getText().toString();
if( getString( R.string.item_clustering ).equalsIgnoreCase( item ) ) {
startActivity( new Intent( getActivity(), ClusterMarkerActivity.class ) );
} else if( getString( R.string.item_heat_map ).equalsIgnoreCase( item ) ) {
startActivity( new Intent( getActivity(), HeatMapActivity.class ) );
} else if( getString( R.string.item_polylines ).equalsIgnoreCase( item ) ) {
startActivity( new Intent( getActivity(), PolylineActivity.class ) );
} else if( getString( R.string.item_spherical_geometry ).equalsIgnoreCase( item ) ) {
startActivity( new Intent( getActivity(), SphericalGeometryActivity.class ) );
}
}
}
每个字符串都已定义并放置在string-array
以保持一致。
<string name="item_clustering">Clustering</string>
<string name="item_heat_map">Heat Map</string>
<string name="item_polylines">Polyline Decoding</string>
<string name="item_spherical_geometry">Spherical Geometry Utils</string>
<string-array name="list_items">
<item>@string/item_clustering</item>
<item>@string/item_heat_map</item>
<item>@string/item_polylines</item>
<item>@string/item_spherical_geometry</item>
</string-array>

列表可用后,您需要创建BaseMapActivity.java ,它处理将要构建的每个示例活动的所有与地图相关的公共设置。 此Activity
将初始化GoogleMap
并将相机放大到指定区域。 在这种情况下,该地区就是美国科罗拉多州的丹佛市。 在本系列的最后两篇文章中,该类中的所有内容都应该看起来很熟悉。
public abstract class BaseMapActivity extends AppCompatActivity {
protected LatLng mCenterLocation = new LatLng( 39.7392, -104.9903 );
protected GoogleMap mGoogleMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( getMapLayoutId() );
initMapIfNecessary();
}
@Override
protected void onResume() {
super.onResume();
initMapIfNecessary();
}
protected void initMapIfNecessary() {
if( mGoogleMap != null ) {
return;
}
mGoogleMap = ( (MapFragment) getFragmentManager().findFragmentById( R.id.map ) ).getMap();
initMapSettings();
initCamera();
}
protected void initCamera() {
CameraPosition position = CameraPosition.builder()
.target( mCenterLocation )
.zoom( getInitialMapZoomLevel() )
.build();
mGoogleMap.animateCamera(CameraUpdateFactory.newCameraPosition(position), null);
}
protected int getMapLayoutId() {
return R.layout.activity_map;
}
protected float getInitialMapZoomLevel() {
return 12.0f;
}
protected abstract void initMapSettings();
}
现在您已经构建了初始项目,您可以继续下一部分,为在本教程中介绍的每个实用程序创建一个新的Activity
。
3.热图
热图是一种直观地表示地图上数据点浓度的极好方法。 Google Maps Utils库使将它们添加到应用程序变得容易。 首先,创建一个名为HeatMapActivity
的新BaseMapActivity
并将其添加到您的AndroidManifest.xml文件中。 在该类的顶部,声明一个HeatmapTileProvider
,我们将使用它来构建地图叠加层。
private HeatmapTileProvider mProvider;
在BaseMapActivity
,名为initMapSettings
的方法被调用,该方法允许您将自定义项添加到地图。 对于此Activity
,您需要重写该方法以获取LatLng
对象的ArrayList
,然后将其用于生成HeatmapTileProvider
对象。
提供程序提供了多种可用于更改热图外观的方法,例如渐变颜色,每个点的半径和每个点的权重。 构建提供商后,您可以创建热点图TileOverlay
并将其应用于地图。
@Override
protected void initMapSettings() {
ArrayList<LatLng> locations = generateLocations();
mProvider = new HeatmapTileProvider.Builder().data( locations ).build();
mProvider.setRadius( HeatmapTileProvider.DEFAULT_RADIUS );
mGoogleMap.addTileOverlay(new TileOverlayOptions().tileProvider(mProvider));
}
在上述initMapSettings
实现中, generateLocations
是一个辅助方法,该方法在中心地图位置周围生成1000个LatLng
位置。
private ArrayList<LatLng> generateLocations() {
ArrayList<LatLng> locations = new ArrayList<LatLng>();
double lat;
double lng;
Random generator = new Random();
for( int i = 0; i < 1000; i++ ) {
lat = generator.nextDouble() / 3;
lng = generator.nextDouble() / 3;
if( generator.nextBoolean() ) {
lat = -lat;
}
if( generator.nextBoolean() ) {
lng = -lng;
}
locations.add(new LatLng(mCenterLocation.latitude + lat, mCenterLocation.longitude + lng));
}
return locations;
}
一旦完成了initMapSettings
和generateLocations
实现,就可以运行您的应用程序,然后单击热图部分以查看其运行情况。

4.聚类标记
当地图在一个较小的区域中有很多数据点时,随着用户的缩小,它会变得非常混乱。 不仅如此,而且一次显示太多标记会导致某些设备的运行速度大大降低。
为了减轻由这些问题引起的挫败感,您可以使用Google Maps Utils库将标记动画化为群集。 您需要做的第一件事是创建一个实现ClusterItem
接口的新模型对象。 该模型需要从ClusterItem
接口实现getPosition
方法,以返回有效的LatLng
对象。
public class ClusterMarkerLocation implements ClusterItem {
private LatLng position;
public ClusterMarkerLocation( LatLng latLng ) {
position = latLng;
}
@Override
public LatLng getPosition() {
return position;
}
public void setPosition( LatLng position ) {
this.position = position;
}
}
创建模型后,您可以创建一个名为ClusterMarkerActivity
的新Activity
并将其添加到清单中。 初始化地图时,您需要创建一个ClusterManager
,将其与GoogleMap
关联,然后将LatLng
位置作为ClusterMarkerLocations
添加到ClusterManager
,以便实用程序知道要进行集群的内容。 查看initMarkers
的实现,以更好地了解其工作原理。
private void initMarkers() {
ClusterManager<ClusterMarkerLocation> clusterManager = new ClusterManager<ClusterMarkerLocation>( this, mGoogleMap );
mGoogleMap.setOnCameraChangeListener(clusterManager);
double lat;
double lng;
Random generator = new Random();
for( int i = 0; i < 1000; i++ ) {
lat = generator.nextDouble() / 3;
lng = generator.nextDouble() / 3;
if( generator.nextBoolean() ) {
lat = -lat;
}
if( generator.nextBoolean() ) {
lng = -lng;
}
clusterManager.addItem( new ClusterMarkerLocation( new LatLng( mCenterLocation.latitude + lat, mCenterLocation.longitude + lng ) ) );
}
}
在此示例中,我们创建了1000个随机点来显示并将其添加到地图中。 Google Maps Utils库为我们处理了其他所有事情。


5.其他实用程序
除了最后两项之外,Google Maps Utils库还包含许多有用的小实用程序。 如果您有许多不同的点组成一条路线,则可以将它们编码为折线 ,然后使用PolyUtil
将其添加到地图中。 这将显示地图上每个点之间的路径。
public class PolylineActivity extends BaseMapActivity {
private static final String polyline = "gsqqFxxu_SyRlTys@npAkhAzY{MsVc`AuHwbB}Lil@}[goCqGe|BnUa`A~MkbG?eq@hRq}@_N}vKdB";
@Override
protected void initMapSettings() {
List<LatLng> decodedPath = PolyUtil.decode(polyline);
mGoogleMap.addPolyline(new PolylineOptions().addAll(decodedPath));
}
}

除了PolyUtil
,Google还添加了SphericalUtil
,可用于测量距离或找出沿球体表面的几何形状。 如果要查找地图上两个点之间的距离,可以调用SphericalUtil.computeDistanceBetween( LatLng position1, LatLng position2 )
以米为单位返回该距离的double
。 如果要在两点之间找到标题,可以调用SphericalUtil.computeHeading( LatLng point1, LatLng point2 )
。
与此相关的是, SpericalUtil
类中的另一个实用程序方法使您可以在特定的航向和距离处找到一个点。 我建议浏览文档以了解有关SpericalUtil
类的更多信息。
结论
在本教程中,您仅介绍了Google Maps Utils库及其提供的所有内容。 它可以添加到您的应用程序的其他功能包括添加KML数据的叠加层,创建自定义标记以及用于处理GeoJSON的辅助方法。
幸运的是,Google已开源了整个库,因此您可以在GitHub上找到该库的源代码和演示代码。 在完成了本系列的最后三部分之后,您现在应该对Google Maps足够熟悉,可以将它们添加到自己的应用程序中,以丰富用户体验并创建出色的应用程序。
翻译自: https://code.tutsplus.com/tutorials/getting-started-with-google-maps-for-android-advanced--cms-24789