ArcGIS for Android 例子Offline Editor (BETA)(四)

来源:互联网 发布:java开发培训班哪个好 编辑:程序博客网 时间:2024/04/29 10:40

接着《ArcGIS for Android 例子Offline Editor (BETA)(三)》。

1、 在实现编辑按钮事件之前,需要完善以前的代码,进入GDBUtil,在downGeodatabase方法中在下载完GDB中加入代码:

showProgress(activity, false);//因为重新下载了GDB了,所以需要把OfflineEditorActivity已经实例化的TemplatePicker设为nullactivity.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;//标识是否存在FeatureLayer[] layers= mMapView.getLayers();for(Layer layer:layers){if(layer instanceof FeatureLayer){//存在FeatureexitFeature=true;break;}}if(exitFeature){//设置MapView的TouchListenermyTouchListener=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 的TouchListenerprivate GraphicsLayer editGraphicLayer;//绘制点、线、面private int editMode;//编辑图层的类型boolean featureUpdate = false;//是否进行要素的更新long featureUpdateId;//被更新要素的IDint addedGraphicId;//新增Graphic的ID


其中的MyTouchListenerMapViewTouchListener等下实现。类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绘制出中点。在绘制之前我们需要先知道,当前编辑图层的类型,并把它保存起来,可以通过以下方法:
//设置editModevoid 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中所有的GraphiceditGraphicLayer.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创建出GdbFeatureGdbFeature 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;}


上面的方法中还有两个方法(getSelectedPointmovePoint)未实现,分别用来判断用户是否要选中点,和移动点的:

/** * 计算在一个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、最后来实现保存按钮的事件,保存编辑结果,主要使用FeatureTableupdateFeatureaddFeature方法来更新和添加要素,可用如下代码:

/** * 保存 */public void saveButton(View v) {//取得正在被编辑的FeatureTableFeatureTable 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来新建更新和添加。求解释。




0 0