【问题标题】:Dialog causes drag done events to not propagate对话框导致拖动完成事件不传播
【发布时间】:2017-10-18 16:45:23
【问题描述】:

我在拖放操作期间使用弹出对话框。当放置发生时,会弹出一个对话框,当它被关闭时,事件链应该继续并允许在拖动操作结束时发生一些事情。如果弹出对话框是 FX 则没有问题,但如果是 Gluon 则不会发生拖动完成操作。

这是一个示例代码:

import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;

import com.gluonhq.charm.glisten.mvc.View;

public class MainView extends View {

    HBox root;

    public MainView(String name) {
        super(name);

        Label source = new Label("Source");
        configureDragSource(source);
        Label target = new Label("Target");
        configureDragTarget(target);
        Label popupTarget = new Label("Popup Target");
        configureDragPopupTarget(popupTarget);
        root = new HBox(40, source, target, popupTarget);
        setCenter(root);
    }

    private void configureDragSource(Label source) {
        source.setOnDragDetected(e -> {
            root.setBackground(new Background(new BackgroundFill(Color.RED, null, null)));
            Dragboard db = source.startDragAndDrop(TransferMode.ANY);
            ClipboardContent content = new ClipboardContent();
            content.put(DataFormat.PLAIN_TEXT, source.getText());
            db.setContent(content);
        });
        source.setOnDragDone(e -> root.setBackground(new Background(new BackgroundFill(null, null, null))));
    }

    private void configureDragTarget(Label target) {
        target.setOnDragOver(e -> e.acceptTransferModes(TransferMode.ANY));

    }

    private void configureDragPopupTarget(Label popupTarget) {
        popupTarget.setOnDragOver(e -> e.acceptTransferModes(TransferMode.ANY));
        popupTarget.setOnDragDropped(e -> {
            javafx.scene.control.Alert popup1 = new javafx.scene.control.Alert(AlertType.INFORMATION);
            com.gluonhq.charm.glisten.control.Alert popup2 = new com.gluonhq.charm.glisten.control.Alert(AlertType.INFORMATION);
            popup1.showAndWait();
        });
    }
}

应拖动背景变为红色的源。拖动操作完成后,背景应恢复为默认值。常规放置目标什么都不做,颜色变化有效。但是,当放在弹出目标上时,对话框会出现,当它关闭时,颜色只会改变 FX 对话框,而不会改变胶子对话框。将popup1.showAndWait(); 更改为popup2

如果重要,这是应用程序类

import com.gluonhq.charm.glisten.application.MobileApplication;

public class TestApplication extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> new MainView(HOME_VIEW));
    }

    public static void main(String[] args) {
        launch(args);
    }
}

这是 gradle 构建文件:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.3.5'
    }
}

apply plugin: 'org.javafxports.jfxmobile'
apply plugin: 'eclipse'

jar {
    manifest {
        attributes 'Main-Class': 'com.test.TestApplication'
    }
}

jfxmobile {
    downConfig {
        version = '3.3.0'
        plugins 'display', 'lifecycle', 'statusbar', 'storage'
    }
    android {
        compileSdkVersion = 19
//      manifest = 'src/android/AndroidManifest.xml'
    }
    ios {
        infoPList = file('src/ios/Default-Info.plist')
        forceLinkClasses = [
                'com.gluonhq.**.*',
                'javax.annotations.**.*',
                'javax.inject.**.*',
                'javax.json.**.*',
                'org.glassfish.json.**.*'
        ]
    }
}

eclipse {
    classpath {
        downloadJavadoc = true
        downloadSources = true
    }
}

repositories {
    jcenter()
    maven {
        url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
    }
}

mainClassName = 'com.test.TestApplication'

dependencies {
    compile 'com.gluonhq:charm:4.3.5'
}

task wrapper(type: Wrapper) {
    gradleVersion = '4.2'
}

也发生在 compile 'com.gluonhq:charm:4.3.7' 和 4.4.0 上。

在 Java 8 b141 上运行。为什么会这样?这是一个错误吗?

【问题讨论】:

  • BTW 在对话框 javadoc 页面上,dialog.setContent(new Label("Just a regular dialog, plain and simple"); 行缺少)
  • 使用popup2在桌面上运行时是否看到 NPE?
  • @JoséPereda 否。我还在事件处理程序中添加了 try catch,但没有捕获任何内容。我应该看一个特定的地方吗?
  • 我让它在 Mac 上运行。 DnD 实现是非常特定于平台的。

标签: gluon-mobile


【解决方案1】:

JavaFX 内置对话框和 Gluon 对话框不一样。事实上,后者是从Layer 扩展而来的,同时也是模态的和阻塞的,和前者一样。

在 Mac 上运行发布的代码,我在放弃popupTarget 后得到一个 NPE,可以使用以下方法轻松解决:

Platform.runLater(() -> popup2.showAndWait());

此外,鉴于对话框使用了拖动事件,可能的解决方案是:

Platform.runLater(() -> 
    popup2.showAndWait()
        .ifPresent(r -> 
            root.setBackground(new Background(new BackgroundFill(null, null, null)))));

因此您可以重构setOnDragDone 方法,并创建一个在对话框关闭后也可以调用的方法。

编辑

这些是目标和源在 dnd 事件开始后接收到的拖动事件:

目标通过拖放手势接收这些事件:

 DRAG_OVER
 DRAG_OVER
 ... // until drop is done:
 // show Dialog
 ...
 // hide Dialog
 DRAG_DROPPED
 DRAG_EXITED
 DRAG_EXITED_TARGET

这些与 JavaFX 和 Gluon 对话框的工作方式完全相同(至少在 Windows 上。在 Mac 上,由于 NPE,使用 Platform.runLater() 显然会延迟显示对话框和隐藏对话框事件,但现在让我们只关注 Windows)。

在最后一个事件之后,源收到:

DRAG_DONE

但仅限于 JavaFX 对话框。

经过一些调试,Gluon的Dialog中止drag done事件的原因可以解释如下:

Scene 类有一个 DnDGesture 私有类:

/**
 * A Drag and Drop gesture has a lifespan that lasts from mouse
 * PRESSED event to mouse RELEASED event.
 */
class DnDGesture {
   ...
}

正如评论中所解释的,它具有从鼠标按下事件到鼠标释放事件的生命周期。

使用 JavaFX 内置对话框,这将显示在新舞台中,因此显示在新场景中。这里的关键是这个对话框(以及所有的鼠标事件集)显示在一个新的模态阶段,所以一旦对话框被隐藏,主阶段再次获得焦点并恢复,正确完成鼠标的 dnd 手势发布。

但是使用 Gluon 的对话框,没有第二阶段。一切都发生在同一阶段,一旦释放鼠标,Scene.DnDGesture 变为 null,因此当目标中发生DRAG_EXITED_TARGET 事件时,会进行正确的调用以完成 dnd 过程,但此时 @987654335 @ 为空,并且该调用不再到达源。

我不认为这是一个错误,但更像是一种权衡,因为有很多原因可以避免在移动环境中使用第二阶段/场景并保留视图/图层(在一个阶段中)设计。

【讨论】:

  • 是的,目前我的解决方法是在拖放事件中调用 drag done 事件处理程序,类似于您在第二个示例中所做的,只是没有 runLater,因为我没有收到任何错误。胶子对话框不应该表现得像原生对话框吗?这会被视为错误吗?
  • 我已经编辑了我的答案,进一步解释了正在发生的事情,以及为什么我不认为这是一个错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-16
  • 1970-01-01
  • 2023-02-04
  • 1970-01-01
  • 2018-12-29
  • 2012-02-01
  • 2015-10-05
相关资源
最近更新 更多