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

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

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


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


上面的方法中还有两个方法(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) {
		//取得正在被编辑的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来新建更新和添加。求解释。




评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值