通过交互绘制几何对象
客户端要素除了有显示业务数据的功能,同时也负责着和用户的交互。比如用户想要进行一个多边形查询,首先需要在客户端绘制一个多边形,然后再使用这个多边形进行一个空间查询。这样的一个功能我们可以从ArcGIS Android的DrawGraphicElements例子来学习一下,下面先看一下这个例子运行的效果:
图 25 通过用户交互绘制一个多边形
这个例子的关键在于可以通过在屏幕上进行触屏操作,然后记录触屏的坐标构成一个几何对象,结束后将这个几何对象通过Graphic的形式绘制到屏幕上。下面让我们看看从点击上面的“Select Geometry”按钮之后,一步步都发生了什么。
首先,先会弹出了一个对话框,这在Java代码中非常简单:
geometryButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
showDialog(0);
}
});
showDialog是Activity的一个方法,这个方法会显示onCreateDialog方法定义的对话框,这个方法的内容如下(精简了一些提示信息等代码):
protected Dialog onCreateDialog(int id) {
return new AlertDialog.Builder(DrawGraphicElements.this)
.setTitle("Select Geometry")
.setItems(geometryTypes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
graphicsLayer.clear();
String geomType = geometryTypes[which];
label.setText(geomType + " selected.");
selectedGeometryIndex = which;
if (geomType.equalsIgnoreCase("Polygon")) {
SimpleFillSymbol sfs = new SimpleFillSymbol(Color.GREEN);
sfs.setAlpha(60);
graphicsLayer.setRenderer(new SimpleRenderer(sfs));
myListener.setType("POLYGON");
} else if (geomType.equalsIgnoreCase("Polyline")) {
graphicsLayer.setRenderer(new SimpleRenderer(new SimpleLineSymbol(Color.RED, 5)));
myListener.setType("POLYLINE");
} else if (geomType.equalsIgnoreCase("Point")) {
graphicsLayer.setRenderer(new SimpleRenderer(new SimpleMarkerSymbol(Color.BLUE, 15, STYLE.CIRCLE)));
myListener.setType("POINT");
}
}
}).create();
}
这个对话框通过setItems方法显示出了3个可选项:“Point”、“Polyline”、“Polygon”,当点击不同的Item的时候,会触发事件监听,然后根据不同的Item设置不同的绘制类型(注意一下高亮的几行代码)。
这里还出现了一个没有出场过的myListener对象,上面通过给这个对象赋不同值达到改变绘制类型的目的,而具体的实现还需要看这个myListener的定义:
myListener = new MyTouchListener(DrawGraphicElements.this, mapView);
mapView.setOnTouchListener(myListener);
class MyTouchListener extends MapOnTouchListener {
MultiPath poly;
String type = "";
Point startPoint = null;
public MyTouchListener(Context context, MapView view) {
super(context, view);
}
public void setType(String geometryType) {
this.type = geometryType;
}
public String getType() {
return this.type;
}
@Override
public boolean onSingleTap(MotionEvent e) {
if (type.length() > 1 && type.equalsIgnoreCase("POINT")) {
graphicsLayer.clear();
Graphic graphic = new Graphic();
graphic.setGeometry(mapView.toMapPoint(new Point(e.getX(), e.getY())));
graphicsLayer.addGraphic(graphic);
graphicsLayer.postInvalidate();
clearButton.setEnabled(true);
return true;
}
return false;
}
@Override
public boolean onDragPointerMove(MotionEvent from, MotionEvent to) {
if (type.length() > 1 && (type.equalsIgnoreCase("POLYLINE") ||
type.equalsIgnoreCase("POLYGON"))) {
Point mapPt = mapView.toMapPoint(to.getX(), to.getY());
if (startPoint == null) {
graphicsLayer.clear();
poly = type.equalsIgnoreCase("POLYLINE")?new Polyline():new Polygon();
startPoint = mapView.toMapPoint(from.getX(), from.getY());
poly.startPath((float) startPoint.getX(), (float) startPoint.getY());
Graphic graphic = new Graphic();
graphic.setGeometry(poly);
graphicsLayer.addGraphic(graphic);
}
poly.lineTo((float) mapPt.getX(), (float) mapPt.getY());
graphicsLayer.postInvalidate();
return true;
}
return super.onDragPointerMove(from, to);
}
@Override
public boolean onDragPointerUp(MotionEvent from, MotionEvent to) {
if (type.length() > 1 && (type.equalsIgnoreCase("POLYLINE") ||
type.equalsIgnoreCase("POLYGON"))) {
if (type.equalsIgnoreCase("POLYGON")) {
poly.lineTo((float) startPoint.getX(), (float) startPoint.getY());
}
startPoint = null;
graphicsLayer.postInvalidate();
clearButton.setEnabled(true);
return true;
}
return false;
}
}
如果我选择了“Polygon”,那么显然上面代码中的“type”值就是“Polygon”,这时,如果在屏幕上进行触屏操作,那么就会进入 “onDragPointerMove”这个事件。在这个事件中,屏幕首先会捕捉触摸的第一个点,并放入“startPoint”这个对象,同时新建一个Polygon对象。再往后只要在屏幕上移动了一点,就会再进入这个事件中调用lineTo方法对这个Polygon对象进行绘制。最后,用户结束绘制,程序调用“onDragPointerUp”事件监听,这个Graphic对象构造完成。