新项目中需要用到GIS地图,触及自己的知识盲区。所以有必要去学习一下,这里把学习过程与思考记录一下。

我这里使用的是ArcGIS Runtime SDK 100.2.0,ArcGIS Runtime还有一个版本10.2.*,这个版本的ArcGIS Runtime SDK 的使用可以参考《ArcGIS Runtime SDK for Android开发笔记》系列blog。


1、找资料

1、适用于Android的ArcGIS Runtime SDK为Android设备开发GIS地图,这里有android开发的流程,SDK的相关API。ArcGIS Runtime SDKs

Android检查位置信息是否启用并请求打开 安卓查看位置信息_gis

2、GIS地图制作工具–ArcGIS Pro

Android检查位置信息是否启用并请求打开 安卓查看位置信息_gis_02


2、简单使用

明白相关资料和工具的地址之后,我们就可以开始开发了。

1、在项目的build.gradle文件中,添加以下代码

allprojects {
    repositories {
        jcenter()
        // Add the Esri public Bintray Maven repository
        maven {
            url 'https://esri.bintray.com/arcgis'
        }
    }
}

2、在app Module的build.gradle文件中,添加以下代码

//arcgis-android
 compile 'com.esri.arcgisruntime:arcgis-android:100.1.0'

3、在项目的清单文件AndroidManifest.xml中,添加以下权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />

uses-feature说明:

AndroidManifest中的uses-feature配置用来声明一个app在运行时所依赖的外部的硬件或软件特征(feature),uses-feature还提供了一个required属性配置,表示此项依赖的软硬件特征是否是必须的,当它设置为true表示此app运行时必须使用此项特征,如果没有则无法工作,如果它设置为false,表示应用在运行时需要用到这些特征,但如果没有,应用可能会有一部分功能会受到影响,但大部分功能还是可以正常工作。例如一个拍照app,它使用时必须开启设备的摄像头,在没有摄像头的机器上任何功能都无法使用,这就需要通过uses-feature来声明该应用需要摄像头,并将required设置为true。再比如一个支付app,它支持扫码支付的功能,这项功能同样需要开启设备的摄像头,因此需要通过uses-feature声明该应用需要摄像头,但如果一个设备没有摄像头,仅意味着扫码支付的功能无法使用,其他支付方式仍然可以使用,这时就可以设置required属性为false,表明此项feature的需求不是必须的。

布局文件

<com.esri.arcgisruntime.mapping.view.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/main_header" />

代码中获取GIS地图信息

mMapView = (MapView) findViewById(R.id.mapView);
ArcGISMap map = new ArcGISMap(Basemap.Type.TOPOGRAPHIC, 34.056295, -117.195800, 16); 
mMapView.setMap(map);

注意加上下面的代码

@Override 
protected void onPause(){
  mMapView.pause();
  super.onPause();
}

@Override 
protected void onResume(){
  super.onResume();
  mMapView.resume();
}

到这里里的程序应该已经可以显示出Gis地图了。


3、相关概念

离线地图

新项目会经常性的在无信号地区进行相关操作,所以需要使用离线地图。ArcGis关于离线地图的说明。

您的应用程序可以使用OfflineMapTask创建地图的本地副本。此任务请求生成脱机数据所需的所有服务,然后将该数据下载并存储到您的设备中。在地图下载后,您的应用可以脱机并开始使用离线地图。有关如何使用OfflineMapTask的更多详细信息,请参阅创建离线地图文档的服务部分。

在版本100.1中,OfflineMapTask支持以下图层类型:平铺服务,要素服务和要素集合

移动地图包

可以使用ArcGIS Pro将离线地图打包到移动地图包(.mmpk文件)中。 每个软件包可以包含多个地图,相关的图层和数据,以及可选的网络和定位器。 将移动地图包下载或装载到设备后,可以使用MobileMapPackage类打开包并开始使用离线地图。 有关如何使用移动地图包的更多详细信息,请参阅创建离线地图文档。

数据源

有很多的地理数据来源。

1、在线数据源由在线GIS Web服务提供,其中包括地图服务功能服务

ArcGIS地图服务分为两大类:平铺式动态式。平铺服务根据图层的平铺方案提供预先生成(缓存)的图片。可以将瓦片作为栅格图像瓦片或作为矢量数据瓦片来提供。然后该图层在客户端应用程序中组装这些图片。您可以使用平铺图层访问平铺的地图服务。但是,动态地图服务提供了按照客户要求的即时创建的地图图像。您可以使用地图图像层访问动态地图服务

功能服务允许您访问地图中的各个功能。要素服务(例如来自ArcGIS Enterprise和ArcGIS Online的要素服务)会返回来自单个图层的要素集,以响应地图的可见范围或属性或空间查询。您可以使用要素图层来访问此类服务

ArcGIS图像服务通过Web服务提供对栅格数据的访问。图像服务可以像地图服务一样缓存以提高性能当图像服务被缓存时,它支持动态访问和平铺访问动态访问提供访问数据,查询和下载,以及访问单个项目。它还提供访问数据用于处理和分析,而平铺访问提供了更快和可扩展的预加载作为平铺服务。要将图像服务作为动态图像服务访问,请使用栅格图层,该图层允许您检索图像服务的元数据,并使用具有栅格函数的数据进行分析。要将缓存的图像服务作为平铺服务访问,请使用平铺图层,该图层仅允许您检索平铺服务的元数据并显示平铺图像。

2、离线数据源,您还可以访问位于本地的数据源,例如移动地图包或平铺包。这些本地数据源也具有特定类型的层来访问数据。

图层

下图显示了API中的主要图层类和继承关系。 斜体的类名是抽象的。

通用图层属性

所有图层类都从Layer类继承公共属性。这个类实现了Loadable接口,它提供了加载图层资源的异步模式。该类还实现了LayerContent接口。下面列出了该类及其接口的一些常见属性:

使用getName方法显示图层的名称。
使用getDescription方法查看图层的描述。
使用getFullExtent方法获取图层的完整范围。
使用getSpatialReference方法确定图层使用的空间参考。
使用setVisible方法隐藏和显示图层。
使用opacity属性控制图层透明或不透明的使用setOpacity方法。
使用setMinScale和setMaxScale方法更改可见图层的比例范围阈值。
具有基于图像的数据源的图层可以是图像调整图层(继承自ImageAdjustmentLayer),其允许在运行时调整图层的亮度,对比度和伽马值。基础数据源不会更改。

每个图层类都用作底图或操作图层。

……….


4、离线地图加载

4-1、tpk-离线图层包加载

手机中的离线地图碎片位置/data/data/com.cnbs.gisdemo/arcgis/GisTest.tpk

Android检查位置信息是否启用并请求打开 安卓查看位置信息_sdk_03

mMapView = (MapView) findViewById(R.id.mapView);
Utils utils = new Utils();
//tpk--缓存显示
TileCache tileCache = new TileCache(utils.Save_Path  + "/" +  utils.File_name);
ArcGISTiledLayer tiledLayer = new ArcGISTiledLayer(tileCache);
tiledLayer.setMinScale(8000);
tiledLayer.setMaxScale(1600);
Basemap basemap = new Basemap(tiledLayer);
ArcGISMap map = new ArcGISMap(basemap);
mMapView.setMap(map);

4-2、mmpk-离线地图包加载

private void loadMobileMapPackage(String mmpkFile){
        mapPackage = new MobileMapPackage(mmpkFile);
        mapPackage.loadAsync();
        mapPackage.addDoneLoadingListener(new Runnable() {
            @Override
            public void run() {
                if(mapPackage.getLoadStatus() == LoadStatus.LOADED && mapPackage.getMaps().size() > 0){
                    mMapView.setMap(mapPackage.getMaps().get(0));
                }else{
                    // Log an issue if the mobile map package fails to load
                }
            }
        });
    }

5、地图相关操作

5-0、 在地图点击的位置绘制图标

mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this, mMapView) {
            @Override
            public boolean  onSingleTapConfirmed(MotionEvent v) {
                android.graphics.Point screenPoint=new android.graphics.Point(Math.round(v.getX()), Math.round(v.getY()));
                Point clickPoint = mMapView.screenToLocation(screenPoint);
                //获取点击位置的经纬度
                String x = CoordinateFormatter.toLatitudeLongitude(clickPoint, CoordinateFormatter.LatitudeLongitudeFormat.DECIMAL_DEGREES, 4);
                GraphicsOverlay graphicsOverlay_1=new GraphicsOverlay();
                //加个点
//                SimpleMarkerSymbol pointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.DIAMOND, Color.RED, 10);
                //加个图标
                BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.ic_map_blue);
                PictureMarkerSymbol pointSymbol = new PictureMarkerSymbol(drawable);

                Graphic pointGraphic = new Graphic(clickPoint,pointSymbol);
                graphicsOverlay_1.getGraphics().add(pointGraphic);
                mMapView.getGraphicsOverlays().add(graphicsOverlay_1);
                return true;
            }
        });

点击事件对象MotionEvent

MotionEvent {
    action=ACTION_UP, 
    id[0]=0, 
    x[0]=455.5782, 
    y[0]=1095.3904, 
    toolType[0]=TOOL_TYPE_FINGER, 
    buttonState=0, 
    metaState=0, 
    flags=0x0, 
    edgeFlags=0x0, 
    pointerCount=1, 
    historySize=0, 
    eventTime=351739375, 
    downTime=351739296, 
    deviceId=5, 
    source=0x1002 }

5-1、 显示点击位置的经纬度

要显示某个点的经纬度,只需要使用这个方法即可

String x = CoordinateFormatter.toLatitudeLongitude(clickPoint, CoordinateFormatter.LatitudeLongitudeFormat.DECIMAL_DEGREES, 4);

返回字符串示例

38.0431N 114.5119E

5-2、 在地图上绘制点和线

  • 用直角坐标系画
private void addGraphicsOverlay() {
        // point graphic
        Point pointGeometry0 = new Point(12724154.362253, 3573937.672715, SpatialReferences.getWebMercator());
        Point pointGeometry1 = new Point(12724178.110558, 3573928.336934, SpatialReferences.getWebMercator());
        Point pointGeometry2 = new Point(12724205.001669, 3573832.994711, SpatialReferences.getWebMercator());
        // red diamond point symbol
//        SimpleMarkerSymbol pointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.DIAMOND, Color.RED, 10);
        BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.ic_map_blue);
        PictureMarkerSymbol pointSymbol = new PictureMarkerSymbol(drawable);
        // create graphic for point
        Graphic pointGraphic0 = new Graphic(pointGeometry0);
        Graphic pointGraphic1 = new Graphic(pointGeometry1);
        Graphic pointGraphic2 = new Graphic(pointGeometry2);
        // create a graphic overlay for the point
        GraphicsOverlay pointGraphicOverlay = new GraphicsOverlay();
        // create simple renderer
        SimpleRenderer pointRenderer = new SimpleRenderer(pointSymbol);
        pointGraphicOverlay.setRenderer(pointRenderer);
        // add graphic to overlay
        pointGraphicOverlay.getGraphics().add(pointGraphic0);
        pointGraphicOverlay.getGraphics().add(pointGraphic1);
        pointGraphicOverlay.getGraphics().add(pointGraphic2);
        // add graphics overlay to the MapView
        mMapView.getGraphicsOverlays().add(pointGraphicOverlay);

        // line graphic
        PolylineBuilder lineGeometry = new PolylineBuilder(SpatialReferences.getWebMercator());
        lineGeometry.addPoint(12724178.110558, 3573928.336934);
        lineGeometry.addPoint(12724205.001669, 3573832.994711);
        // solid blue line symbol
        SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, R.color.colorAccent, 2);
        // create graphic for polyline
        Graphic lineGraphic = new Graphic(lineGeometry.toGeometry());
        // create graphic overlay for polyline
        GraphicsOverlay lineGraphicOverlay = new GraphicsOverlay();
        // create simple renderer
        SimpleRenderer lineRenderer = new SimpleRenderer(lineSymbol);
        // add graphic to overlay
        lineGraphicOverlay.setRenderer(lineRenderer);
        // add graphic to overlay
        lineGraphicOverlay.getGraphics().add(lineGraphic);
        // add graphics overlay to the MapView
        mMapView.getGraphicsOverlays().add(lineGraphicOverlay);

    }
  • 用地理坐标系(经纬度)画
    和上面的是一样的,只是创建点的方式不一样
Point pointGeometry0 = CoordinateFormatter.fromLatitudeLongitude("30.5449N 114.3034E", null);
Point pointGeometry1 = CoordinateFormatter.fromLatitudeLongitude("30.5459N 114.3035E", null);
Point pointGeometry2 = CoordinateFormatter.fromLatitudeLongitude("30.5469N 114.3036E", null);
  • 画线就有点不同了
Point pointGeometry0 = CoordinateFormatter.fromLatitudeLongitude("30.5469N 114.3036E", null);
Point pointGeometry1 = CoordinateFormatter.fromLatitudeLongitude("30.5459N 114.3035E", null);
Point pointGeometry2 = CoordinateFormatter.fromLatitudeLongitude("30.5449N 114.3034E", null);
PointCollection borderCAtoNV = new PointCollection(SpatialReferences.getWgs84());
borderCAtoNV.add(pointGeometry0);
borderCAtoNV.add(pointGeometry1);
borderCAtoNV.add(pointGeometry2);
Polyline polyline = new Polyline(borderCAtoNV);

SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, R.color.colorAccent, 2);
SimpleRenderer lineRenderer = new SimpleRenderer(lineSymbol);
lineGraphic = new Graphic(polyline, lineSymbol);
lineGraphicOverlay = new GraphicsOverlay();
lineGraphicOverlay.setRenderer(lineRenderer);
lineGraphicOverlay.getGraphics().add(lineGraphic);
mMapView.getGraphicsOverlays().add(lineGraphicOverlay);

5-3、 选中的要素闪烁

handler.postDelayed(runnable, 1000);
private boolean isShow;
    private int recLen = 5;//设置有效时间
    Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            isShow = !isShow;
            recLen--;
            handler.postDelayed(runnable, 1000);
            ListenableList<GraphicsOverlay> list = mMapView.getGraphicsOverlays();
            GraphicsOverlay overlay = list.get(1);
            overlay.setSelectionColor(R.color.colorPrimary);
            overlay.setVisible(isShow);
            if (recLen <= 0) {
                recLen = 5;//重置
                Message message = new Message();
                message.what = 1;
                handlerStop.sendMessage(message);
            }
        }
};

    final Handler handlerStop = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    handler.removeCallbacks(runnable);
                    ListenableList<GraphicsOverlay> list = mMapView.getGraphicsOverlays();
                    GraphicsOverlay overlay = list.get(1);
                    overlay.setSelectionColor(R.color.colorAccent);
                    overlay.setVisible(true);
                    break;
            }
            super.handleMessage(msg);
        }

    };
@Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(runnable);
    }

5-4、 设置地图中心点

Point point1 = new Point(12724178.110558, 3573932.336934, SpatialReferences.getWebMercator());
        mMapView.setViewpointCenterAsync(point1,3800);

5-5、切换图层

在Runtime100里,MapView是通过ArcGISMap类来完成图层的管理。

首先是底图的加载。ArcGISMap类是将底图和业务图层分开的,对于底图,ArcGISMap里用了Baemap类来进行管理。
例子就是上面的4、离线地图加载

手机中的离线地图碎片位置`/data/data/com.cnbs.gisdemo/arcgis/GisTest.tpk`
mMapView = (MapView) findViewById(R.id.mapView);
Utils utils = new Utils();
//tpk--缓存显示
TileCache tileCache = new TileCache(utils.Save_Path  + "/" +  utils.File_name);
ArcGISTiledLayer tiledLayer = new ArcGISTiledLayer(tileCache);
tiledLayer.setMinScale(8000);
tiledLayer.setMaxScale(1600);
Basemap basemap = new Basemap(tiledLayer);
ArcGISMap map = new ArcGISMap(basemap);
mMapView.setMap(map);

我们要切换底图时候,仅需要给ArcGISMap类重新赋值一个底图即可。

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Basemap basemap = new Basemap(layer);
        map.setBasemap(basemap);
        mMapView.setMap(arcGISMap);
    }
});

5-6、地图要素点击事件

//添加点击事件
        MapViewTouchListener mMapViewTouchListener = new MapViewTouchListener(this, mMapView);
        mMapView.setOnTouchListener(mMapViewTouchListener);
        //添加覆盖物
        addGraphicsOverlay();
class MapViewTouchListener extends DefaultMapViewOnTouchListener {

        public MapViewTouchListener(Context context, MapView mapView) {
            super(context, mapView);
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            // get the screen point where user tapped
            android.graphics.Point screenPoint = new android.graphics.Point((int) e.getX(), (int) e.getY());
            final ListenableFuture<List<IdentifyGraphicsOverlayResult>> overlaysAsync = mMapView.identifyGraphicsOverlaysAsync(screenPoint, 10.0, false, 2);
            overlaysAsync.addDoneListener(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<IdentifyGraphicsOverlayResult> overlayResultList = overlaysAsync.get();
                        if (!overlayResultList.isEmpty() && overlayResultList.size() >= 0) {
                            //如果只取第一个点到的,就不用for循环了,直接get(0);如果需要取所有点击到的覆盖物,就可以用for循环
                               int size = overlayResultList.size();
                           if (size > 1) {
                                showTV.setText("你点到了 --" + size + "个元素");
                            } else {
                                showTV.setText("GISDemo");
                            }
//                            for (int i = 0; i < size; i++) {
                            List<Graphic> graphics = overlayResultList.get(0).getGraphics();
//                                List<Graphic> graphics = overlayResultList.get(i).getGraphics();
                            if (!graphics.isEmpty() && graphics.size() >= 0) {
                                Graphic graphic = graphics.get(0);//取点击的第一个
                                Map<String, Object> map = graphic.getAttributes();
                                String hint = (String) map.get("hint");
                                Toast.makeText(getApplicationContext(), "你点到了 - " + hint, Toast.LENGTH_SHORT).show();
                            }
                        }

                    } catch (InterruptedException | ExecutionException ie) {
                        ie.printStackTrace();
                    }
                }
            });
            return super.onSingleTapConfirmed(e);
        }

    }

5-7、切换图层时,地图保持原位置

加上点击事件限制是为了防止用户暴力点击,因为图层加载需要时间;方法是获取地图当前中心点和缩放比,切换底图后重新赋值

private long clickTime = 0;
...
 case R.id.layer_change:     //切换图层需要加载地图,不让用户切换的太快,防止暴力点击
                if (System.currentTimeMillis() - clickTime > 1000) {
                    clickTime = System.currentTimeMillis();
                    //计算中心点
                    int measuredWidth = mMapView.getMeasuredWidth();
                    int measuredHeight = mMapView.getMeasuredHeight();
                    android.graphics.Point point = new android.graphics.Point(measuredWidth / 2, measuredHeight / 2);
                    Point centerDot = mMapView.screenToLocation(point);
                    double mapScale = mMapView.getMapScale();
                    //我们要切换底图时候,仅需要给ArcGISMap类重新赋值一个底图即可。
                    isStreetMap = !isStreetMap;
                    if (isStreetMap) {
                        String streetUrl = httpMethods.getMap_Street_URL();
                        loadBasemap(streetUrl);
                    } else {
                        String satelliteUrl = httpMethods.getMap_Satellite_URL();
                        loadBasemap(satelliteUrl);
                    }
                    if (centerDot == null)
                        centerDot = CoordinateFormatter.fromLatitudeLongitude(center, SpatialReferences.getWgs84());
                    mMapView.setViewpointCenterAsync(centerDot, mapScale);
                }
                break;
//加载地图,提成方法,方便切换底图
    private void loadBasemap(String url) {
        //地图服务端地址
        ArcGISTiledLayer tiledLayer = new ArcGISTiledLayer(url);
        tiledLayer.setMinScale(MinScale);  //控制缩小,数值越大,缩小倍数越大,看的范围越广
        tiledLayer.setMaxScale(MaxScale);   //控制放大,数值越小,放大倍数越高
        Basemap basemap = new Basemap(tiledLayer);
        mArcGISMap = new ArcGISMap(basemap);
        mMapView.setMap(mArcGISMap);
    }

6、完整代码

写完不放完整代码,总感觉不太好。O(∩_∩)O哈哈哈~

public class MainActivity extends BaseActivity {
    @BindView(R.id.header_title)
    TextView headerTitle;
    @BindView(R.id.header_right_img)
    ImageView headerRightImg;
    @BindView(R.id.header_left_img)
    ImageView headerLeftImg;
    @BindView(mapView)
    MapView mMapView;
    @BindView(R.id.activity_main)
    RelativeLayout activityMain;
    private GraphicsOverlay pointGraphicOverlay;
    private GraphicsOverlay lineGraphicOverlay;

    private static double MinScale = 5000000;
    private static double MaxScale = 1000;
    private double CurrentScale = 9000;
    private double ScaleChange = 1000;        //每次放大或缩小的尺寸
    private ArcGISMap mArcGISMap;
    private String userId;
    private List<MapTaskPointBean> mapTaskPoints;
    private String center = "38.048,114.507250";  //石家庄中心
    private MyApplication instance;
    private boolean isStreetMap = true;
    private boolean isOffline;
    private String SDPath = Environment.getExternalStorageDirectory() + "/CableInspection/";
    private LocationManager locationManager;
    private String provider;
    private final String MAX = "+";
    private final String MIN = "-";
    private Location mLocation;
    private int taskType = -1;
    private int taskId = -1;
    private int objectTypeId = -1;
    private String taskName = "";
    private GraphicsOverlay overlayDevice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        instance = MyApplication.getInstance();
        UserBean userBean = instance.getUserInfo();
        userId = userBean == null ? "-1" : userBean.getUserId();
        isStreetMap = instance.getMapType();
        initArcGis();
        initView();
        hasCurrentLocation();//打开地图时默认定位到当前位置
        initMap();
    }

    //可见的时候,地图上的标识物要绘制
    @Override
    protected void onResume() {
        super.onResume();
        mMapView.resume();
        mMapView.getGraphicsOverlays().clear();
        if (overlayDevice != null) mMapView.getGraphicsOverlays().add(overlayDevice);
        if (mapTaskPoints != null) mapTaskPoints.clear();
        initData();
        //添加覆盖物
        addGraphicsOverlay();
    }

    //获取ArcGis的许可
    private void initArcGis() {
        ArcGISRuntimeEnvironment.setLicense("runtimelite,1000,rud4163659509,none,1JPJD4SZ8L4HC2EN0229");
    }

    private void initView() {
        headerTitle.setText(R.string.app_name_all);
        headerLeftImg.setVisibility(View.VISIBLE);
        headerRightImg.setVisibility(View.VISIBLE);
        headerLeftImg.setImageResource(R.mipmap.user_mag);
    }

    //在地图上显示当前任务的位置
    private void initData() {
        List<TaskList> taskLists = DataSupport.where("user_id =? and task_status =?",
                userId + "", MConstant.Task_Current + "").find(TaskList.class);
        if (taskLists != null && taskLists.size() > 0) {
            TaskList list = taskLists.get(0);
            int taskType = list.getTask_type();
            int taskId = list.getTask_id();
            //正序排序,以保证通道、本体在其他任务点的前面获取
            List<TaskPoint> taskPoints = DataSupport
                    .where("user_id =? and task_id =? and task_type =?", userId + "", taskId + "", taskType + "")
                    .find(TaskPoint.class);
            if (taskPoints == null || taskPoints.size() <= 0) return;
            //任务点集合
            mapTaskPoints = new ArrayList<>();
            for (int i = 0; i < taskPoints.size(); i++) {
                TaskPoint taskPoint = taskPoints.get(i);
                int taskPointId = taskPoint.getTask_point_id();
                int typeId = taskPoint.getObject_type_id();
                String location = taskPoint.getTask_point_location();
                if (TextUtils.isEmpty(location)) return;
                String[] split = location.split(";");
                if (i == 0) center = split[0].replace(",", " ");  //用第一个任务点作为当前地图的中心点
                switch (typeId) {
                    case MConstant.ObjType_td:  //通道----管网
                        if (split.length > 1) {
                            List<Point> line = new ArrayList<>();
                            MapTaskPointBean mapTaskPointBean = new MapTaskPointBean();
                            for (int j = 0; j < split.length; j++) {
                                String dot = split[j];
                                String replace = dot.replace(",", " ");
                                Point lineDot = CoordinateFormatter.fromLatitudeLongitude(replace, SpatialReferences.getWgs84());
                                line.add(lineDot);
                            }
                            mapTaskPointBean.setTaskType(taskType);
                            mapTaskPointBean.setTaskId(taskId);
                            mapTaskPointBean.setTaskPointId(taskPointId);
                            mapTaskPointBean.setDot(false);//线
                            mapTaskPointBean.setLine(line);
                            mapTaskPoints.add(mapTaskPointBean);
                        }
                        break;
                    case MConstant.ObjType_gj:   //工井
                    case MConstant.ObjType_fhq:   //防火墙
                    case MConstant.ObjType_mhzz:   //灭火装置
                        if (split.length > 0) {
                            MapTaskPointBean mapTaskPointBean = new MapTaskPointBean();
                            String dot = split[0];
                            String replace = dot.replace(",", " ");
                            if (i == taskPoints.size() / 2) {
                                center = replace;
                            }
                            Point point = CoordinateFormatter.fromLatitudeLongitude(replace, SpatialReferences.getWgs84());
                            mapTaskPointBean.setTaskType(taskType);
                            mapTaskPointBean.setTaskId(taskId);
                            mapTaskPointBean.setTaskPointId(taskPointId);
                            mapTaskPointBean.setDot(true);//点
                            mapTaskPointBean.setPoint(point);
                            mapTaskPoints.add(mapTaskPointBean);
                        }
                        break;
                    case MConstant.ObjType_bt:   //本体
                        break;
                }
            }
        }
    }

    private long clickTime = 0;

    @OnClick({R.id.header_left_img, R.id.header_right_img, R.id.map_xj_tv, R.id.map_xq_tv,
            R.id.layer_change, R.id.map_location, R.id.scale_max, R.id.scale_min})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.header_left_img:
                startActivity(new Intent(MainActivity.this, UserActivity.class));
                break;
            case R.id.header_right_img:
                startActivity(new Intent(MainActivity.this, SettingActivity.class));
                break;
            case R.id.map_xj_tv:
                Intent intentTaskXJList = new Intent(MainActivity.this, TaskListActivity.class);
                intentTaskXJList.putExtra(MConstant.taskList, MConstant.xjTask);
                startActivity(intentTaskXJList);
                break;
            case R.id.map_xq_tv:
                Intent intentTaskXQList = new Intent(MainActivity.this, TaskListActivity.class);
                intentTaskXQList.putExtra(MConstant.taskList, MConstant.xqTask);
                startActivity(intentTaskXQList);
                break;
            case R.id.layer_change:     //切换图层需要加载地图,不让用户切换的太快,防止暴力点击
                if (!MyUtils.isNetWorkConnected(this)) {
                    new CenterHintToast(MainActivity.this, "你当前处于离线状态,不能切换图层");
                    return;
                }
                if (System.currentTimeMillis() - clickTime > 1000) {
                    clickTime = System.currentTimeMillis();
                    //计算中心点
                    int measuredWidth = mMapView.getMeasuredWidth();
                    int measuredHeight = mMapView.getMeasuredHeight();
                    android.graphics.Point point = new android.graphics.Point(measuredWidth / 2, measuredHeight / 2);
                    Point centerDot = mMapView.screenToLocation(point);
                    double mapScale = mMapView.getMapScale();
                    double rotation = mMapView.getMapRotation();
                    //重新赋值-loadBasemap方法中使用
                    if (centerDot != null){
                        center = CoordinateFormatter.toLatitudeLongitude(centerDot, CoordinateFormatter.LatitudeLongitudeFormat.DECIMAL_DEGREES, 4);
                    }
                    CurrentScale = mapScale;
                    //我们要切换底图时候,仅需要给ArcGISMap类重新赋值一个底图即可。
                    isStreetMap = !isStreetMap;
                    if (isStreetMap) {
                        String streetUrl = instance.getMapStreetURL();
                        loadBasemap(streetUrl);
                    } else {
                        String satelliteUrl = instance.getMapSatelliteURL();
                        loadBasemap(satelliteUrl);
                    }
                    mMapView.setViewpointRotationAsync(rotation);
                }
                break;
            case R.id.map_location:
                if (isOffline) return;
                hasCurrentLocation();
                break;
            case R.id.scale_max:  // + 运算
                changeScale(MAX);
                break;
            case R.id.scale_min: // - 运算
                changeScale(MIN);
                break;
        }
    }

    //改变地图缩放比例--按图层
    private void changeScale(String type) {
        TileInfoBean bean = instance.getTileInfoBean();
        if (bean == null) {
            new CenterHintToast(MainActivity.this, "获取缩放比例失败");
            return;
        }
        List<TileInfoBean.LodsBean> lods = bean.getLods();
        if (lods == null || lods.size() <= 0) return;
        double mapScale = mMapView.getMapScale();
        double fastScale = lods.get(0).getScale();
        double lastScale = lods.get(lods.size() - 1).getScale();
        if (mapScale>=fastScale&&MIN.equals(type))return;
        if (mapScale<=lastScale&&MAX.equals(type))return;
        for (int i = 0; i < lods.size() - 1; i++) {
            if (MAX.equals(type)) {  // + 运算(放大地图、显示区域变小)
                if (mapScale <= lods.get(i).getScale() && mapScale > lods.get(i+1).getScale()) {
                    CurrentScale = lods.get(i+1).getScale();
                }
            } else if (MIN.equals(type)) {  // - 运算(缩小地图、显示区域变大)
                if (mapScale < lods.get(i).getScale() && mapScale >= lods.get(i + 1).getScale()) {
                    CurrentScale = lods.get(i).getScale();
                }
            }
        }
        mMapView.setViewpointScaleAsync(CurrentScale);
    }

    //获取定位的权限
    private void hasCurrentLocation() {
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        //获取可用的位置提供器
        List<String> allProviders = locationManager.getAllProviders();
        if (allProviders.contains(LocationManager.GPS_PROVIDER)) {
            provider = LocationManager.GPS_PROVIDER;
        } else if (allProviders.contains(LocationManager.NETWORK_PROVIDER)) {
            provider = LocationManager.NETWORK_PROVIDER;
            new CenterHintToast(MainActivity.this, "为了定位的准确性\n请开启GPS定位服务");
        } else {
            new CenterHintToast(MainActivity.this, "请在 设置 中开启定位服务");
            return;
        }
        //权限检测
        String[] permissionStr = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, permissionStr, 111);
        } else {
            showLocation();
        }
    }

    //定位到当前点
    @SuppressLint("MissingPermission")
    private void showLocation() {
        mLocation = locationManager.getLastKnownLocation(provider);
        locationManager.requestLocationUpdates(provider, 1000, 0, new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                mLocation = location;
                if (mLocation != null) {
                    drawLocationImg();
                    center = mLocation.getLatitude() + " " + mLocation.getLongitude();
                }
            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {

            }

            @Override
            public void onProviderEnabled(String provider) {

            }

            @Override
            public void onProviderDisabled(String provider) {
                new CenterHintToast(MainActivity.this, "请在 设置 中开启GPS定位服务");
            }
        });
        if (mLocation != null) {
            drawLocationImg();
            center = mLocation.getLatitude() + " " + mLocation.getLongitude();
        }
        //设置地图中心点
        Point point = CoordinateFormatter.fromLatitudeLongitude(center, SpatialReferences.getWgs84());
        mMapView.setViewpointCenterAsync(point, CurrentScale);
    }

    //绘制当前点的图标
    private void drawLocationImg() {
        String pointStr = mLocation.getLatitude() + " " + mLocation.getLongitude();
        Point point = CoordinateFormatter.fromLatitudeLongitude(pointStr, SpatialReferences.getWgs84());
        Graphic graphic = new Graphic(point);
        if (overlayDevice == null) {     //显示自己当前位置
            overlayDevice = new GraphicsOverlay();
            BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.device_location);
            final PictureMarkerSymbol pointSymbol = new PictureMarkerSymbol(drawable);
            SimpleRenderer pointRenderer = new SimpleRenderer(pointSymbol);
            overlayDevice.setRenderer(pointRenderer);
            overlayDevice.getGraphics().add(graphic);
            mMapView.getGraphicsOverlays().add(overlayDevice);
        } else {
            overlayDevice.getGraphics().remove(0);
            overlayDevice.getGraphics().add(graphic);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 111:
                if (grantResults.length > 0) {   //有权限被拒绝
                    List<String> deniedPermissions = new ArrayList<>();
                    for (int i = 0; i < grantResults.length; i++) {
                        int grantResult = grantResults[i];
                        String permission = permissions[i];
                        if (grantResult != PackageManager.PERMISSION_GRANTED) {
                            deniedPermissions.add(permission);
                        }
                    }
                    if (deniedPermissions.isEmpty()) {   //全部授权成功
                        showLocation();
                    } else {
                        new CenterHintToast(MainActivity.this, "请在 设置-应用管理 中开启此应用的定位权限。");
                    }
                }
                break;
            default:
                break;
        }
    }

    //加载地图,提成方法,方便切换底图,异步执行
    private void loadBasemap(final String url) {
   /*     //如果用户处于离线状态,不管之前的地图状态直接加载离线包
        if (isOffline) {
            String filename = MyUtils.Save_Path + "/" + MyUtils.File_name;
            if (!(new File(filename)).exists()) return;//防止闪退
            DBTiledLayer tiledLayer = DBTiledLayer.init(filename);
            tiledLayer.setMinScale(MinScale);  //控制缩小,数值越大,缩小倍数越大,看的范围越广
            tiledLayer.setMaxScale(MaxScale);   //控制放大,数值越小,放大倍数越高
            Basemap basemap = new Basemap(tiledLayer);
            mArcGISMap = new ArcGISMap(basemap);
        }*/
        new Thread(new Runnable() {
            @Override
            public void run() {
                new ReturnJson(url,mHandler);    //存地图缩放比例
            }
        }).start();
    }

    //--获取地图配置信息成功
    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 13:
                    Bundle data = msg.getData();
                    String url = data.getString("url");
                    MinScale = data.getDouble("fastScale");
                    MaxScale = data.getDouble("lastScale");
                    ArcGISTiledLayer tiledLayer = new ArcGISTiledLayer(url);
                    tiledLayer.setMinScale(MinScale);  //控制缩小,数值越大,缩小倍数越大,看的范围越广
                    tiledLayer.setMaxScale(MaxScale);   //控制放大,数值越小,放大倍数越高
                    Basemap basemap = new Basemap(tiledLayer);
                    mArcGISMap = new ArcGISMap(basemap);
                    mMapView.setMap(mArcGISMap);
                    Point point = CoordinateFormatter.fromLatitudeLongitude(center, SpatialReferences.getWgs84());
                    mMapView.setViewpointCenterAsync(point, CurrentScale);
                    break;
            }
        }
    };

    private void initMap() {
        //加载地图底图
        if (isStreetMap) {
            String streetUrl = instance.getMapStreetURL();
            loadBasemap(streetUrl);
        } else {
            String satelliteUrl = instance.getMapSatelliteURL();
            loadBasemap(satelliteUrl);
        }
        //添加点击事件
        MapViewTouchListener mMapViewTouchListener = new MapViewTouchListener(this, mMapView);
        mMapView.setOnTouchListener(mMapViewTouchListener);
    }

    //点击地图覆盖物
    class MapViewTouchListener extends DefaultMapViewOnTouchListener {

        public MapViewTouchListener(Context context, MapView mapView) {
            super(context, mapView);
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            // get the screen point where user tapped
            android.graphics.Point screenPoint = new android.graphics.Point((int) e.getX(), (int) e.getY());
            final ListenableFuture<List<IdentifyGraphicsOverlayResult>> overlaysAsync = mMapView.identifyGraphicsOverlaysAsync(screenPoint, 20.0, false, 2);
            overlaysAsync.addDoneListener(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<IdentifyGraphicsOverlayResult> overlayResultList = overlaysAsync.get();
                        if (!overlayResultList.isEmpty() && overlayResultList.size() >= 0) {
                            //如果只取第一个点到的,就不用for循环了,直接get(0);如果需要取所有点击到的覆盖物,就可以用for循环
                            int size = overlayResultList.size();
                            List<Graphic> graphics = overlayResultList.get(0).getGraphics();
                            if (!graphics.isEmpty() && graphics.size() >= 0) {
                                Graphic graphic = graphics.get(0);//取点击的第一个
                                Map<String, Object> map = graphic.getAttributes();
                                int taskPointId = (int) map.get("taskPointId");
                                showPopupWindow(taskPointId);
                            }
                        }
                    } catch (InterruptedException | ExecutionException ie) {
                        ie.printStackTrace();
                    }
                }
            });
            return super.onSingleTapConfirmed(e);
        }
    }

    public void showPopupWindow(final int taskPointId) {
        View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.map_task_info_pop, null);
        final PopupWindow popupWindow = new PopupWindow(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        popupWindow.setTouchable(true);
        popupWindow.setAnimationStyle(R.style.popup_style);
        popupWindow.setBackgroundDrawable(getResources().getDrawable(R.drawable.shape_main_map_pop));
        popupWindow.showAsDropDown(headerTitle);
        TextView taskPointName = (TextView) view.findViewById(R.id.taskPoint_name);
        TextView taskPointLocation = (TextView) view.findViewById(R.id.taskPoint_location);
        TextView taskPointLeft = (TextView) view.findViewById(R.id.taskPoint_left);
        TextView taskPointRight = (TextView) view.findViewById(R.id.taskPoint_right);
        //取任务点的相关信息
        List<TaskList> taskLists = DataSupport.where("user_id =? and task_status =?",
                userId + "", MConstant.Task_Current + "").find(TaskList.class);
        if (taskLists != null && taskLists.size() > 0) {
            TaskList list = taskLists.get(0);
            taskType = list.getTask_type();
            taskId = list.getTask_id();
            taskName = list.getTask_name();
            List<TaskPoint> taskPoints = DataSupport
                    .where("user_id =? and task_id =? and task_type =? and task_point_id =?",
                            userId + "", taskId + "", taskType + "", taskPointId + "")
                    .find(TaskPoint.class);
            if (taskPoints != null && taskPoints.size() > 0) {
                TaskPoint point = taskPoints.get(0);
                objectTypeId = point.getObject_type_id();
                List<ObjectAttribute> attributes = DataSupport.where("user_id=? and task_type=? and task_id=? and task_point_id=?",
                        userId, taskType + "", taskId + "", taskPointId + "").find(ObjectAttribute.class);
                if (attributes != null && attributes.size() > 0) {
                    ObjectAttribute attribute = attributes.get(0);
                    String att_info2 = attribute.getAtt_info2();
                    String[] split = att_info2.split(":");
                    String att_info1 = attribute.getAtt_info1();
                    String[] split1 = att_info1.split(":");
                    if (split.length > 1 && split1.length > 1) {
                        taskPointName.setText(split[1] + ": " + split1[1]);
                    }
                    taskPointLocation.setText(attribute.getAtt_info11());
                }
            }
        }
        taskPointLeft.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //任务详情
                Intent intent = new Intent(MainActivity.this, TaskInfoActivity.class);
                intent.putExtra("taskName", taskName);
                intent.putExtra("taskType", taskType);
                intent.putExtra("taskId", taskId);
                startActivity(intent);
            }
        });
        taskPointRight.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //缺陷录入
                Intent intent = new Intent(MainActivity.this, TaskRecordActivity.class);
                intent.putExtra("objTypeId", objectTypeId);
                intent.putExtra("taskPointId", taskPointId);
                intent.putExtra("taskType", taskType);
                intent.putExtra("taskId", taskId);
                intent.putExtra("taskStatus", MConstant.Task_Current);
                startActivity(intent);
            }
        });
    }

    //添加点线覆盖物
    private void addGraphicsOverlay() {
        if (mLocation != null) drawLocationImg();
        //-----------画点-------------
        pointGraphicOverlay = new GraphicsOverlay();
        BitmapDrawable drawable0 = (BitmapDrawable) getResources().getDrawable(R.drawable.dot_map);
        final PictureMarkerSymbol pointSymbol0 = new PictureMarkerSymbol(drawable0);
        SimpleRenderer pointRenderer = new SimpleRenderer(pointSymbol0);
        pointGraphicOverlay.setRenderer(pointRenderer);
        //--------------画线---------------
        lineGraphicOverlay = new GraphicsOverlay();
        SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, R.color.colorPrimary, 3);
        SimpleRenderer lineRenderer = new SimpleRenderer(lineSymbol);
        lineGraphicOverlay.setRenderer(lineRenderer);
        //添加需要绘制的数据
        if (mapTaskPoints == null || mapTaskPoints.size() <= 0) return;
        for (MapTaskPointBean mapTaskPoint : mapTaskPoints) {
            boolean dot = mapTaskPoint.isDot();
            if (dot) {//画点
                Map<String, Object> map = new HashMap<>();
                map.put("taskPointId", mapTaskPoint.getTaskPointId());
                Point point = mapTaskPoint.getPoint();
                Graphic graphic = new Graphic(point, map, pointSymbol0);
                pointGraphicOverlay.getGraphics().add(graphic);
            } else {//画线
                PointCollection pointCollection = new PointCollection(SpatialReferences.getWgs84());
                Map<String, Object> map = new HashMap<>();
                map.put("taskPointId", mapTaskPoint.getTaskPointId());
                List<Point> line = mapTaskPoint.getLine();
                for (Point point : line) {
                    pointCollection.add(point);
                }
                Polyline polyline = new Polyline(pointCollection);
                Graphic graphic = new Graphic(polyline, map, lineSymbol);
                lineGraphicOverlay.getGraphics().add(graphic);
            }
        }

        // add graphics overlay to the MapView
        mMapView.getGraphicsOverlays().add(lineGraphicOverlay);
        mMapView.getGraphicsOverlays().add(pointGraphicOverlay);
        //设置地图中心点
        Point point = CoordinateFormatter.fromLatitudeLongitude(center, SpatialReferences.getWgs84());
        mMapView.setViewpointCenterAsync(point, CurrentScale);
        //------------------闪烁---------------------
        handler.postDelayed(runnable, 1000);
    }

    private boolean isShow = true;
    private int recLen = 4;//设置闪烁次数
    Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            isShow = !isShow;
            recLen--;
            handler.postDelayed(runnable, 1000);
            //显示或消失
            if (lineGraphicOverlay != null) lineGraphicOverlay.setVisible(isShow);
            if (pointGraphicOverlay != null) pointGraphicOverlay.setVisible(isShow);
            if (recLen <= 0) {
                recLen = 4;//重置时要和初始值一致
                Message message = new Message();
                message.what = 1;
                handlerStop.sendMessage(message);
            }
        }
    };

    final Handler handlerStop = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    handler.removeCallbacks(runnable);
                    if (lineGraphicOverlay != null) lineGraphicOverlay.setVisible(true);
                    if (pointGraphicOverlay != null) pointGraphicOverlay.setVisible(true);
                    break;
            }
            super.handleMessage(msg);
        }

    };

    @Override
    protected void onPause() {
        mMapView.pause();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(runnable);
    }

    /**
     * 连续点击两次退出应用,时间间隔不超过5s
     */
    private long exitTime = 0;

    @Override
    public void onBackPressed() {
        if (System.currentTimeMillis() - exitTime > 2000) {
            Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
            exitTime = System.currentTimeMillis();
        } else {
            instance.setMapType(isStreetMap);
            ActivityCollector.finishAll();
        }
    }

}

8、补充

关于GIS地图在android上的使用也可以看这个作者的系列博文,安卓智能地图开发与实施系列,作者:大虾卢
[ArcGIS for Android Runtime 100基础操作]()