学了这么久的Android studio的开发,作者都快忘了自己的专业是GIS了,碰巧最近在搞一些GIS的开发大赛,正好学习一些GIS+Android的开发,今天也是有所收获,在此记录一下
前置条件
学习GIS的都知道,我们的开发都离不开地图的利用,所以我们首先要学会如何去调用地图,这里我们用高德地图来进行一个示范:这里是要做好的准备,在这两个文章中都有所介绍:
Android studio申请高德(百度)地图key
Android studio进阶教程之(二)–如何导入高德地图
在做完这一切之后,我们就可以进行对地图的开发了
一、控件的交互
1.放大与缩小
在我们的之前的代码中其实已经把这个功能已经做出来了:
private void initializeMap() {
if (aMap == null) {
aMap = mMapView.getMap();
// 设置信息窗口适配器
aMap.setInfoWindowAdapter(this);
// 设置Marker点击监听器
aMap.setOnMarkerClickListener(this);
// 初始地图位置和缩放级别
aMap.animateCamera(CameraUpdateFactory.zoomTo(16));
aMap.getUiSettings().setScaleControlsEnabled(true);
}
}
官方的定位是这样的:
private UiSettings mUiSettings;//定义一个UiSettings对象
mUiSettings = aMap.getUiSettings();//实例化UiSettings类对象
setZoomControlsEnabled(boolean b);//缩放按钮
2.指南针
setCompassEnabled(boolean b);
3.定位按钮
aMap.setLocationSource(this);//通过aMap对象设置定位数据源的监听
mUiSettings.setMyLocationButtonEnabled(true); //显示默认的定位按钮
aMap.setMyLocationEnabled(true);// 可触发定位并显示当前位置
4.比例尺与LOGO
setScaleControlsEnabled(boolean b);//控制比例尺控件是否显示
setLogoPosition(int position);//设置logo位置
这些都是地图应该所应有的属性,这里就不再过多赘述。
二、在地图上绘制图形
1.绘制点并包含信息
这个应该是每个GIS开发者都应该会的东西,地理信息没有信息怎么能行,所以一个包含信息的点就非常重要。
地图 SDK 提供的点标记功能包含两大部分,一部分是点(俗称 Marker)、另一部分是浮于点上方的信息窗体(俗称 InfoWindow)。
所以我们今天就以两个功能进行设计
官方代码如下:
绘制 Marker 的代码如下:
LatLng latLng = new LatLng(39.906901,116.397972);
final Marker marker = aMap.addMarker(new MarkerOptions().position(latLng).title("北京").snippet("DefaultMarker"));
绘制自定义 Marker:
MarkerOptions markerOption = new MarkerOptions();
markerOption.position(Constants.XIAN);
markerOption.title("西安市").snippet("西安市:34.341568, 108.940174");
markerOption.draggable(true);//设置Marker可拖动
markerOption.icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory
.decodeResource(getResources(),R.drawable.location_marker)));
// 将Marker设置为贴地显示,可以双指下拉地图查看效果
markerOption.setFlat(true);//设置marker平贴地图效果
Marker 点击事件
点击 Marker 时会回调AMap.OnMarkerClickListener,监听器的实现示例如下:
// 定义 Marker 点击事件监听
AMap.OnMarkerClickListener markerClickListener = new AMap.OnMarkerClickListener() {
// marker 对象被点击时回调的接口
// 返回 true 则表示接口已响应事件,否则返回false
@Override
public boolean onMarkerClick(Marker marker) {
return false;
}
};
// 绑定 Marker 被点击事件
mAMap.setOnMarkerClickListener(markerClickListener);
实现 InfoWindowAdapter
InfoWindowAdapter是一个接口,其中有两个方法需要实现,依次来看一下:
public interface InfoWindowAdapter {
View getInfoWindow(Marker marker);
View getInfoContents(Marker marker);
}
InfoWindow 点击事件
点击 InfoWindow 时会回调 AMap.OnInfoWindowClickListener,监听器的实现示例如下:
OnInfoWindowClickListener listener = new OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker arg0) {
arg0.setTitle("infowindow clicked");
}
};
//绑定信息窗点击事件
aMap.setOnInfoWindowClickListener(listener);
至此,我们的基础性质就已经学完了,接下来我们进行实战:
主体代码:
private void addMarker(double lat, double lng, String title, String phone) {
LatLng position = new LatLng(lat, lng);
aMap.addMarker(new MarkerOptions()
.position(position)
.title(title)
.snippet("售卖商品: " + phone)
.draggable(false));
}
// 实现信息窗口布局
@Override
public View getInfoWindow(Marker marker) {
View infoWindow = getLayoutInflater().inflate(R.layout.custom_info_window, null);
renderInfoWindow(marker, infoWindow);
return infoWindow;
}
@Override
public View getInfoContents(Marker marker) {
return null; // 使用默认信息窗口背景时返回null
}
private void renderInfoWindow(Marker marker, View view) {
TextView title = view.findViewById(R.id.info_window_title);
TextView content = view.findViewById(R.id.info_window_content);
title.setText(marker.getTitle());
content.setText(marker.getSnippet());
}
// 处理Marker点击事件
@Override
public boolean onMarkerClick(Marker marker) {
// 显示信息窗口(第二个参数为是否强制使用默认布局)
marker.showInfoWindow();
return true; // 消费点击事件
}
添加信息:
private void addMarkers() {
// 使用自定义方法创建带数据的Marker
addMarker(28.711666, 115.826600, "AAA水果批发商小王", "苹果");
addMarker(28.711667, 115.826750, "AAA水果批发商小李", "芒果");
addMarker(28.711680, 115.826450, "AAA水果批发商小张", "草莓");
// 东北地区
addMarker(45.8038, 126.5340, "哈尔滨水果批发商老刘", "蓝莓"); // 黑龙江哈尔滨
addMarker(43.8171, 125.3235, "长春水果批发商孙姐", "人参果"); // 吉林长春
addMarker(41.8354, 123.4299, "沈阳水果批发商老金", "南果梨"); // 辽宁沈阳
// 华北地区
addMarker(39.9042, 116.4074, "北京新发地批发市场", "平谷大桃"); // 北京
addMarker(37.8706, 112.5489, "太原水果批发商老陈", "沙金红杏"); // 山西太原
addMarker(38.0428, 114.5149, "石家庄水果批发商赵总", "赞皇大枣"); // 河北石家庄
// 华东地区
addMarker(31.2304, 121.4737, "上海西郊国际批发", "南汇水蜜桃"); // 上海
addMarker(30.2595, 120.2194, "杭州水果批发商王姐", "塘栖枇杷"); // 浙江杭州
addMarker(32.0603, 118.7969, "南京众彩批发市场", "固城湖螃蟹"); // 江苏南京
addMarker(36.6512, 117.1201, "济南堤口批发市场", "烟台樱桃"); // 山东济南
// 华中地区
addMarker(30.5951, 114.2999, "武汉光霞果批市场", "梁子湖螃蟹"); // 湖北武汉
addMarker(28.1941, 112.9723, "长沙红星批发市场", "炎陵黄桃"); // 湖南长沙
addMarker(34.7473, 113.6253, "郑州万邦物流城", "灵宝苹果"); // 河南郑州
// 华南地区
addMarker(23.1291, 113.2644, "广州江南果菜市场", "增城荔枝"); // 广东广州
addMarker(20.0444, 110.1999, "海口南北水果市场", "文昌椰子"); // 海南海口
addMarker(22.8176, 108.3663, "南宁海吉星市场", "百色芒果"); // 广西南宁
// 西北地区
addMarker(34.3416, 108.9398, "西安雨润批发市场", "周至猕猴桃"); // 陕西西安
addMarker(36.0611, 103.8343, "兰州大青山市场", "白兰瓜"); // 甘肃兰州
addMarker(43.8256, 87.6168, "乌鲁木齐九鼎市场", "哈密瓜"); // 新疆乌鲁木齐
// 西南地区
addMarker(29.6535, 91.1705, "拉萨药王山市场", "林芝苹果"); // 西藏拉萨
addMarker(25.0433, 102.7062, "昆明金马正昌市场", "蒙自石榴"); // 云南昆明
addMarker(26.6470, 106.6302, "贵阳石板哨市场", "修文猕猴桃"); // 贵州贵阳
addMarker(30.5728, 104.0668, "成都濛阳农批市场", "攀枝花芒果"); // 四川成都
// 特别行政区
addMarker(22.3193, 114.1694, "香港长沙湾市场", "菲律宾香蕉"); // 香港
addMarker(22.1987, 113.5439, "澳门提督马路市场", "台湾莲雾"); // 澳门
}
全部代码:
package com.example.gdmap;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.amap.api.maps.AMap;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.MapView;
import com.amap.api.maps.MapsInitializer;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.MyLocationStyle;
public class MapActivity extends AppCompatActivity implements
AMap.OnMarkerClickListener,
AMap.InfoWindowAdapter {
private MapView mMapView;
private AMap aMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
// 初始化地图
MapsInitializer.updatePrivacyShow(this, true, true);
MapsInitializer.updatePrivacyAgree(this, true);
mMapView = findViewById(R.id.map);
mMapView.onCreate(savedInstanceState);
initializeMap();
setupLocationStyle();
addMarkers();
}
private void initializeMap() {
if (aMap == null) {
aMap = mMapView.getMap();
// 设置信息窗口适配器
aMap.setInfoWindowAdapter(this);
// 设置Marker点击监听器
aMap.setOnMarkerClickListener(this);
// 初始地图位置和缩放级别
aMap.animateCamera(CameraUpdateFactory.zoomTo(16));
aMap.getUiSettings().setScaleControlsEnabled(true);
}
}
private void setupLocationStyle() {
MyLocationStyle myLocationStyle = new MyLocationStyle();
myLocationStyle.interval(6000);
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_SHOW);
aMap.setMyLocationStyle(myLocationStyle);
aMap.setMyLocationEnabled(true);
}
private void addMarkers() {
// 使用自定义方法创建带数据的Marker
addMarker(28.711666, 115.826600, "AAA水果批发商小王", "苹果");
addMarker(28.711667, 115.826750, "AAA水果批发商小李", "芒果");
addMarker(28.711680, 115.826450, "AAA水果批发商小张", "草莓");
// 东北地区
addMarker(45.8038, 126.5340, "哈尔滨水果批发商老刘", "蓝莓"); // 黑龙江哈尔滨
addMarker(43.8171, 125.3235, "长春水果批发商孙姐", "人参果"); // 吉林长春
addMarker(41.8354, 123.4299, "沈阳水果批发商老金", "南果梨"); // 辽宁沈阳
// 华北地区
addMarker(39.9042, 116.4074, "北京新发地批发市场", "平谷大桃"); // 北京
addMarker(37.8706, 112.5489, "太原水果批发商老陈", "沙金红杏"); // 山西太原
addMarker(38.0428, 114.5149, "石家庄水果批发商赵总", "赞皇大枣"); // 河北石家庄
// 华东地区
addMarker(31.2304, 121.4737, "上海西郊国际批发", "南汇水蜜桃"); // 上海
addMarker(30.2595, 120.2194, "杭州水果批发商王姐", "塘栖枇杷"); // 浙江杭州
addMarker(32.0603, 118.7969, "南京众彩批发市场", "固城湖螃蟹"); // 江苏南京
addMarker(36.6512, 117.1201, "济南堤口批发市场", "烟台樱桃"); // 山东济南
// 华中地区
addMarker(30.5951, 114.2999, "武汉光霞果批市场", "梁子湖螃蟹"); // 湖北武汉
addMarker(28.1941, 112.9723, "长沙红星批发市场", "炎陵黄桃"); // 湖南长沙
addMarker(34.7473, 113.6253, "郑州万邦物流城", "灵宝苹果"); // 河南郑州
// 华南地区
addMarker(23.1291, 113.2644, "广州江南果菜市场", "增城荔枝"); // 广东广州
addMarker(20.0444, 110.1999, "海口南北水果市场", "文昌椰子"); // 海南海口
addMarker(22.8176, 108.3663, "南宁海吉星市场", "百色芒果"); // 广西南宁
// 西北地区
addMarker(34.3416, 108.9398, "西安雨润批发市场", "周至猕猴桃"); // 陕西西安
addMarker(36.0611, 103.8343, "兰州大青山市场", "白兰瓜"); // 甘肃兰州
addMarker(43.8256, 87.6168, "乌鲁木齐九鼎市场", "哈密瓜"); // 新疆乌鲁木齐
// 西南地区
addMarker(29.6535, 91.1705, "拉萨药王山市场", "林芝苹果"); // 西藏拉萨
addMarker(25.0433, 102.7062, "昆明金马正昌市场", "蒙自石榴"); // 云南昆明
addMarker(26.6470, 106.6302, "贵阳石板哨市场", "修文猕猴桃"); // 贵州贵阳
addMarker(30.5728, 104.0668, "成都濛阳农批市场", "攀枝花芒果"); // 四川成都
// 特别行政区
addMarker(22.3193, 114.1694, "香港长沙湾市场", "菲律宾香蕉"); // 香港
addMarker(22.1987, 113.5439, "澳门提督马路市场", "台湾莲雾"); // 澳门
}
private void addMarker(double lat, double lng, String title, String phone) {
LatLng position = new LatLng(lat, lng);
aMap.addMarker(new MarkerOptions()
.position(position)
.title(title)
.snippet("售卖商品: " + phone)
.draggable(false));
}
// 实现信息窗口布局
@Override
public View getInfoWindow(Marker marker) {
View infoWindow = getLayoutInflater().inflate(R.layout.custom_info_window, null);
renderInfoWindow(marker, infoWindow);
return infoWindow;
}
@Override
public View getInfoContents(Marker marker) {
return null; // 使用默认信息窗口背景时返回null
}
private void renderInfoWindow(Marker marker, View view) {
TextView title = view.findViewById(R.id.info_window_title);
TextView content = view.findViewById(R.id.info_window_content);
title.setText(marker.getTitle());
content.setText(marker.getSnippet());
}
// 处理Marker点击事件
@Override
public boolean onMarkerClick(Marker marker) {
// 显示信息窗口(第二个参数为是否强制使用默认布局)
marker.showInfoWindow();
return true; // 消费点击事件
}
// 生命周期方法
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mMapView.onSaveInstanceState(outState);
}
}
信息的背景样式:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF"/>
<stroke
android:width="1dp"
android:color="#CCCCCC"/>
<corners
android:radius="8dp"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp"
android:background="@drawable/info_window_bg">
<TextView
android:id="@+id/info_window_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="#333"
android:textStyle="bold"/>
<TextView
android:id="@+id/info_window_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="14sp"
android:textColor="#666"/>
</LinearLayout>
效果图:
这样我们就在地图中插入了带有信息的点了。
2.在地图中划线
后面的部分在GIS中就没有很常用了,所以我在这里把示例代码给出,读者可以自行实现:
List<LatLng> latLngs = new ArrayList<LatLng>();
latLngs.add(new LatLng(39.999391,116.135972));
latLngs.add(new LatLng(39.898323,116.057694));
latLngs.add(new LatLng(39.900430,116.265061));
latLngs.add(new LatLng(39.955192,116.140092));
polyline =AMap.addPolyline(new PolylineOptions().
addAll(latLngs).width(10).color(Color.argb(255, 1, 1, 1)));
3.绘制多边形
画圆:
LatLng latLng = new LatLng(39.984059,116.307771);
circle = AMap.addCircle(new CircleOptions().
center(latLng).
radius(1000).
fillColor(Color.argb(progress, 1, 1, 1)).
strokeColor(Color.argb(progress, 1, 1, 1)).
strokeWidth(15));
椭圆:
aMap.addPolygon(new PolygonOptions()
.addAll(createRectangle( new LatLng(31.238068, 121.501654), 1, 1))
.fillColor(Color.LTGRAY).strokeColor(Color.RED).strokeWidth(1));
PolygonOptions options = new PolygonOptions();
int numPoints = 400;
float semiHorizontalAxis = 5f;
float semiVerticalAxis = 2.5f;
double phase = 2 * Math.PI / numPoints;
for (int i = 0; i <= numPoints; i++) {
options.add(new LatLng(Constants.BEIJING.latitude
+ semiVerticalAxis * Math.sin(i * phase),
Constants.BEIJING.longitude + semiHorizontalAxis
* Math.cos(i * phase)));
}
// 绘制一个椭圆
polygon = aMap.addPolygon(options.strokeWidth(25)
.strokeColor(Color.argb(50, 1, 1, 1))
.fillColor(Color.argb(50, 1, 1, 1)));
}
绘制长方形及其四个角
/**
* 生成一个长方形的四个坐标点
*/
private List<LatLng> createRectangle(LatLng center, double halfWidth,
double halfHeight) {
List<LatLng> latLngs = new ArrayList<LatLng>();
latLngs.add(new LatLng(center.latitude - halfHeight, center.longitude - halfWidth));
latLngs.add(new LatLng(center.latitude - halfHeight, center.longitude + halfWidth));
latLngs.add(new LatLng(center.latitude + halfHeight, center.longitude + halfWidth));
latLngs.add(new LatLng(center.latitude + halfHeight, center.longitude - halfWidth));
return latLngs;
}
}
// 绘制一个长方形
aMap.addPolygon(new PolygonOptions()
.addAll(createRectangle(Constants.SHANGHAI, 1, 1))
.fillColor(Color.LTGRAY).strokeColor(Color.RED).strokeWidth(1));
绘制多边形,以S形状为例:
// 定义多边形的5个点点坐标
LatLng latLng1 = new LatLng(42.742467, 79.842785);
LatLng latLng2 = new LatLng(43.893433, 98.124035);
LatLng latLng3 = new LatLng(33.058738, 101.463879);
LatLng latLng4 = new LatLng(25.873426, 95.838879);
LatLng latLng5 = new LatLng(30.8214661, 78.788097);
// 声明 多边形参数对象
PolygonOptions polygonOptions = new PolygonOptions();
// 添加 多边形的每个顶点(顺序添加)
polygonOptions.add(latLng1, latLng2, latLng3, latLng4, latLng5);
polygonOptions.strokeWidth(15) // 多边形的边框
.strokeColor(Color.argb(50, 1, 1, 1)) // 边框颜色
.fillColor(Color.argb(1, 1, 1, 1)); // 多边形的填充色
尾言
这次主要面向为学习GIS相关专业的移动开发的读者,因为本人也是这一方面专业的,未来也会不断将Android与GIS相结合,比如arcgis,supermap,postgis等,敬请期待。