Arcgis runtime for android mmpk加载图层组实现上下移动切换

Posted lwx2233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Arcgis runtime for android mmpk加载图层组实现上下移动切换相关的知识,希望对你有一定的参考价值。

目录

前言

一、mmpk

二、加载mmpk并生成图层树

1.加载mmpk数据

2.生成图层树

三、identify功能

四、图层上下移动

前言

最近单位在开发一个app,利用的是arcgis runtime for android去制作地图模块,它需要加载离线地图,并对每个图层进行identify操作,同时还要有一个图层操作的图层树(参考arcgis的layer),开发工具是android studio。


一、mmpk

mmpk是使用arcgis pro之中的一种切片格式,不同于tpk与vtpk,mmpk中保留了图层的属性,从代码层面来说,可以通过featurelayer的方式来对其中的图层进行读取。

二、加载mmpk并生成图层树

1.加载mmpk数据

arcgis runtime for android有专门的方法对mmpk数据进行读取,代码直接复制即可,android studio会自动检测错误并让你alt + enter修改(这就是使用android studio的好处)。

注意,这里新建了一个图层组(grouplayer)去对mmpk中的图层进行存放!

String mmpkPath = getExternalFilesDir(null) + "/xxx.mmpk";            
MobileMapPackage mobileMapPackage = new MobileMapPackage(mmpkPath);
GroupLayer projectAreaGroupLayer = new GroupLayer();
projectAreaGroupLayer.setName("业务图层");
mobileMapPackage.loadAsync();

2.生成图层树

首先,需要使用mmpk加载完毕后的监听事件将图层逐个读取并存放到数组中,代码如下:

//layerName的集合
public static List LayerName = new ArrayList<>();
mobileMapPackage.addDoneLoadingListener(new Runnable() 
        @Override
        public void run() 
            LoadStatus mainLoadStasus = mobileMapPackage.getLoadStatus();
            if (mainLoadStasus == LoadStatus.LOADED) 
                List<ArcGISMap> mainArcGISMapL = mobileMapPackage.getMaps();
                ArcGISMap mainArcGISMapMMPK = mainArcGISMapL.get(0);
                LayerList mainMMPKLL = mainArcGISMapMMPK.getOperationalLayers();
                int h = mainMMPKLL.toArray().length;
                for (int i = 0; i < h; i++) 
                    FeatureLayer mainFeatureLayer = (FeatureLayer) mainMMPKLL.get(0);
                    Log.d("mmpk当前加载的操作图层的名字", mainFeatureLayer.getName());
                    //将图层的名称存入数组中
                    LayerName.add(mainFeatureLayer.getName());
                    mainArcGISMapMMPK.getOperationalLayers().remove(0);
                    //往grouplayer中加载featurelayer
                    projectAreaGroupLayer.getLayers().add(mainFeatureLayer);
                
                // add the group layer and other layers to the scene as operational layers
                mainArcGISMap.getOperationalLayers().addAll(Arrays.asList(projectAreaGroupLayer));
                Log.d("图层组的长度", String.valueOf(projectAreaGroupLayer.getLayers().toArray().length));
                // zoom to the extent of the group layer when the child layers are loaded
                ListenableList<Layer> layers = projectAreaGroupLayer.getLayers();
                for (Layer childLayer : layers) 
                    childLayer.addDoneLoadingListener(() -> 
                        if (childLayer.getLoadStatus() == LoadStatus.LOADED) 
                            mapView.setViewpoint(new Viewpoint(projectAreaGroupLayer.getFullExtent()));
                        
                    );
                
                setupRecyclerView(mainArcGISMap.getOperationalLayers());
             else 
                Exception e = mobileMapPackage.getLoadError();
                e.printStackTrace();
                Toast.makeText(MainActivity.this, "加载失败!", Toast.LENGTH_LONG).show();
            
        
    );

这里的layername是用来保存以后图层上下移动的参数,直接循环写入即可。

而setuprecyclerView是另一个方法,主要是用来调用官方网站的recyclerview的,具体如何构建这个官网上就有代码,方法的代码如下:

private void setupRecyclerView(List<Layer> layers) 
        mLayersAdapter = new LayersAdapter(this,this,MainActivity.this);
        mLayersRecyclerView.setAdapter(mLayersAdapter);

        for (Layer layer : layers) 
            // if layer can be shown in legend
            if (layer.canShowInLegend()) 
                layer.addDoneLoadingListener(() -> mLayersAdapter.addLayer(layer));
                layer.loadAsync();
            
        
    

这里的mLayerAdapter是读取了LayerAdapter这个类,参照官网的recycleview例子,mLayerRecycleView则是用findViewId的方法读取了activity.xml中的layersRecyclerView控件,之后在某一个按钮处写了展示RecyclerView,代码如下:

//获取图层管理按钮
            ImageButton layersButton = (ImageButton) findViewById(R.id.layersButton);
            //添加图层管理按钮的点击事件
            layersButton.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View view) 
                    if (mBottomSheetBehavior.getState() != BottomSheetBehavior.STATE_EXPANDED) 
                        // show bottom sheet
                        mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                     else 
                        // hide bottom sheet
                        mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
                    
                
            );

mBottomSheetBehavior便是从下面弹出的图层树,读取了BottomSheetBehavior这个类(在官网有)。

mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);

最终效果如下:

三、identify功能

这个最简单了其实,只需要在mapview的点击事件中添加identify的功能即可,代码如下:

/点击地图时,判断下拉框是否存在,存在则关闭,不存在则identify
            mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this, mapView) 
                @Override
                public boolean onSingleTapConfirmed(MotionEvent v) 
                    if (mBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) 
                        mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
                     else 
                        android.graphics.Point screenPoint = new android.graphics.Point(Math.round(v.getX()), Math.round(v.getY()));
                        final Point clickPoint = mapView.screenToLocation(screenPoint);

                        //FeatureLayer从GroupLayer中获取,仅获取了一个图层,这里可能需要做图层筛选。
                        //由于上下移动的原因,图层的layer_index会变化,而这里没有变,所以需要重新筛选与控制
                        for(int i = 0;i<LayerName.size();i++)
                        
                            if (Objects.equals(LayerName.get(i),current_layer_name))
                            
                                current_layer_index = i;
                            
                        

                        //通过get后面的数字控制
                        FeatureLayer featureLayer = (FeatureLayer) projectAreaGroupLayer.getLayers().get(current_layer_index);
//                      FeatureLayer featureLayer=(FeatureLayer) mMapView.getMap().getOperationalLayers().get(0);
                        FeatureTable mTable = featureLayer.getFeatureTable();//得到查询属性表
                        int tolerance = 10;
                        double mapTolerance = tolerance * mapView.getUnitsPerDensityIndependentPixel();
                        Envelope envelope = new Envelope(clickPoint.getX() - mapTolerance, clickPoint.getY() - mapTolerance,
                                clickPoint.getX() + mapTolerance, clickPoint.getY() + mapTolerance, mapView.getSpatialReference());
                        QueryParameters query = new QueryParameters();
                        query.setGeometry(envelope);// 设置查询范围
                        final ListenableFuture<FeatureQueryResult> featureQueryResultFuture = featureLayer.selectFeaturesAsync(query, FeatureLayer.SelectionMode.NEW);
                        mCallout = mapView.getCallout();
                        featureQueryResultFuture.addDoneListener(new Runnable() 
                            @Override
                            public void run() 
                                try 
                                    if (mCallout.isShowing())
                                    
                                        mCallout.dismiss();
                                    
                                    FeatureQueryResult featureQueryResult2 = featureQueryResultFuture.get();
                                    // create a textview to display field values
                                    TextView calloutContent = new TextView(getApplicationContext());
                                    calloutContent.setTextColor(Color.BLACK);
                                    calloutContent.setSingleLine(false);
                                    calloutContent.setVerticalScrollBarEnabled(true);
                                    calloutContent.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
                                    calloutContent.setMovementMethod(new ScrollingMovementMethod());
                                    calloutContent.setLines(5);
                                    Iterator<Feature> iterator = featureQueryResult2.iterator();

                                    while (iterator.hasNext()) 
                                        feature = iterator.next();
                                        // create a Map of all available attributes as name value pairs
                                        Map<String, Object> attr = feature.getAttributes();
                                        Set<String> keys = attr.keySet();
                                        calloutContent.append(key + ":" + value.toString() + "\\n");
                                        // center the mapview on selected feature
                                        Envelope envelope = feature.getGeometry().getExtent();
                                        mapView.setViewpointGeometryAsync(envelope, 200);
                                        // show callout
                                        mCallout.setLocation(envelope.getCenter());
                                        mCallout.setContent(calloutContent);
                                        mCallout.show();
                                    
                                 catch (Exception e1) 
                                    String error = "Select feature failed: " + e1.getMessage();
                                    Toast.makeText(MainActivity.this, error, Toast.LENGTH_LONG).show();
                                
                            
                        );

                        
                   
                    return true;
                
            );

这一段可能会少,粘的时候注意一下。

这里的current_layer_index是服务于图层上下移动的,记得先声明,它是通过读取LayerName这个集合,来判断图层的层级。

四、图层上下移动

思路是这样子的,先记录当前选中的图层layer的图层顺序x,然后将他删除,再次将他加载到x+1/x-1的图层顺序中,并调整Layername集合,保证每次移动图层时,layename与图层的layerlist相对应,从而实现了图层的上下移动。难点是需要理解grouplayer与layer的关系。

插句题外话,其实arcgis for js是有reorderlayer这个参数可以去调整图层的顺序,但是很遗憾runtime for android我没找到,所以只能用这种曲线救国的方式实现。

//声明实时更新layerName
        List upDown_layer_name = MainActivity.LayerName;
        //声明图层总数的下标
        int layer_total_index = MainActivity.LayerName.size();
        //图层上移,已完成
        ivMoveUp.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                //声明判断图层是否为最高层级的参数,0代表否,1代表是
                int is_highest_layer = 0;
                //获取选中的图层的名称
                Log.d("点击的itemView的名字", layer.getName());
                //根据图层名称获取图层的层级(即加载顺序)
                String MoveLayerName = layer.getName();
                //获取该图层的下标
                int layerIndex = 9999;
                int layerIndexNext = 9999;
                int LayerNameLength = layer_total_index;
                System.out.println(LayerNameLength);
                for (int i = 0; i < layer_total_index; i++) 
                    //判断图层名与其中是否存在相同,相同则记录数组下标
                    if (Objects.equals(upDown_layer_name.get(i), MoveLayerName)) 
                        if (i < layer_total_index - 1) 
                            layerIndex = i;
                            layerIndexNext = i + 1;
                            is_highest_layer = 0;
                        
                        else
                        
                            is_highest_layer = 1;
                            Toast.makeText(activity2, "该图层的层级已经是最低级别了!", Toast.LENGTH_LONG).show();
                        
                    
                
                System.out.println(layerIndex);
                System.out.println(layerIndexNext);

                if (is_highest_layer == 0)
                
                    //这里对其进行修改
                    MapView mapView = activity2.findViewById(R.id.mapView);
                    GroupLayer groupLayer = (GroupLayer)mapView.getMap().getOperationalLayers().get(0);
                    //删除选中的图层
                    groupLayer.getLayers().remove(layerIndex);
                    //重新添加该图层,并使之上移
                    groupLayer.getLayers().add(layerIndexNext,layer);

                    upDown_layer_name.clear();
                    System.out.println("图层名称集合总数:" + MainActivity.LayerName.size());
                    System.out.println(upDown_layer_name.size());
                    System.out.println(layer_total_index);

                    //清空数组的同时,也将原始的数组的同时清除了
                    for (int i = 0;i <layer_total_index;i++)
                    
                        String layer_name = groupLayer.getLayers().get(i).getName();
                        System.out.println(layer_name);
                        upDown_layer_name.add(layer_name);
                    
                    System.out.println("图层名称集合总数:" + MainActivity.LayerName.size());
                
            
        );

此处代码写在LayerAdapter这个类中,需要提前去传值,同时涉及不同类之间的layout调用,可以参考下面这个文章完善你的代码,完整代码我就不贴了太多了。 

android 调用其他activity类中的方法_smalltree_it的博客-CSDN博客


总结

福至心灵想到的图层上下移动方法,花了一天在寻找看有没有参数直接改,没办法曲线救国两个钟出成果了,所以朋友们,绝知此事要躬行啊!

以上是关于Arcgis runtime for android mmpk加载图层组实现上下移动切换的主要内容,如果未能解决你的问题,请参考以下文章

Arcgis Runtime for andriod 100 加载geodatabase

Arcgis Runtime for andriod 100 加载TPK

Arcgis Runtime for andriod 100 Simple marker symbol

ArcGIS Runtime for Android 5 加载地图

用ArcGIS Runtime for Android建立简单App,展示地图

ArcGIS Runtime for Android 1 开发环境部署