ArcGIS for Android 例子Offline Editor (BETA)(四)
2013-12-20 14:56
274 查看
接着《ArcGIS for Android 例子Offline Editor (BETA)(三)》。
1、 在实现编辑按钮事件之前,需要完善以前的代码,进入GDBUtil,在downGeodatabase方法中在下载完GDB中加入代码:
进入OfflineEditorActivity中,在刷新按钮的方法中,加入如下代码:其中的clear()方法,后面会实现。
2 、在第一次显示TemplatePicker,需要发较多的时间加载数据,所以需要让其在子线程中加载,可以定义一个异步类来加载和显示TemplatePicker:
3、 改变编辑按钮事件的代码,先判断MapView中是否有FeatureLayer,如果有,就给MapView设置TouchListener(监听类后面实现),并判断tp是否被实例化,如果被实例化直接显示出来就可以,如果没有显示出来执行异步类。
4、 在OfflineEditorActivity中加入常量用来标识编辑的图形类型:
5、在在OfflineEditorActivity中加入如下变量:points保存着所有节点数据,画线和面都是通过它来进行的,节点在显示的时候以黑色圆点来显示。midPoints中包含着没两个节点之间的中点,是通过points来计算出来,显示的时候以绿色圆点显示。editGraphicLayer是用来显示点、线、面的绘制结果。
其中的MyTouchListener为MapView的TouchListener等下实现。类EditState为要素要在编辑过程中某一种状态。而它的List集合就能保存编辑过程,撤销操作时候需要用到它。类EditState的代码如下:
6、在onCreate方法中,给MapView添加状态变化的监听,在加载完成之后,就把editGraphicLayer加入到MapView中
8、在设置完editMode之后就可以完成绘制方法:
9、为了方便,我们可以上面的绘制封装在一个方法内,并在这个方法内,并可以在这个方法内,设置各个按钮的状态:
10、 以上有了绘制方法,我们就要实现清除方法,清除的方法比较简单只要将各个按钮和变量回复到编辑强状态
11、接下来就可以实现编辑按钮中给MapView设置的TouchListener了,新建类MyTouchListener继承MapOnTouchListener,并重写其中的onSingleTap这个方法,这个方法中的逻辑判断有点复杂,都是用if语句来判断的。可以用下图表示:
![](http://img.blog.csdn.net/20131219225304765?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWlmZWkxOTg5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
当用户单击屏幕的时候,就判断用户是否想要选中当前已经存在的要素进行编辑的,如果是就查找出这个要素,并将这个要素的节点加入到points中。如果不是想编辑现有的要素,就判断是否当前在编辑点图层,如果是,就把已经增加的点的删除,把当前点加入到points中。如果当前不是在编辑点图层,就判断在已经增加的节点中是否有节点或者中点被选中了,如果有点被选中了,就继续判断用户的本次点击是不是想要重新选择点,如果是就去点原先的选择,改成当前点选中,如果不是就把选中的点移动到点击的位置。如果已经增加的节点中没有节点或者中点被选中,就判断用户本次的点击是不是想要选中点,如果是,就选中用户点击的点,如果不是就增加新的点到points中,在每次编辑之后都要调用下reflesh()方法,和实例化一个EditState,加入到editstates,来保持历史。其实这样的判断我感觉还是有缺陷,比如在编辑点图层的时候,无法连续添加多点还有无法删除中点等,但作为练习应该够了。
onSingleTap可以如下写法:
上面的方法中还有两个方法(getSelectedPoint,movePoint)未实现,分别用来判断用户是否要选中点,和移动点的:
以上的onSingleTap方法跟例子有点不一样,例如没有在进行点图层编辑时,将点图层专门绘制到一个GraphicLayer中。还有其他的一些写法不同,我运行的一遍挺正常的,希望不要出现其他的情况吧。
12、 完成的编辑按钮之后,其他的删除,取消选择,清除编辑,撤销编辑就比较简单了,直接上代码:
13、最后来实现保存按钮的事件,保存编辑结果,主要使用FeatureTable的updateFeature和addFeature方法来更新和添加要素,可用如下代码:
以上保存代码,跟例子也有不一样的,但我测试之后也能正常保存,不知道例子为什么要那样写,获取到要更新或者添加的GdbFeature,不直接进行更新和添加,还要根据GdbFeature来构建一个Graphic之后使用这个Graphic来新建更新和添加。求解释。
1、 在实现编辑按钮事件之前,需要完善以前的代码,进入GDBUtil,在downGeodatabase方法中在下载完GDB中加入代码:
showProgress(activity, false); //因为重新下载了GDB了,所以需要把OfflineEditorActivity已经实例化的TemplatePicker设为null activity.setTp(null);
进入OfflineEditorActivity中,在刷新按钮的方法中,加入如下代码:其中的clear()方法,后面会实现。
GDBUtil.showMessage(this, "刷新成功"); if(tp!=null){ tp=null; clear(); }
2 、在第一次显示TemplatePicker,需要发较多的时间加载数据,所以需要让其在子线程中加载,可以定义一个异步类来加载和显示TemplatePicker:
//下载和同步Geodatabase的异步类 class MyAsyncTask extends AsyncTask<String, Void, Void>{ private OfflineEditorActivity activity; private MapView map; public MyAsyncTask(OfflineEditorActivity activity, MapView map) { super(); this.activity = activity; this.map = map; } //判断params参数,来执行下载或者同步 protected Void doInBackground(String... params) { String param=params[0]; if(param.equalsIgnoreCase("downLoad")){//下载 GDBUtil.downGeodatabase(activity, map); }else if(param.equalsIgnoreCase("sync")){//同步 GDBUtil.sycnGeodatabase(activity); } return null; } }
3、 改变编辑按钮事件的代码,先判断MapView中是否有FeatureLayer,如果有,就给MapView设置TouchListener(监听类后面实现),并判断tp是否被实例化,如果被实例化直接显示出来就可以,如果没有显示出来执行异步类。
/** * 编辑 */ public void editButton(View v) { boolean exitFeature=false;//标识是否存在Feature Layer[] layers= mMapView.getLayers(); for(Layer layer:layers){ if(layer instanceof FeatureLayer){//存在Feature exitFeature=true; break; } } if(exitFeature){ //设置MapView的TouchListener myTouchListener=new MyTouchListener(this, mMapView); mMapView.setOnTouchListener(myTouchListener); if(tp!=null){ tp.showAtLocation(btn_edit, Gravity.BOTTOM, 0, 0); }else{ new TemplatePickerTask().execute(); } }else{ GDBUtil.showMessage(this, "没有可以编辑的图层"); } }
4、 在OfflineEditorActivity中加入常量用来标识编辑的图形类型:
private static final int POINT=0; private static final int POLYLINE=1; private static final int POLYGON=2;
5、在在OfflineEditorActivity中加入如下变量:points保存着所有节点数据,画线和面都是通过它来进行的,节点在显示的时候以黑色圆点来显示。midPoints中包含着没两个节点之间的中点,是通过points来计算出来,显示的时候以绿色圆点显示。editGraphicLayer是用来显示点、线、面的绘制结果。
private List<Point> points=new ArrayList<Point>();//保存节点 private List<Point> midPoints=new ArrayList<Point>();//保存没两个节点之间的中点 private List<EditState> editstates=new ArrayList<EditState>();//保持编辑的历史 private boolean isVerSelected;//是否有节点被选中 private boolean isMidSelected;//是否有中点被选中 private int selectIndex=-1;//被选中点的序号 private MyTouchListener myTouchListener;//MapView 的TouchListener private GraphicsLayer editGraphicLayer;//绘制点、线、面 private int editMode;//编辑图层的类型 boolean featureUpdate = false;//是否进行要素的更新 long featureUpdateId;//被更新要素的ID int addedGraphicId;//新增Graphic的ID
其中的MyTouchListener为MapView的TouchListener等下实现。类EditState为要素要在编辑过程中某一种状态。而它的List集合就能保存编辑过程,撤销操作时候需要用到它。类EditState的代码如下:
//编辑的某一时刻的状态 class EditState{ List<Point> points=new ArrayList<Point>();//节点 boolean isVerSelected;//是否有节点被选中 boolean isMidSelected;//是否有中点被选中 int selectIndex;//被选中点的序号 EditState(List<Point> points, boolean isVerSelected, boolean isMidSelected, int selectIndex) { super(); this.points .addAll(points); this.isVerSelected = isVerSelected; this.isMidSelected = isMidSelected; this.selectIndex = selectIndex; } }
6、在onCreate方法中,给MapView添加状态变化的监听,在加载完成之后,就把editGraphicLayer加入到MapView中
mMapView.setOnStatusChangedListener(new OnStatusChangedListener() { private static final long serialVersionUID = 1L; public void onStatusChanged(Object source, STATUS status) { if(source==mMapView && status==STATUS.LAYER_LOADED){ editGraphicLayer=new GraphicsLayer(); mMapView.addLayer(editGraphicLayer); } } });7、其实在编辑的时候,都是先将编辑结果保持的到points中,之后在根据当前编辑的图层类型,来计算出midPoints,最后在通过points绘制出边界,节点,通过midPoints绘制出中点。在绘制之前我们需要先知道,当前编辑图层的类型,并把它保存起来,可以通过以下方法:
//设置editMode void setEditMode(){ if(tp==null){ return; } //得到要编辑图层的类型 Type type= tp.getSelectFeatureLayer().getGeometryType(); if(type.equals(Type.POINT)){ editMode=POINT; }else if(type.equals(Type.POLYLINE)){ editMode=POLYLINE; }else if(type.equals(Type.POLYGON)){ editMode=POLYGON; } }
8、在设置完editMode之后就可以完成绘制方法:
//绘制线/面 void drawLine(){ //如果points为null,或者points中的节点个数小2个,就不绘制 if(points==null || points.size()<=1){ return; } Graphic g=null; MultiPath paths = null; if(editMode==POLYLINE){ paths=new Polyline(); }else if(editMode==POLYGON){ paths=new Polygon(); } //绘制起点 paths.startPath(points.get(0)); //绘制剩下的点 for(int i=1;i<points.size();i++){ paths.lineTo(points.get(i)); } //绘制线 if(editMode==POLYLINE){ g=new Graphic(paths, new SimpleLineSymbol(Color.BLACK, 4)); }else if(editMode==POLYGON){//绘制面 SimpleFillSymbol symbol=new SimpleFillSymbol(Color.YELLOW); symbol.setAlpha(50);//半透明 symbol.setOutline(new SimpleLineSymbol(Color.BLACK, 4));//设置外包线 g=new Graphic(paths, symbol); } editGraphicLayer.addGraphic(g); } //绘制节点 void drawVer(){ //依次绘制节点 for(int i=0;i<points.size();i++){ Graphic g=null; //被选中节点的符号 SimpleMarkerSymbol selectedSymbol=new SimpleMarkerSymbol(Color.RED, 20, SimpleMarkerSymbol.STYLE.CIRCLE); //没有被选中节点的编号 SimpleMarkerSymbol symbol=new SimpleMarkerSymbol(Color.BLACK, 20, SimpleMarkerSymbol.STYLE.CIRCLE); //如果有节点被选择,并且选择节点的序号和要绘制节点序号一致 if(isVerSelected && i==selectIndex){ g=new Graphic(points.get(i), selectedSymbol); }else if(selectIndex==-1 && i==points.size()-1){//没有节点被选中,默认为最后一个点被选中 g=new Graphic(points.get(i), selectedSymbol); }else{ g=new Graphic(points.get(i), symbol); } editGraphicLayer.addGraphic(g); } } //绘制中点 void drawMidPoint(){ midPoints.clear(); //根据节点得到中点 for(int i=0;i<points.size()-1;i++){ Point p=points.get(i);//当前点 Point nextP=points.get(i+1);//下一点 midPoints.add(new Point((p.getX()+nextP.getX())/2, (p.getY()+nextP.getY())/2)); } //如果当前绘制的为面,还需要加上起点和终点的中点 if(editMode==POLYGON){ Point startP=points.get(0); Point endP=points.get(points.size()-1); midPoints.add(new Point((startP.getX()+endP.getX())/2, (startP.getY()+endP.getY())/2)); } Graphic g=null; //被选中节点的符号 SimpleMarkerSymbol selectedSymbol=new SimpleMarkerSymbol(Color.RED, 20, SimpleMarkerSymbol.STYLE.CIRCLE); //没有被选中节点的编号 SimpleMarkerSymbol symbol=new SimpleMarkerSymbol(Color.GREEN, 15, SimpleMarkerSymbol.STYLE.CIRCLE); //如果中点有被选中 for(int i=0;i<midPoints.size();i++){ if(isMidSelected && selectIndex==i){ g=new Graphic(midPoints.get(i), selectedSymbol); }else{ g=new Graphic(midPoints.get(i), symbol); } editGraphicLayer.addGraphic(g); } }
9、为了方便,我们可以上面的绘制封装在一个方法内,并在这个方法内,并可以在这个方法内,设置各个按钮的状态:
//刷新绘制图层按钮 void refresh(){ if(editMode!=POINT){ if(editGraphicLayer!=null){ //移除editGraphicLayer中所有的Graphic editGraphicLayer.removeAll(); } //绘制线,节点,中点 drawLine(); drawVer(); drawMidPoint(); btn_undo.setEnabled(editstates.size()>1); } //设置各个按钮的是否可用 btn_clear.setEnabled(true); btn_remove.setEnabled(points.size() > 1 && !isMidSelected); btn_cancel.setEnabled(isMidSelected || isVerSelected); btn_save.setEnabled((editMode == POINT && points.size() > 0) || (editMode == POLYLINE && points.size() > 1) || (editMode == POLYGON && points.size() > 2)); }有了这个方法之后在编辑过程中,只要改变points集合的Point,在调用下这个方法,就能把编辑结果显示出来。
10、 以上有了绘制方法,我们就要实现清除方法,清除的方法比较简单只要将各个按钮和变量回复到编辑强状态
//清除,回到编辑前状态 void clear(){ if(editGraphicLayer!=null){ editGraphicLayer.removeAll(); } points.clear(); midPoints.clear(); editstates.clear(); isMidSelected=false; isVerSelected=false; selectIndex=-1; btn_cancel.setEnabled(false); btn_clear.setEnabled(false); btn_remove.setEnabled(false); btn_save.setEnabled(false); btn_undo.setEnabled(false); }
11、接下来就可以实现编辑按钮中给MapView设置的TouchListener了,新建类MyTouchListener继承MapOnTouchListener,并重写其中的onSingleTap这个方法,这个方法中的逻辑判断有点复杂,都是用if语句来判断的。可以用下图表示:
当用户单击屏幕的时候,就判断用户是否想要选中当前已经存在的要素进行编辑的,如果是就查找出这个要素,并将这个要素的节点加入到points中。如果不是想编辑现有的要素,就判断是否当前在编辑点图层,如果是,就把已经增加的点的删除,把当前点加入到points中。如果当前不是在编辑点图层,就判断在已经增加的节点中是否有节点或者中点被选中了,如果有点被选中了,就继续判断用户的本次点击是不是想要重新选择点,如果是就去点原先的选择,改成当前点选中,如果不是就把选中的点移动到点击的位置。如果已经增加的节点中没有节点或者中点被选中,就判断用户本次的点击是不是想要选中点,如果是,就选中用户点击的点,如果不是就增加新的点到points中,在每次编辑之后都要调用下reflesh()方法,和实例化一个EditState,加入到editstates,来保持历史。其实这样的判断我感觉还是有缺陷,比如在编辑点图层的时候,无法连续添加多点还有无法删除中点等,但作为练习应该够了。
onSingleTap可以如下写法:
public boolean onSingleTap(MotionEvent e) { Point point=mMapView.toMapPoint(new Point(e.getX(),e.getY())); if(tp!=null){ //设置编辑的图层类型 setEditMode(); //判断用户是不是想要选择现有要素进行编辑 long[] selectedIDs= tp.getSelectFeatureLayer().getFeatureIDs(e.getX(), e.getY(), 30); if(selectedIDs.length>0 && !featureUpdate){//有要素可以选中,并且当前没有标识为更新要素 //设置要更新的要素为可以选中要素的第一个 featureUpdateId=selectedIDs[0]; Feature selectedFer= tp.getSelectFeatureLayer().getFeature(featureUpdateId); if(editMode==POLYLINE||editMode==POLYGON){ if(editMode==POLYLINE){//当前编辑的为线图层 Polyline line=(Polyline) selectedFer.getGeometry(); //将线上的节点加入到points中 for(int i=0;i<line.getPointCount();i++){ points.add(line.getPoint(i)); } }else if(editMode==POLYGON){//当前编辑的为面图层 Polygon polygon=(Polygon) selectedFer.getGeometry(); //将面上的节点加入到points中 for(int i=0;i<polygon.getPointCount();i++){ points.add(polygon.getPoint(i)); } } featureUpdate=true;//标识更新要素 //增加一个编辑过程 editstates.add(new EditState(points, isVerSelected, isMidSelected, selectIndex)); refresh();//刷新 } }else{//没有要素可以被选中 if(editMode==POINT){//在编辑点图层 editGraphicLayer.removeAll(); try { //根据当前点和Template创建出GdbFeature GdbFeature feature= ((GdbFeatureTable)(tp.getSelectFeatureLayer().getFeatureTable())).createFeatureWithTemplate(tp.getSelectTemplate(), point); //Symbol symbol=feature.getSymbol(); Symbol symbol=tp.getSelectFeatureLayer().getRenderer().getSymbol(feature); Graphic g=new Graphic(feature.getGeometry(), symbol, feature.getAttributes()); addedGraphicId= editGraphicLayer.addGraphic(g); } catch (TableException e1) { e1.printStackTrace(); } btn_save.setEnabled(true); btn_clear.setEnabled(true); }else{//在编辑线、面图层 if(!isMidSelected&&!isVerSelected){//没有已添加的点被选中 int idx1=getSelectedPoint(e.getX(), e.getY(), points, mMapView); if(idx1!=-1){//有节点被选中 isVerSelected=true; selectIndex=idx1; } if(!isVerSelected){//没有节点被选中,继续判断是否有中点被选中 int idx2=getSelectedPoint(e.getX(), e.getY(), midPoints, mMapView); if(idx2!=-1){//有中点被选中 isMidSelected=true; selectIndex=idx2; } } if(!isMidSelected&&!isVerSelected){//到这还是没有点被选中,说明用户要添加点 points.add(point); } }else{//有已添加的点被选中 int idx1=getSelectedPoint(e.getX(), e.getY(), points, mMapView); int idx2=getSelectedPoint(e.getX(), e.getY(), midPoints, mMapView); if(idx1==-1 &&idx2==-1){//不是重新选择点 //移动点 movePoint(point); } if(idx1!=-1){//有节点可以重新被选中 selectIndex=idx1; } if(idx2!=-1){//有中点可以重新被选中 selectIndex=idx2; } } //增加一个编辑过程 editstates.add(new EditState(points, isVerSelected, isMidSelected, selectIndex)); refresh();//刷新 } } Log.i("stateCount", editstates.size()+""); } return true; }
上面的方法中还有两个方法(getSelectedPoint,movePoint)未实现,分别用来判断用户是否要选中点,和移动点的:
/** * 计算在一个point的List集合中距离(x,y)最近的一个点, * 如果最近的一个点与(x,y)距离小于40*40就返回该点在List中序号,否则返回-1 */ int getSelectedPoint(double x,double y,List<Point> points,MapView map){ if(points==null||points.size()<=0){ return -1; } double dis_smll =Double.MAX_VALUE; int index=0; //遍历points,找出距离(x,y)最近的点的距离 for(int i=0;i<points.size();i++){ Point screenP=map.toScreenPoint(points.get(i)); //得到两点的x坐标差值得平方加上y坐标差值得平方 double dis=(x-screenP.getX())*(x-screenP.getX())+(y-screenP.getY())*(y-screenP.getY()); if(dis<dis_smll){ dis_smll=dis; index=i; } } if(dis_smll<40*40){//如果最短的两点距离小于40*40,就表示该点被选中 return index; } return -1; } //移动被选中的点 void movePoint(Point p){ if(isVerSelected){ //移掉被选中的点 points.remove(selectIndex); //增加当前点 points.add(selectIndex, p); }else if(isMidSelected){ points.add(selectIndex+1, p); } //恢复到没有选中状态 isVerSelected=false; isMidSelected=false; selectIndex=-1; }
以上的onSingleTap方法跟例子有点不一样,例如没有在进行点图层编辑时,将点图层专门绘制到一个GraphicLayer中。还有其他的一些写法不同,我运行的一遍挺正常的,希望不要出现其他的情况吧。
12、 完成的编辑按钮之后,其他的删除,取消选择,清除编辑,撤销编辑就比较简单了,直接上代码:
/** * 删除点 */ public void removeButton(View v) { if(points.size()>0){ //没有点被选,就默认移除最后一个点 if(!isMidSelected&&!isVerSelected){ points.remove(points.size()-1); //增加一个编辑过程 editstates.add(new EditState(points, isVerSelected, isMidSelected, selectIndex)); refresh();//刷新 } //有节点被选中 if(isVerSelected){ points.remove(selectIndex); isVerSelected=false; selectIndex=-1; //增加一个编辑过程 editstates.add(new EditState(points, isVerSelected, isMidSelected, selectIndex)); refresh();//刷新 } } } /** * 取消 */ public void cancelButton(View v) { isVerSelected=false; isMidSelected=false; selectIndex=-1; } /** * 清除 */ public void clearButton(View v) { clear(); } /** * 撤销 */ public void undoButton(View v) { //移除editstates最后一个 editstates.remove(editstates.size()-1); //恢复的上一个状态 EditState state= editstates.get(editstates.size()-1); isMidSelected= state.isMidSelected; isVerSelected=state.isVerSelected; selectIndex=state.selectIndex; points=state.points; refresh(); }
13、最后来实现保存按钮的事件,保存编辑结果,主要使用FeatureTable的updateFeature和addFeature方法来更新和添加要素,可用如下代码:
/** * 保存 */ public void saveButton(View v) { //取得正在被编辑的FeatureTable FeatureTable editFeatureTable= tp.getSelectFeatureLayer().getFeatureTable(); try { if(editMode==POINT){ Graphic g= editGraphicLayer.getGraphic(addedGraphicId); editFeatureTable.addFeature(g); }else{ MultiPath paths=null; if(editMode==POLYLINE){ paths=new Polyline(); }else if(editMode==POLYGON){ paths=new Polygon(); } paths.startPath(points.get(0)); for(int i=1;i<points.size();i++){ paths.lineTo(points.get(i)); } GdbFeature feature= ((GdbFeatureTable)editFeatureTable).createFeatureWithTemplate(tp.getSelectTemplate(), paths); if(featureUpdate){//更新要素 editFeatureTable.updateFeature(featureUpdateId, feature); }else{//添加要素 editFeatureTable.addFeature(feature); } } clear(); } catch (Exception e) { e.printStackTrace(); } }
以上保存代码,跟例子也有不一样的,但我测试之后也能正常保存,不知道例子为什么要那样写,获取到要更新或者添加的GdbFeature,不直接进行更新和添加,还要根据GdbFeature来构建一个Graphic之后使用这个Graphic来新建更新和添加。求解释。
相关文章推荐
- ArcGIS for Android 例子Offline Editor (BETA)(一)
- ArcGIS for Android 例子Offline Editor (BETA)(二)
- ArcGIS for Android 例子Offline Editor (BETA)(三)
- ArcGIS for Android 例子Offline Editor (BETA)(五)
- ArcGis for Android 10.2.8个人整理
- ArcGIS for Android示例解析之高亮要素-----HighlightFeatures
- ArcGIS for Android示例解析之空间查询-----QueryTask
- arcGis for android 学习之callOut
- python for android : 一个输入ISBN的查询书籍例子
- 如何卸载Eclipse中ArcGIS for Android的旧版本?
- ArcGIS For Android 的标绘与可视化
- Arcgis for Android 处理点击地图触发地图平移监听的问题
- ArcGIS for Android 在Eclipse上的安装配置 (上:下载篇)
- ArcGIS Runtime SDK for Android 各版本下载地址及介绍
- ArcGIS for Android Runtime100 基本操作(一)——点线面测距离长度和面积
- ArcGis for android
- ArcGIS for Android
- ArcGIS Runtime SDK For Android 授权方法(去除水印)
- ArcGIS for Android FeatureLayer的属性更新
- 1.ArcGIS Runtime SDK for Android-第一个地图应用