手势和触摸事件
当JavaFX应用程序在支持手势识别的触摸屏或触摸板设备上运行时,会生成手势事件。对于支持手势识别的平台,会使用本地识别来识别执行的手势。 表5-1 描述了支持的手势以及生成的相应事件类型。
表5-1 支持的手势和生成的事件类型
手势 | 描述 | 生成的事件 |
---|---|---|
旋转 |
使用两个手指进行旋转动作,一个手指顺时针绕另一个手指旋转以顺时针旋转物体,一个手指逆时针绕另一个手指旋转以逆时针旋转物体。 |
|
滚动 |
上下滑动或左右滑动进行垂直或水平滚动。 |
如果使用鼠标滚轮进行滚动,只会生成 |
划动 |
在屏幕或触控板上进行向右、向左、向上或向下的划动动作。不会将对角线移动识别为划动。 |
每个划动手势只会生成一个划动事件。还会生成 |
缩放 |
使用两个手指进行捏合动作,手指靠拢进行缩小,手指分开进行放大。 |
|
当应用程序在具有触摸屏的设备上运行时,触摸事件会在用户触摸屏幕时生成。这些事件可以用于对触摸或手势的各个触点进行较低级别的跟踪。有关触摸事件的更多信息,请参阅使用触摸事件。
手势的目标
大多数手势的目标是手势开始时所有触点的中心点处的节点。滑动手势的目标是整个手指路径的中心节点。
如果目标点上有多个节点,则最上层的节点被视为目标。从单个连续手势生成的所有事件(包括手势的惯性)都会传递给手势开始时选择的节点。有关事件目标的更多信息,请参阅目标选择。
生成的其他事件
除了手势或触摸事件之外,手势和触摸还可以生成其他类型的事件。滑动手势除了滑动事件外,还会生成滚动事件。根据滑动的长度,滑动和滚动事件可能具有不同的目标。滚动事件的目标是手势开始的点处的节点。滑动事件的目标是整个手势路径的中心节点。
触摸屏上的触摸还会生成相应的鼠标事件。例如,触摸屏上的触摸点会生成TOUCH_PRESSED
和MOUSE_PRESSED
事件。在屏幕上移动一个点会生成滚动事件和拖动事件。即使您的应用程序不直接处理触摸或手势事件,通过响应由触摸生成的鼠标事件,它也可以在具有触摸屏的设备上运行,而只需进行最小的更改。
如果您的应用程序处理触摸、手势和鼠标事件,请确保不要多次处理单个操作。例如,如果一个手势生成滚动事件和拖动事件,并且您为两种类型的事件的处理程序提供相同的处理,屏幕上的移动可能是预期量的两倍。您可以使用isSynthesized()
方法来判断鼠标事件是由鼠标移动还是触摸屏上的移动生成的,并且只处理一次事件。
手势事件示例
手势事件示例展示了一个矩形、一个椭圆和一个事件日志。图5-1展示了示例。
日志记录了已处理的事件。该示例允许您尝试不同的手势,并查看每个手势生成的事件。
手势事件示例可在GestureEventsExample.zip
文件中找到。解压缩NetBeans项目并在NetBeans IDE中打开。
要生成手势事件,您必须在支持手势的触摸屏或触摸板设备上运行示例。要生成触摸事件,您必须在触摸屏设备上运行示例。
创建形状
手势事件示例展示了一个矩形和一个椭圆。示例5-1展示了用于创建每个形状和包含形状的布局面板的代码。
示例5-1 设置形状
// 创建响应手势的形状,并使用VBox进行组织 VBox shapes = new VBox(); shapes.setAlignment(Pos.CENTER); shapes.setPadding(new Insets(15.0)); shapes.setSpacing(30.0); shapes.setPrefWidth(500); shapes.getChildren().addAll(createRectangle(), createEllipse()); ... private Rectangle createRectangle() { final Rectangle rect = new Rectangle(100, 100, 100, 100); rect.setFill(Color.DARKMAGENTA); ... return rect; } private Ellipse createEllipse() { final Ellipse oval = new Ellipse(100, 50); oval.setFill(Color.STEELBLUE); ... return oval; }
您可以使用手势来移动、旋转和缩放这些对象。
处理事件
一般来说,手势事件示例中形状对象的事件处理程序对于每种处理的事件类型执行类似的操作。对于所有类型的事件,都会在事件日志中发布一个条目。
在支持手势惯性的平台上,event-type_FINISHED
事件之后可能会生成其他事件。例如,如果滚动手势有惯性,那么在SCROLL_FINISHED
事件之后可能会生成SCROLL
事件。使用isInertia()
方法来识别基于手势惯性生成的事件。如果该方法返回true
,则表示该事件是在手势完成后生成的。
手势在触摸屏或触摸板上生成事件。鼠标滚轮也会生成SCROLL
事件。使用isDirect()
方法来识别事件的来源。如果该方法返回true
,则表示该事件是由触摸屏上的手势生成的。否则,该方法返回false
。您可以根据事件的来源提供不同的行为。
触摸屏上的触摸还会生成相应的鼠标事件。例如,触摸对象会生成TOUCH_PRESSED
和MOUSE_PRESSED
事件。使用isSynthesized()
方法来确定鼠标事件的来源。如果该方法返回true
,则表示该事件是由触摸而不是鼠标生成的。
手势事件示例中的inc()
和dec()
方法用于提供一个视觉提示,表明对象是手势的目标。跟踪进行中的手势数量,并在活动手势数量从0变为1或减少到0时更改目标对象的外观。
在手势事件示例中,矩形和椭圆的处理程序是相似的。因此,以下部分的代码示例显示了矩形的处理程序。有关椭圆的处理程序,请参见GestureEvents.java
。
处理滚动事件
当执行滚动手势时,会生成SCROLL_STARTED
、SCROLL
和SCROLL_FINISHED
事件。当移动鼠标滚轮时,只会生成SCROLL
事件。在手势事件示例中,示例5-2展示了矩形处理滚动事件的处理程序。椭圆的处理程序类似。
示例5-2 定义滚动事件的处理程序
rect.setOnScroll(new EventHandler<ScrollEvent>() { @Override public void handle(ScrollEvent event) { if (!event.isInertia()) { rect.setTranslateX(rect.getTranslateX() + event.getDeltaX()); rect.setTranslateY(rect.getTranslateY() + event.getDeltaY()); } log("矩形:滚动事件" + ",惯性:" + event.isInertia() + ",直接:" + event.isDirect()); event.consume(); } }); rect.setOnScrollStarted(new EventHandler<ScrollEvent>() { @Override public void handle(ScrollEvent event) { inc(rect); log("矩形:滚动开始事件"); event.consume(); } }); rect.setOnScrollFinished(new EventHandler<ScrollEvent>() { @Override public void handle(ScrollEvent event) { dec(rect); log("矩形:滚动结束事件"); event.consume(); } });
除了在处理事件中描述的常见处理外,SCROLL
事件通过沿着滚动手势的方向移动对象来进行处理。如果滚动手势结束时超出窗口范围,形状将移出窗口。矩形的处理程序会忽略基于惯性生成的SCROLL
事件。椭圆的处理程序会继续根据由惯性生成的SCROLL
事件移动椭圆,即使手势在窗口内结束,椭圆也可能移出窗口。
处理缩放事件
当执行缩放手势时,会生成ZOOM_STARTED
、ZOOM
和ZOOM_FINISHED
事件。在手势事件示例中,示例5-3显示了矩形处理缩放事件的处理程序。椭圆的处理程序类似。
示例5-3 定义缩放事件的处理程序
rect.setOnZoom(new EventHandler<ZoomEvent>() { @Override public void handle(ZoomEvent event) { rect.setScaleX(rect.getScaleX() * event.getZoomFactor()); rect.setScaleY(rect.getScaleY() * event.getZoomFactor()); log("矩形:缩放事件" + ",惯性:" + event.isInertia() + ",直接:" + event.isDirect()); event.consume(); } }); rect.setOnZoomStarted(new EventHandler<ZoomEvent>() { @Override public void handle(ZoomEvent event) { inc(rect); log("矩形:缩放事件开始"); event.consume(); } }); rect.setOnZoomFinished(new EventHandler<ZoomEvent>() { @Override public void handle(ZoomEvent event) { dec(rect); log("矩形:缩放事件结束"); event.consume(); } });
除了在处理事件中描述的常规处理外,ZOOM
事件通过根据手势的移动来缩放对象进行处理。矩形和椭圆的处理程序处理所有ZOOM
事件都是相同的,无论惯性或事件的来源如何。
处理旋转事件
当执行旋转手势时,会生成ROTATE_STARTED
、ROTATE
和ROTATE_FINISHED
事件。在手势事件示例中,示例5-4显示了矩形处理旋转事件的处理程序。椭圆的处理程序类似。
示例5-4 定义旋转事件的处理程序
rect.setOnRotate(new EventHandler<RotateEvent>() { @Override public void handle(RotateEvent event) { rect.setRotate(rect.getRotate() + event.getAngle()); log("矩形:旋转事件" + ",惯性:" + event.isInertia() + ",直接:" + event.isDirect()); event.consume(); } }); rect.setOnRotationStarted(new EventHandler<RotateEvent>() { @Override public void handle(RotateEvent event) { inc(rect); log("矩形:旋转事件开始"); event.consume(); } }); rect.setOnRotationFinished(new EventHandler<RotateEvent>() { @Override public void handle(RotateEvent event) { dec(rect); log("矩形:旋转事件结束"); event.consume(); } });
除了在处理事件中描述的常规处理外,ROTATE
事件通过根据手势的移动来旋转对象进行处理。矩形和椭圆的处理程序处理所有ROTATE
事件都是相同的,无论惯性或事件的来源如何。
处理鼠标事件
鼠标事件是由鼠标操作和触摸屏上的触摸引发的。在手势事件示例中,示例5-7显示了椭圆对MOUSE_PRESSED
和MOUSE_RELEASED
事件的处理程序。
示例5-7 定义鼠标事件的处理程序
oval.setOnMousePressed(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { if (event.isSynthesized()) { log("椭圆:来自触摸的鼠标按下事件" + ",合成的:" + event.isSynthesized()); } event.consume(); } }); oval.setOnMouseReleased(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { if (event.isSynthesized()) { log("椭圆:来自触摸的鼠标释放事件" + ",合成的:" + event.isSynthesized()); } event.consume(); } });
只有当触摸屏上的触摸引发鼠标按下和鼠标释放事件时,椭圆才会处理这些事件。矩形的处理程序记录所有鼠标按下和鼠标释放事件。
管理日志
手势事件示例显示了屏幕上形状处理的事件日志。使用ObservableList对象记录每个形状的事件,并使用ListView对象显示事件列表。日志限制为50个条目。最新的条目添加到列表的顶部,最旧的条目从底部删除。有关管理日志的代码,请参见GestureEvents.java
。
在应用程序中使用形状,并注意每个手势生成的事件。