您的位置:首页 > 移动开发 > Android开发

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

2013-12-20 14:56 274 查看
接着《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


其中的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来新建更新和添加。求解释。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: