setOnMouseEntered 不适用于 ListView 中的 ImageView

setOnMouseEntered not Working for ImageView in ListView

我对 ImageView 的 JavaFx 上的 setOnMouseEntered 有疑问。 我正在尝试使用 ColorAdjust 更改放置在 Listview 中的 ImageView 的亮度。效果本身适用于不在 ListView 中的 ImageView。

我猜只有 ListView 触发 setOnMouseEntered,而不是 ImageViews,这是我的目标。 同样的问题是ImageViews的悬停效果,在ListView中不会触发。

fxml:

 <ListView fx:id="cardsView" />

java-代码:

@FXML private ListView<ImageView> cardsView;
private ObservableMap<ImageView, Card> hCards;

@FXML
public void initialize() {
  hCards= FXCollections.observableHashMap();
  cardsView.getItems().setAll(hCards.keySet());
  hCards.addListener(
    (MapChangeListener<ImageView, Card>)
        change -> {
          cardsView.getItems().removeAll(change.getKey());
          if (change.wasAdded()) {
            cardsView.getItems().add(change.getKey());
          }
        });
  }

稍后,将为这些 ImageView 中的每一个添加:

private void addLightEffectOnMouseEntered(ImageView imageView) {
  imageView.setOnMouseEntered(
    t -> {
      ColorAdjust colorAdjust = new ColorAdjust();
      colorAdjust.setBrightness(0.4);
      imageView.setEffect(colorAdjust);
    });
}

在调试时,我发现 css 和 setOnMouseEntered 之类的内容已正确添加。所以它似乎以某种方式被 ListView 阻止,ChildNodes 获得 setOnMouseEntered 或 Hover 效果而不是 ListView

您的问题与这个问题基本相同:Adding EventHandler To ImageView contained in a Label。所有 Cell 专业化,包括 ListCell,都继承自 Labeled,它们所有的默认皮肤都继承自 LabeledSkinBase,这就是问题的根源。当 ImageView 用作 Labeled 的图形时,作为对错误的修复(请参阅其他问答),它被设置为鼠标透明。由于 ImageView 是鼠标透明的,因此您的 MOUSE_ENTERED 处理程序永远不会被调用,原因很明显。

如果您不知道,ListView returns 的默认单元工厂是一个 ListCell 实现,当项目是 Node 的实例时,设置单元格的 graphic 到项目。一个简单的解决方法是使用您自己的 ListCell 实现,将 ImageView 包装在另一个节点中,例如 Pane。这是一个例子:

listView.setCellFactory(lv -> new ListCell<>() {

  private final Pane imageViewContainer = new Pane();

  @Override
  protected void updateItem(ImageView item, boolean empty) {
    super.updateItem(item, empty);
    if (empty || item == null) {
      imageViewContainer.getChildren().clear();
      setGraphic(null);
    } else {
      imageViewContainer.getChildren().setAll(item);
      setGraphic(imageViewContainer);
    }
  }
});

这将防止 ImageView 变为鼠标透明。


附带说明一下,使用 GUI 对象(例如 ImageView)作为 ListView(或任何其他虚拟化控件)的模型项通常不是一个好主意。在这种情况下,这可能是一个更糟糕的想法,因为这种设置鼓励同时将与您的应用程序相关的每个 Image 保存在内存中。根据图像的数量以及这些图像的大小,这很容易导致 OutOfMemoryError 或至少消耗用户不必要的 RAM。

您可能需要考虑使用 Card 作为模型项并结合内存受限的 Image 对象缓存(请参阅 WeakReference / SoftReference,但您也可以寻找第三个-方缓存库)。 Card class 可以保存其关联图像的位置,或者缓存可以根据 Card.

的状态导出位置

您仍然会使用 ImageView 作为 ListCell 的图形,但是,您仍然需要使用上述解决方法。使用内存受限缓存的好处是,如果 Card 未显示在 ListCell 中,则其关联的 Image 可能符合垃圾收集条件,从而减少内存您的应用需求。

缓存还允许您在应用程序的任何地方使用相同的 Image(相同的 Image 可以在多个 ImageView 之间共享),这意味着您不必总是在需要特定的 Image 时加载一个新的 Image (因为在请求时它可能仍在内存中)。换句话说,任何缓存提供的典型功能。