AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题

问题[javafx](coding)

Martin Hope
Meltryllis
Asked: 2025-02-18 22:15:41 +0800 CST

JavaFX中添加新节点后,只能获取旧坐标

  • 6

这是一个显示我的问题的示例代码:

public class TestApplication extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox box = new VBox();
        Button button = new Button("Add a Label at top.");
        button.setOnAction(event -> {
            box.getChildren().addFirst(new Label("Label"));
            // Here I want to compute something about coordinate
            Platform.runLater(() -> System.out.println(button.getBoundsInParent().getMinY()));
        });
        box.getChildren().add(button);
        box.setPrefSize(500, 500);
        Scene scene = new Scene(box);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

点击按钮后,窗口的布局如下:

在此处输入图片描述

然后我想要得到button.getBoundsInParent().getMinY()。

我认为这minY等于标签的高度(这也是我想要得到的)。

但是结果却是-1.399999976158142(不知道为什么不是0,不过和我这次的问题没关系)。

我想问的是:

为什么只能获取到添加标签前的坐标,如何才能获取到 位置的正确坐标System.out.println。

提前感谢您的帮助!🙏

更新:

我已阅读评论。我想要实现的功能是滚动以使节点可见。

button在我的程序中,和之间有很多层容器ScrollPane。所以我尝试编写一个静态方法来处理这个任务。

它如下所示:

public class TestApplication extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox box = new VBox();
        Button button = new Button("Add a Label at top.");
        button.setOnAction(event -> {
            box.getChildren().addFirst(new Label("Label"));
            Platform.runLater(() -> scrollVerticalToVisible(button));
        });
        box.getChildren().add(button);
        box.setPrefSize(500, 500);
        Scene scene = new Scene(new ScrollPane(box));
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void scrollVerticalToVisible(Node node) {
        Bounds bounds = node.getBoundsInParent();
        Node container = node;
        while (container != null && !(container instanceof ScrollPane)) {
            bounds = container.localToParent(bounds);
            container = container.getParent();
        }
        if (container == null) {
            return;
        }
        ScrollPane scrollPane = (ScrollPane) container;
        double height = scrollPane.getContent().getBoundsInLocal().getHeight();
        // Same Issue, I cannot get correct y
        double y = bounds.getMaxY() - scrollPane.getViewportBounds().getMinY();
        double viewHeight = scrollPane.getViewportBounds().getHeight();
        System.out.println(y + " " + viewHeight + " " + height);

        scrollPane.setVvalue((y - viewHeight) / (height - viewHeight));
    }

    public static void main(String[] args) {
        launch(args);
    }
}
javafx
  • 1 个回答
  • 56 Views
Martin Hope
AslanPAPA
Asked: 2025-01-23 00:27:12 +0800 CST

JavaFX(setWidth 和 setHeight)中尺寸以什么单位来测量?

  • 8

我正在使用 JavaFX 创建应用程序。在我的代码中,我使用 setWidth() 和 setHeight() 方法设置窗口或元素的大小。例如:

stage.setWidth(400);
stage.setHeight(400);

传递给这些方法的值使用什么单位?它们是像素还是其他单位?如果它们是像素,官方文档或其他可靠来源中哪里提到了这一点?

我在 JavaFX 代码中使用了 setWidth() 和 setHeight() 方法,并期望以像素为单位设置大小。但是,我在官方文档中找不到确认单位的明确声明。我正在寻找权威来源来验证单位确实是像素还是其他单位。

javafx
  • 1 个回答
  • 34 Views
Martin Hope
r-uu
Asked: 2025-01-07 04:59:39 +0800 CST

无需 CSS 即可设置 JavaFX 组件的样式

  • 6

我经常想对 JavaFX 组件进行一些简单的样式设置,例如更改树形表单元格的背景。我找不到其他替代方案

cell.setStyle("-fx-background-color: blue");

对于同一个表格单元格,我也想改变文本的颜色:

cell.setTextFill(Color.WHITE);

我认为编写第二个方法调用比摆弄字符串更不容易出错setStyle("???"),更不用说使用.css文件进行样式设置了。@jewelsea 告诉我我这里漏掉了一些东西。有人能给我提示吗?

javafx
  • 1 个回答
  • 50 Views
Martin Hope
TheOrnithologist
Asked: 2024-12-13 03:47:21 +0800 CST

如何一致地规定嵌套 JavaFX 控件的大小?

  • 5

我正在努力培养我的编程技能,并使用 JavaFX 构建 UI 应用程序,因为 Java 是我最擅长的语言。

不幸的是,我发现使用 FXML 构建 UI 非常繁琐。我决定将窗口设置为固定大小,因为我认为第一次构建无响应 UI 会更容易。

添加元素很容易,但要让它们调整到合适的大小却非常困难,尤其是当布局嵌套时(例如,TiledPane 位于 HBox 中)。我想我误解了子元素如何从父元素继承属性,反之亦然。

在我的应用程序中,我经常嵌套,因为提供给视图的数据可能需要几秒钟才能加载,并且我不想给用户带来频繁的加载屏幕的负担。

例如,这是根据您按下的按钮显示数据的屏幕的 FXML。我的窗口是 1600x900。我希望第二个 HBox 中的元素填满屏幕的其余部分,但我似乎无法调整它们。我还想调整其他元素的大小,但我认为一旦我理解了我所缺少的任何原则,这将变得更容易。

<VBox fx:id="detachmentView" visible="false">
                <HBox alignment="CENTER">
                    <Button fx:id="detachmentRules" text="Rules" styleClass="medium_button" onAction="#detachmentRulesButton"/>
                    <Button fx:id="detachmentStratagems" text="Stratagems" styleClass="medium_button" onAction="#detachmentStratagemButton"/>
                    <Button fx:id="detachmentEnhancements" text="Enhancements" styleClass="medium_button" onAction="#detachmentEnhancementButton"/>
                </HBox>
                <Separator/>
                <HBox>
                    <HBox maxWidth="666">
                        <VBox>
                            <Label text="Detachment Name:" styleClass="detachment_header"/>
                            <ListView fx:id="detachmentList" onMouseClicked="#detachmentListSelect" styleClass="detachment_list" prefWidth="333"/>
                        </VBox>
                        <VBox fx:id="stratagemHeader" visible="false" managed="false">
                            <Label text="Stratagem Name:"  styleClass="detachment_header"/>
                            <ListView fx:id="stratagemList" onMouseClicked="#stratagemListSelect" styleClass="detachment_list" prefWidth="333"/>
                        </VBox>
                        <VBox fx:id="enhancementHeader" visible="false" managed="false">
                            <Label text="Enhancement Name:" styleClass="detachment_header"/>
                            <ListView fx:id="enhancementList" onMouseClicked="#enhancementListSelect" styleClass="detachment_list" prefWidth="333"/>
                        </VBox>
                    </HBox>
                    <StackPane>
                        <StackPane fx:id="detachmentRulesView">
                            <VBox>
                                <Label fx:id="detachmentLabel" styleClass="detachment_header_big"/>
                                <VBox fx:id="dynamicVBox"/>
                            </VBox>
                        </StackPane>
                        <StackPane fx:id="detachmentStratagemView" visible="false">
                            <BorderPane>
                                <center>
                                    <VBox>
                                        <Label fx:id="stratagemID" styleClass="detachment_header"/>
                                        <Label fx:id="stratagemType" styleClass="detachment_header"/>
                                        <Label fx:id="stratagemCost" styleClass="detachment_header"/>
                                        <Label fx:id="stratagemPhase" styleClass="detachment_header"/>
                                        <WebView fx:id="stratagemRules" style="-fx-pref-height: 250;"/>
                                        <TextArea fx:id="stratagemLegend" wrapText="true" editable="false"/>
                                    </VBox>
                                </center>
                            </BorderPane>
                        </StackPane>
                        <StackPane fx:id="enhancementView" visible="false">
                            <BorderPane>
                                <center>
                                    <VBox>
                                        <Label fx:id="enhancementName" styleClass="detachment_header"/>
                                        <Label fx:id="enhancementCost" styleClass="detachment_header"/>
                                        <WebView fx:id="enhancementRules" style="-fx-pref-height: 250;"/>
                                        <TextArea fx:id="enhancementLegend" wrapText="true" editable="false"/>
                                    </VBox>
                                </center>
                            </BorderPane>
                        </StackPane>
                    </StackPane>
                </HBox>
            </VBox>

它产生如下结果: FXML 元素大小调整

我已阅读 JavaFX 文档、多个专门介绍 JavaFX 的网站、本论坛和 ChatGPT。我尝试在许多不同的父元素和子元素上为相关元素添加各种修饰符,如 prefHeight、maxHeight、minHeight、VBox.vgrow,但均无济于事。我在其他地方使用过这些修饰符,但效果不错,因此我不确定我做错了什么。

这也完全可能是构建 UI 的一种糟糕方式,我需要重构。

javafx
  • 1 个回答
  • 26 Views
Martin Hope
MuseumPiece
Asked: 2024-11-28 13:28:46 +0800 CST

Javafx Scene 使用工具提示时获取节点

  • 5

我有一些代码可以根据 TreeTableView 中的 TreeTableRow 生成二维码。我使用以下代码在树表上附加了一个工具提示。工具提示运行良好,将鼠标悬停在行上时将显示“Hello World”二维码。但是,图像需要动态生成。

img 使用 io.nayuki.qrcodegen 生成如下,愿它们永远存在。



QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM);
BufferedImage img = toImage(qr0, 2, 1, 0xFFFFFF,0x000000); 


Tooltip tp = new Tooltip();
Tooltip.install(treeTableView, tp);
tp.setOnShowing(ev -> {// called just prior to being shown    Point mouse = java.awt.MouseInfo.getPointerInfo().getLocation();
    Point2D local = treeTableView.screenToLocal(mouse.x, mouse.y);
    ImageView iv1 = new ImageView();
    iv1.setImage(SwingFXUtils.toFXImage(img, null));
    tp.setGraphic(iv1);
});

除了谷歌搜索/搜索短语“Javafx 在鼠标下找到节点”之外,没有太多内容。

从那时起,我找到了一种解决方法,即使用行工厂并将当前行存储在全局变量 hoverRow 中。

row.setOnMouseEntered(event->{
    StockBinsTree.this.hoverRow=row;
});
row.setOnMouseExited(event->{
    StockBinsTree.this.hoverRow=null;
});

修改工具提示如下:

tp.setOnShowing(ev -> {// called just prior to being shown    if(hoverRow!=null) {
       StockBin sbItem = hoverRow.getItem();
       QrCode qr0 = QrCode.encodeText(sbItem.toLink(), QrCode.Ecc.HIGH);
       BufferedImage img = toImage(qr0, 2, 2, 0xFFFFFF,0x000000);  // See QrCodeGeneratorDemo       ImageView iv1 = new ImageView();
       iv1.setImage(SwingFXUtils.toFXImage(img, null));
       tp.setGraphic(iv1);
    }
});

有效解决了,但是原来的问题关于如何找到鼠标下的节点仍然是一个谜。

javafx
  • 1 个回答
  • 30 Views
Martin Hope
ApparentlyJory
Asked: 2024-11-06 05:00:56 +0800 CST

在 JavaFX 中创建圆形笔刷

  • 5

我目前正在使用 JavaFX 创建绘画应用程序。我目前有一个绘画函数(称为 squiggle),它从鼠标事件中获取一个点数组并创建连接每个点的线段。当我只处理 1px 线段时,这起初还不错。后来我使用 实现了一个笔触宽度变换器GraphicsContent.setStroke(),它突出了线段自然呈矩形的事实。这会导致线条(尤其是在使用较粗的笔触宽度时)变得非常模糊且通常很奇怪。

我的想法是用这些点创建一个圆圈阵列,使其具有更真实的画笔感觉,但是这并不奏效,因为这些圆圈没有连接并且在每个点之间留下了很大的间隙。

当我尝试用线段连接每个圆时,它只会回到与之前相同的问题,因为矩形通常会遮住圆的阴影。

我也尝试过使用路径,这是我以前从未尝试过的。这只会让我回到原来的问题,但现在我在绘图时遇到了更多的延迟。这可能是因为我的实现方式错误,但目前,我甚至不确定路径是否会对我有帮助。

这是我toPath在 Squiggle 类中的方法:

public Path toPath(){
    Path path = new Path();
    path.setStroke(this.getColor());
    path.setStrokeWidth(this.getStrokeWidth());
    path.setStrokeLineCap(StrokeLineCap.ROUND);

    if (this.points.isEmpty()) {
        return path;
    }

    Point start = this.points.getFirst();
    path.getElements().add(new MoveTo(start.x, start.y));

    for(int i = 1; i < this.points.size(); i++){
        Point point = this.points.get(i);
        path.getElements().add(new LineTo(point.x, point.y));
    }
    return path;
}

这是我的绘画面板中的更新方法:

GraphicsContext g2d = this.getGraphicsContext2D();

for(Squiggle sq: squiggles){
    Path path = sq.toPath();
    if (!path.getElements().isEmpty()){
        for (var element : path.getElements()) {
            if (element instanceof MoveTo moveTo) {
                g2d.moveTo(moveTo.getX(), moveTo.getY());
            } else if (element instanceof LineTo lineTo) {
                g2d.lineTo(lineTo.getX(), lineTo.getY());
            }
        }
    
        g2d.stroke();
    }
}

我做错了什么吗?或者我需要尝试别的吗?

javafx
  • 1 个回答
  • 34 Views
Martin Hope
Adalberto José Brasaca
Asked: 2024-10-29 00:14:56 +0800 CST

JavaFX - 相同组件层次结构中两个 GridPane 的不同行为

  • 7

我认为这两个属性的定义是相同的。在“correct.fxml”网格中,如果我添加 1 行或 1 列,则会出现相应的 ScrollPane 滚动条。在“wrong.fxml”网格中不会发生这种情况,我不明白为什么。也许我没有看到它们之间的区别。感谢您的帮助。

正确.fxml

在此处输入图片描述

错误.fxml

在此处输入图片描述

正确.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.StackPane?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="429.0" 
prefWidth="761.0" stylesheets="@stylesheet.css" xmlns="http://javafx.com/javafx/22" 
xmlns:fx="http://javafx.com/fxml/1">
   <center>
      <ScrollPane BorderPane.alignment="CENTER">
         <content>
            <StackPane>
               <children>
                  <Pane StackPane.alignment="CENTER">
                     <children>
                        <GridPane alignment="CENTER" hgap="1.0" vgap="1.0">
                          <columnConstraints>
                            <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                            <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                             <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="100.0" />
                          </columnConstraints>
                          <rowConstraints>
                            <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                          </rowConstraints>
                        </GridPane>
                     </children>
                  </Pane>
               </children>
            </StackPane>
         </content>
      </ScrollPane>
   </center>
</BorderPane>

错误.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.StackPane?>

<BorderPane prefHeight="277.0" prefWidth="495.0" xmlns="http://javafx.com/javafx/22" 
xmlns:fx="http://javafx.com/fxml/1" 
fx:controller="br.com.ablogic.crossword.MainViewController">
   <center>
      <ScrollPane fx:id="scpGrid" fitToHeight="true" fitToWidth="true" 
focusTraversable="false" BorderPane.alignment="CENTER">
         <content>
            <StackPane fx:id="stkPane">
               <children>
                  <Pane fx:id="pane" StackPane.alignment="CENTER">
                     <children>
                        <GridPane alignment="CENTER" hgap="1.0" vgap="1.0">
                          <columnConstraints>
                            <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                              <ColumnConstraints halignment="CENTER" hgrow="NEVER" 
maxWidth="40.0" minWidth="40.0" prefWidth="40.0" />
                          </columnConstraints>
                          <rowConstraints>
                            <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                            <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                            <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                              <RowConstraints maxHeight="40.0" minHeight="40.0" 
prefHeight="40.0" valignment="CENTER" vgrow="NEVER" />
                          </rowConstraints>
                        </GridPane>
                     </children>
                 </Pane>
              </children>
           </StackPane>
        </content>
     </ScrollPane>
  </center>
</BorderPane>
javafx
  • 1 个回答
  • 33 Views
Martin Hope
tretonis
Asked: 2024-10-07 21:52:20 +0800 CST

JavaFX TreeTableView,失去焦点后选择栏显示默认颜色

  • 6

当自定义 TreeTableView 内的单元格然后选择任意行时,条形图的颜色采用默认的 modena 样式,到目前为止它是正确的。

失去焦点后选择栏的文本不再是默认的黑色

如何在 JavaFX 中解决这个问题,有没有优雅的解决方案?

我试过

tableRowProperty().flatMap(TreeTableRow::focusedProperty).addListener((obs, wasSelected, isNowSelected) -> updateTextFill());

但失去焦点时它也无法正常工作

在此处输入图片描述

符合预期

在此处输入图片描述

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView; 
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Callback;

public class SelectionBarColorFocus extends Application {

    BorderPane bp = new BorderPane();
    
    @Override
    public void start(Stage primaryStage) {
         TreeTableView<Person> treeTableView = new TreeTableView<>();

        // Criar colunas
        TreeTableColumn<Person, String> nameColumn = new TreeTableColumn<>("Name");
        nameColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().nameProperty());
         
        
        nameColumn.setCellFactory((TreeTableColumn<Person, String> param) -> {

            TreeTableCell<Person, String> cell = new TreeTableCell<Person, String>() {
        
                   final ImageView imv = new ImageView();
                   final Text text = new Text("");
                   
                   HBox hbox = new HBox(imv, text);
                    
                   {
                      tableRowProperty().flatMap(TreeTableRow::selectedProperty).addListener((obs, wasSelected, isNowSelected) -> updateTextFill());                       
                      // tableRowProperty().flatMap(TreeTableRow::focusedProperty).addListener((obs, wasSelected, isNowSelected) -> updateTextFill());                       
                   }
                  
                    
                    @Override
                    public void updateItem(String item, boolean empty) {
                        
                        super.updateItem(item, empty);
                        if (item != null) {
                            text.setFill(Color.ORANGE);
                            text.setText(item);
                            setGraphic(hbox);
                            
                        }else {                            
                            text.setText("");
                            setGraphic(null);
                        }
                    }
                    
                    private void updateTextFill() {
                         text.setFill(getTableRow().isSelected() ? Color.WHITE: Color.ORANGE);
                    }   
                };

                return cell;
            }
        );

        
        
        TreeTableColumn<Person, String> addressColumn = new TreeTableColumn<>("Address");
        addressColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().addressProperty());



        
        TreeTableColumn<Person, Number> ageColumn = new TreeTableColumn<>("Age");
        ageColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().ageProperty());
        
        ageColumn.setCellFactory((TreeTableColumn<Person, Number> param) -> {
            TreeTableCell<Person, Number> cell = new TreeTableCell<Person, Number>() {
                
                boolean flat = false;
                final Text text = new Text("");
                
                {
                  focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
                          System.out.println("jorge focused="+isNowFocused);
                      }
                  );   
                }
                
                @Override
                public void updateItem(Number item, boolean empty) {
                    super.updateItem(item, empty);
                    
                    if(flat == false) {                         
                        tableRowProperty().flatMap(TreeTableRow::selectedProperty).addListener((obs, wasSelected, isNowSelected) -> updateTextFill(item, empty));                        
                        flat = true;
                    }                    
                    updateTextFill(item, empty);
                }
                
            
                private void updateTextFill(Number item, boolean empty) {                    
                    if(item != null && item.intValue() != -1) {
                            text.setFill(Color.BLUE);                          
                            text.setText(item.toString());
                            setGraphic(text);
                        
                    }else {
                        text.setText("");
                        setText("");
                        setGraphic(null);
                    }
                    text.setFill(getTableRow().isSelected() ? Color.WHITE: Color.BLUE);
                }    
                
            };
            
            return cell;
         });
        
        nameColumn.setPrefWidth(150);
        addressColumn.setPrefWidth(200);
        ageColumn.setPrefWidth(100);
    
        treeTableView.getColumns().addAll(nameColumn, addressColumn, ageColumn);

        TreeItem<Person> rootItem = new TreeItem<>(new Person("Persons", "", -1));
        rootItem.getChildren().addAll(
                new TreeItem<>(new Person("Alice", "456 Park Ave", 25)),
                new TreeItem<>(new Person("Bob", "789 Broadway", 35)),
                new TreeItem<>(new Person("Carol", "987 Elm St", 40)),
                new TreeItem<>(new Person("John", "123 Main St", 40))
        );

        rootItem.setExpanded(true);
        treeTableView.setRoot(rootItem);
        
        
        Button b = new Button("Row Lost Focus");
        
        StackPane st = new StackPane();
        st.getChildren().add(b);
        
        bp.setCenter(treeTableView);
        bp.setBottom(st);

        Scene scene = new Scene(bp, 500, 300);
        primaryStage.setScene(scene);
        primaryStage.setTitle("JavaFX TreeTableView SelectionBar Lost Focus");
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
    
    
    
    public static class Person {
        private StringProperty name;
        private StringProperty address;
        private IntegerProperty age;

        public Person(String name, String address, int age) {
            this.name = new SimpleStringProperty(name);
            this.address = new SimpleStringProperty(address);
            this.age = new SimpleIntegerProperty(age);
        }

        public StringProperty nameProperty() {
            return name;
        }

        public StringProperty addressProperty() {
            return address;
        }

        public IntegerProperty ageProperty() {
            return age;
        }
    }
}
javafx
  • 1 个回答
  • 43 Views
Martin Hope
David Tonhofer
Asked: 2024-10-06 22:20:30 +0800 CST

JavaFX 事件发送到按钮控件:为什么有两个 MOUSE_ENTERED / MOUSE_EXITED 事件?

  • 9

我对根据鼠标指针发生的情况发送到 JavaFX按钮控件的事件进行了一些实验,并从中生成了一个分层状态机图(近似,因为我的编辑器没有任何语义概念):

这一切都非常简单,只需从起始状态跟踪行为并查看会发生什么。这里有一个微妙之处,因为按钮控件内的标签也起着作用,它会生成MOUSE_ENTERED_TARGET事件MOUSE_EXITED_TARGET(我向 ChatGPT 询问了这个问题,它打印了一个非常漂亮的答案,但答案是错的😂)

按钮控制分层状态机

我唯一想知道的一点是——为什么我会持续收到两件MOUSE_ENTERED或两个MOUSE_EXITED事件,一个带有consumed = false,一个带有consumed = true?

示例代码

以下是(接近最小的)示例。它只是创建了一个按钮,我们可以手动点击它来查看生成了哪些事件:

文件com.example.stack_overflow.Main.java

package com.example.stack_overflow;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.util.Objects;

class PaneBuilder {

    private final StackPane stackPane;

    private static void log(String text) {
        System.out.println(text);
    }

    public PaneBuilder() {
        stackPane = buildCenteringStackPaneAroundButton(buildButton());
    }

    private static Button buildButton() {
        final var button = new Button("Click Me!");
        button.setMinWidth(120); // I think these are "120pt", why is there no "unit"?
        final var desc = "Button";
        button.addEventHandler(ActionEvent.ACTION, event -> {
            handleActionEvent(desc, event);
        });
        button.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
            handleMouseEvent(desc, event);
        });
        button.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> {
            handleMouseEvent(desc, event);
        });
        button.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
            handleMouseEvent(desc, event);
        });
        button.addEventHandler(MouseEvent.MOUSE_ENTERED, event -> {
            handleMouseEvent(desc, event);
        });
        button.addEventHandler(MouseEvent.MOUSE_EXITED, event -> {
            handleMouseEvent(desc, event);
        });
        button.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, event -> {
            handleMouseEvent(desc, event);
        });
        button.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, event -> {
            handleMouseEvent(desc, event);
        });
        button.armedProperty().addListener((obs, oldVal, newVal) -> log(desc + ": armed status changes: " + oldVal + " -> " + newVal));
        return button;
    }

    public Pane getPane() {
        return stackPane;
    }

    private static StackPane buildCenteringStackPaneAroundButton(Button button) {
        final var stackPane = new StackPane();
        stackPane.setAlignment(Pos.CENTER);
        final var hbox = buildHBoxAroundVBoxAroundButton(button);
        final var insets = new Insets(20, 20, 20, 20); // I guess those are "20points"
        StackPane.setMargin(hbox, insets);
        stackPane.getChildren().add(hbox);
        return stackPane;
    }

    private static HBox buildHBoxAroundVBoxAroundButton(Button button) {
        final var hbox = new HBox();
        hbox.setAlignment(Pos.CENTER);
        hbox.getChildren().add(buildVBoxAroundButton(button));
        return hbox;
    }

    private static VBox buildVBoxAroundButton(Button button) {
        final var vbox = new VBox();
        vbox.setAlignment(Pos.CENTER);
        vbox.getChildren().add(button);
        return vbox;
    }

    private static void appendEventDesc(StringBuilder buf, Event event) {
        buf.append("\n   Type          : " + event.getEventType());
        buf.append("\n   Class         : " + event.getClass().getName());
        buf.append("\n   Consumed      : " + event.isConsumed());
        buf.append("\n   Source class  : " + event.getSource().getClass().getName());
        buf.append("\n   Event         : " + Objects.toIdentityString(event));
    }

    public static void handleMouseEvent(String desc, MouseEvent event) {
        final var buf = new StringBuilder("Mouse event in " + desc);
        appendEventDesc(buf, event);
        log(buf.toString());
    }

    public static void handleActionEvent(String desc, ActionEvent event) {
        final var buf = new StringBuilder("Action event in " + desc);
        appendEventDesc(buf, event);
        log(buf.toString());
    }
}

public class Main extends Application {

    @Override
    public void start(Stage stage) {
        stage.setTitle("Button Example");
        stage.setScene(new Scene(new PaneBuilder().getPane()));
        stage.sizeToScene();
        stage.show();
    }

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

}

以及构建和运行上述程序的 POM。该程序必须使用JavaFX Maven 插件javafx:run的Maven 目标运行

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>stack_overflow</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- RUNNING VIA MAVEN + JAVAFX PLUGIN IN IDE: -->
    <!--   'Main Menu' > 'Run' > 'Run Maven Goal' > 'Plugins' > 'JavaFx Maven Plugin' > 'javafx:run' -->
    <!--   This will invoke the goal "javafx:run" of the "javafx-maven-plugin". -->
    <!--   With the 'Run New Maven Goal' menu entry, you can define that goal, and it will appear in the -->
    <!--   context menu as 'right-mouse-button menu' > 'run maven' > 'javafx:run' -->

    <!-- RUNNING VIA MAVEN IN COMMAND LINE w/o leaving the IDE (no need for OpenJDX SDK): -->
    <!-- Right-click on the project window and select "Open Terminal at the current Maven module path". -->
    <!-- Enter the command "mvn javafx:run" or for debugging output "mvn -X javafx:run". -->
    <!-- This will invoke the goal "javafx:run" of the "javafx-maven-plugin". -->

    <properties>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!-- This is the latest OpenJavaFX version on 2024-09-26 (version of 2024-09-16) -->
        <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-controls -->

        <javafx.version>23</javafx.version>

        <javafx.plugin.version>0.0.8</javafx.plugin.version>
        <compiler.plugin.version>3.13.0</compiler.plugin.version>
        <exec.plugin.version>3.4.1</exec.plugin.version>
        <dependency.plugin.version>3.8.0</dependency.plugin.version>
        <main.class>com.example.stack_overflow.Main</main.class>

        <java.compiler.source.version>21</java.compiler.source.version>
        <java.compiler.target.version>21</java.compiler.target.version>

    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-controls -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-fxml -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>

            <!-- Standard Java Compiler Plugin -->
            <!-- https://maven.apache.org/plugins/maven-compiler-plugin/ -->
            <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-compiler-plugin -->

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${compiler.plugin.version}</version>
                <configuration>
                    <source>${java.compiler.source.version}</source>
                    <target>${java.compiler.target.version}</target>
                </configuration>
            </plugin>

            <!-- Special Plugin to run JavaFX programs -->
            <!-- https://github.com/openjfx/javafx-maven-plugin -->
            <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-maven-plugin -->
            <!-- If you run the plugin's goal on the command line with the '-X' option like so: -->
            <!-- mvn -X javafx:run -->
            <!-- you will see the command line the plugin builds, which looks as follows, with ++ replaced by double dash -->

            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>${javafx.plugin.version}</version>
                <configuration>
                    <mainClass>${main.class}</mainClass>
                    <options>
                        <option>-ea</option>
                    </options>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

运行该程序会出现以下窗口:

在此处输入图片描述

如果我们现在用鼠标指针沿着指示的红色路径移动,然后单击鼠标并再次将鼠标指针移出按钮:

在此处输入图片描述

我们得到了属性值armed和按钮发出的事件的以下变化。请注意双引号MOUSE_ENTERED= consumed,false然后是true。

进入按钮控件:

Mouse event in Button
   Type          : MOUSE_ENTERED
   Class         : javafx.scene.input.MouseEvent
   Consumed      : false
   Source class  : javafx.scene.control.Button
   Event         : javafx.scene.input.MouseEvent@2ebee20a
Mouse event in Button
   Type          : MOUSE_ENTERED
   Class         : javafx.scene.input.MouseEvent
   Consumed      : true
   Source class  : javafx.scene.control.Button
   Event         : javafx.scene.input.MouseEvent@2ebee20a

输入按钮控件的标签:

Mouse event in Button
   Type          : MOUSE_ENTERED_TARGET
   Class         : javafx.scene.input.MouseEvent
   Consumed      : false
   Source class  : javafx.scene.control.Button
   Event         : javafx.scene.input.MouseEvent@5d41df9f

单击,注意状态的变化armed。

Mouse event in Button
   Type          : MOUSE_PRESSED
   Class         : javafx.scene.input.MouseEvent
   Consumed      : false
   Source class  : javafx.scene.control.Button
   Event         : javafx.scene.input.MouseEvent@3df998ed
Button: armed status changes: false -> true
Mouse event in Button
   Type          : MOUSE_RELEASED
   Class         : javafx.scene.input.MouseEvent
   Consumed      : false
   Source class  : javafx.scene.control.Button
   Event         : javafx.scene.input.MouseEvent@241a11d5
Action event in Button
   Type          : ACTION
   Class         : javafx.event.ActionEvent
   Consumed      : false
   Source class  : javafx.scene.control.Button
   Event         : javafx.event.ActionEvent@3d8fce01
Button: armed status changes: true -> false
Mouse event in Button
   Type          : MOUSE_CLICKED
   Class         : javafx.scene.input.MouseEvent
   Consumed      : false
   Source class  : javafx.scene.control.Button
   Event         : javafx.scene.input.MouseEvent@505036ef

退出按钮控件的标签:

Mouse event in Button
   Type          : MOUSE_EXITED_TARGET
   Class         : javafx.scene.input.MouseEvent
   Consumed      : false
   Source class  : javafx.scene.control.Button
   Event         : javafx.scene.input.MouseEvent@2d496725

退出按钮控制:

Mouse event in Button
   Type          : MOUSE_EXITED
   Class         : javafx.scene.input.MouseEvent
   Consumed      : false
   Source class  : javafx.scene.control.Button
   Event         : javafx.scene.input.MouseEvent@59a94fe0
Mouse event in Button
   Type          : MOUSE_EXITED
   Class         : javafx.scene.input.MouseEvent
   Consumed      : true
   Source class  : javafx.scene.control.Button
   Event         : javafx.scene.input.MouseEvent@59a94fe0
javafx
  • 2 个回答
  • 54 Views
Martin Hope
HighWard
Asked: 2024-09-05 05:16:18 +0800 CST

JavaFX setTranslate() 与 setLayout()

  • 8

我注意到,对于透视相机和 3D 形状,setTranslate() 可以在它们的 Z 轴上使用,那么这是否意味着我们使用此方法来操纵 3D 平面并使用 setLayout() 来操纵 2D 平面?

两者似乎都在 3D 平面上移动对象的坐标,因此有什么理由不互换使用它们吗(除了可能是不好的做法)?

javafx
  • 1 个回答
  • 52 Views

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve