javafx 问题整理

记录一下javaFx的使用的问题

这些问题都是在开发rv64仿真工具中遇到的,下图是目前阶段的效果 https://github.com/anons-org/opencar/

Location is not set.

错误写法(或许是老版的写法,未验证)

   FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/setings.fxml"));

换成

URL path = this.getClass().getClassLoader().getResource("fxml/main.fxml"); // 注意路径不带/开头
File file = new File(path.getFile());
root = FXMLLoader.load(file.toURI().toURL());

tableView

tv实现内存查看工具

  • 如下

数据我用map去构造,每行数据一个map,每个map 16个key 分别是0~15 ,这些key对应tv表头的列即可,map的val存一个对象
该对象两个字段一个是原始值 一个是用于显示的值,因为数据需要格式化显示。

效果

相关代码

package com.far.vms.opencar.ui.main.RightTablePanle;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MemoryData {

    /**
                   1  2  3......15
        0x00000010 0e 0a 03......
        0x00000020 0e 0a 03......
     */
    Map<Long,List<Map<String,InnerMemVal>>> memMap = new HashMap<>();
    

    public static class InnerMemVal{

        int val;
        String viewVal;

        public int getVal() {
            return val;
        }

        public void setVal(int val) {
            this.val = val;
        }

        public String getViewVal() {
            return viewVal;
        }

        public void setViewVal(String viewVal) {
            this.viewVal = viewVal;
        }
    }

    public Map<Long, List<Map<String, InnerMemVal>>> getMemMap() {
        return memMap;
    }

    public void setMemMap(Map<Long, List<Map<String, InnerMemVal>>> memMap) {
        this.memMap = memMap;
    }
}

package com.far.vms.opencar.ui.main.RightTablePanle;

import cn.hutool.poi.excel.sax.SheetRidReader;
import javafx.beans.property.SimpleMapProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.MapValueFactory;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.util.Callback;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class TabMemoryInfo {

    TabPane tabMemory;

    TableView tvMemoryGrid;

    ObservableList<Map<String, MemoryData.InnerMemVal>> tvMemoryGridDataWrapper = FXCollections.observableArrayList();

    public TabPane getTabMemory() {
        return tabMemory;
    }

    public void setTabMemory(TabPane tabMemory) {
        this.tabMemory = tabMemory;
    }

    public TableView getTvMemoryGrid() {
        return tvMemoryGrid;
    }

    public void setTvMemoryGrid(TableView tvMemoryGrid) {
        this.tvMemoryGrid = tvMemoryGrid;
    }


    public void initControl() {

        for (int i = 0; i < 15; i++) {
            String colName = String.valueOf(i);
            TableColumn<Map, String> tableColumn = new TableColumn<>(colName);
            //  new MapValueFactory<>(colName);
            tableColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Map, String>, ObservableValue<String>>() {
                @Override
                public ObservableValue<String> call(TableColumn.CellDataFeatures<Map, String> mapStringCellDataFeatures) {

                    MemoryData.InnerMemVal xxx = (MemoryData.InnerMemVal) mapStringCellDataFeatures.getValue().get(tableColumn.getText());

                    ObservableValue<String> s = new SimpleStringProperty(xxx.getViewVal());

                    return s;
                }
            });
            tvMemoryGrid.getColumns().add(tableColumn);
        }
        tvMemoryGrid.setItems(tvMemoryGridDataWrapper);


        Map<String, MemoryData.InnerMemVal> innerMemValMap = new HashMap<>();
        for (int i = 0; i < 15; i++) {
            MemoryData.InnerMemVal memVal = new MemoryData.InnerMemVal();
            memVal.setViewVal("xxx");
            innerMemValMap.put(String.valueOf(i), memVal);
        }

        tvMemoryGridDataWrapper.add(innerMemValMap);


    }

}

每个单元格增加鼠标右键菜单

  • 这个是基于tableview添加菜单

https://blog.csdn.net/weixin_29629231/article/details/112525827

  • 下面是基于每个单元格
package com.far.vms.opencar.ui.main.RightTablePanle;

import com.far.vms.opencar.OpenCarWindos;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.*;
import javafx.scene.layout.AnchorPane;
import javafx.util.Callback;

import java.util.List;

public class RightTablePanle {
    private OpenCarWindos ctx;

    TabPane rightTabPane;

    //当前PC显示
    TextField txtPcVal;
    //通用寄存器
    TableView tvGeneralRegister;

    ObservableList<RegData> tvGeneralRegisterDataWrapper = FXCollections.observableArrayList();


    public OpenCarWindos getCtx() {
        return ctx;
    }

    public RightTablePanle setCtx(OpenCarWindos ctx) {
        this.ctx = ctx;
        return this;
    }

    public TabPane getRightTabPane() {
        return rightTabPane;
    }

    public void setRightTabPane(TabPane rightTabPane) {
        this.rightTabPane = rightTabPane;
    }

    public void initControl() {

        SplitPane splitPane = (SplitPane) ctx.getRootMain().lookup("#spbox");//#ta是textarea的id号

        var scrollPane = splitPane.getItems();
        for (var e : scrollPane) {
            if (e instanceof ScrollPane) {
                ScrollPane scrollPane1 = ((ScrollPane) e);
                rightTabPane = (TabPane) ((AnchorPane) scrollPane1.getContent()).lookup("#tabRightGroup");
                rightTabPane.getTabs().forEach(item -> {
                    if ("tabItemRegInfo".equals(item.getId())) {
                        txtPcVal = (TextField) item.getContent().lookup("#txtPcVal");
                        tvGeneralRegister = (TableView) item.getContent().lookup("#tvGeneralRegister");
                    }
                });
            }
        }
        initGeneralRegisterTv();
    }

    public void initGeneralRegisterTv() {
        //创建表格的列
        TableColumn regName = new TableColumn<>("寄存器");
        TableColumn viewAddr = new TableColumn<>("地址");
        TableColumn viewVal = new TableColumn<>("值");


        viewAddr.setCellFactory(new Callback<TableColumn, TableCell>() {
            @Override
            public TableCell call(TableColumn tableColumn) {
                final TableCell<RegData, String> cell = new TableCell<>();
                //javafx的api非常奇怪,这行代码的作用是 将新的cell的值 和新的cell的值进行绑定
                //简单说 必须有这句,才会把值显示出来
                cell.textProperty().bind(cell.itemProperty());

//                cell.setOnKeyPressed(new EventHandler<KeyEvent>() {
//                    @Override
//                    public void handle(KeyEvent keyEvent) {
//
//                        if (keyEvent.getCode() == KeyCode.C) {
//                            // final Clipboard clipboard = Clipboard.getSystemClipboard();
//                            final ClipboardContent content = new ClipboardContent();
//                            content.putString(cell.getItem());
//                        }
//                    }
//                });

                cell.setOnMouseClicked(new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent mouseEvent) {
                        if (mouseEvent.getButton() == MouseButton.SECONDARY) {
                            //获取选中单元格的值
                            var itemval = cell.getItem();
                            RegData regData = cell.getTableRow().getItem();
                            int xx = 1;
                            //设置菜单
                            //https://blog.csdn.net/qq_26486347/article/details/96600969
                            ContextMenu contextMenu = new ContextMenu();
                            // 菜单项
                            MenuItem hex = new MenuItem("十六进制");
                            hex.setOnAction(new EventHandler<ActionEvent>() {
                                @Override
                                public void handle(ActionEvent event) {
                                    cell.itemProperty().setValue("0x" + Long.toHexString(regData.getAddr()));
                                }
                            });

                            MenuItem bin = new MenuItem("二进制");
                            bin.setOnAction(new EventHandler<ActionEvent>() {
                                @Override
                                public void handle(ActionEvent event) {
                                    cell.itemProperty().setValue("0b" + Long.toBinaryString(regData.getAddr()));
                                }
                            });
                            MenuItem orc = new MenuItem("十进制");
                            orc.setOnAction(new EventHandler<ActionEvent>() {
                                @Override
                                public void handle(ActionEvent event) {
                                    cell.itemProperty().setValue(String.valueOf(regData.getAddr()));
                                }
                            });
                            contextMenu.getItems().addAll(hex, bin, orc);
                            cell.setContextMenu(contextMenu);
                        }
                    }
                });
                return cell;
            }
        });


        viewVal.setCellFactory(new Callback<TableColumn, TableCell>() {
            @Override
            public TableCell call(TableColumn tableColumn) {
                final TableCell<RegData, String> cell = new TableCell<>();
                //javafx的api非常奇怪,这行代码的作用是 将新的cell的值 和新的cell的值进行绑定
                //简单说 必须有这句,才会把值显示出来
                cell.textProperty().bind(cell.itemProperty());

                cell.setOnMouseClicked(new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent mouseEvent) {
                        if (mouseEvent.getButton() == MouseButton.SECONDARY) {
                            //获取选中单元格的值
                            var itemval = cell.getItem();
                            RegData regData = cell.getTableRow().getItem();
                            int xx = 1;
                            //设置菜单
                            //https://blog.csdn.net/qq_26486347/article/details/96600969
                            ContextMenu contextMenu = new ContextMenu();
                            // 菜单项
                            MenuItem hex = new MenuItem("十六进制");
                            hex.setOnAction(new EventHandler<ActionEvent>() {
                                @Override
                                public void handle(ActionEvent event) {
                                    cell.itemProperty().setValue(regData.toHexStr());
                                }
                            });

                            MenuItem bin = new MenuItem("二进制");
                            bin.setOnAction(new EventHandler<ActionEvent>() {
                                @Override
                                public void handle(ActionEvent event) {
                                    cell.itemProperty().setValue(regData.toBinStr());
                                }
                            });
                            MenuItem orc = new MenuItem("十进制");
                            orc.setOnAction(new EventHandler<ActionEvent>() {
                                @Override
                                public void handle(ActionEvent event) {
                                    cell.itemProperty().setValue(String.valueOf(regData.getVal()));
                                }
                            });
                            contextMenu.getItems().addAll(hex, bin, orc);
                            cell.setContextMenu(contextMenu);
                        }
                    }
                });
                return cell;
            }
        });

        //将表格的列和类的属性进行绑定
        regName.setCellValueFactory(new PropertyValueFactory<>("regName"));
        viewAddr.setCellValueFactory(new PropertyValueFactory<>("viewAddr"));
        viewVal.setCellValueFactory(new PropertyValueFactory<>("viewVal"));
        //添加到tableview
        tvGeneralRegister.getColumns().addAll(regName, viewAddr, viewVal);
        tvGeneralRegister.setItems(tvGeneralRegisterDataWrapper);


    }

    public void addDataToGeneralRegisterTv(List<RegData> regData) {
        tvGeneralRegisterDataWrapper.clear();
        regData.forEach(e -> {
            tvGeneralRegisterDataWrapper.add(e);
        });
    }


    public void setPcval(String pc) {
        txtPcVal.setText(pc);
    }

}

增加行点击事件,可拿到每列的数据然后做处理

这个行点击事件的clickCount总数 没有找到办法清零,导致点击数只增不减少,可以采取求模的办法判断点击次数,有知道解决办法的大佬们看到指点一下!

tv.setRowFactory(tview -> {
    TableRow<CodeData> row = new TableRow<>();
    row.setOnMouseClicked(event -> {
        System.out.println(event.getClickCount() % 2 + "次");
        // 点击两次 且 row不为空
        if (event.getClickCount() % 1 == 0 && (!row.isEmpty())) {
            CodeData rowData = row.getItem();
            if (rowData.getCircle().isVisible()) {
                rowData.getCircle().setVisible(false);
            } else {
                rowData.getCircle().setVisible(true);
            }
        }

    });
    return row;
});

连续点击某行无效,换行点击才有效的事件


//        tv.getSelectionModel().getSelectedCells().addListener(new InvalidationListener() {
//            @Override
//            public void invalidated(Observable observable) {
//                ObservableList<TablePosition> tbs = (ObservableList<TablePosition>) observable;
//                tbs.forEach(e -> {
//                    System.out.println("多少次");
//                    if (e.getColumn() == 0) {
//                        Circle data = (Circle) e.getTableColumn().getCellData(e.getRow());
//                        if (data.isVisible()) {
//                            data.setVisible(false);
//                        } else {
//                            data.setVisible(true);
//                        }
//                    }
//                });
//
//            }
//        });


快捷键 javafx

Ctrl+C

KeyCombination ctrl_c = new KeyCodeCombination(KeyCode.C, KeyCombination.CONTROL_DOWN);
window.getScene().getAccelerators().put(ctrl_c, () -> {
   System.out.println("快捷键CTRL + C");
   System.out.println(Thread.currentThread().getName());
});

监听快捷键 javafx

window.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
    //System.out.println("KeyEvent = " + e);
    if (ctrl_c.match(e)) {
        System.out.println("快捷键CTRL + C");
        System.out.println(Thread.currentThread().getName());
    }
});

生成jar问题

  • java.lang.NoClassDefFoundError:javafx/application/Application

  • 开发时用的java17 但是生成javafx的jar包各种问题,无奈降到java11 但javafx 8之后就被移出jdk了。需要单独下载javafx的sdk包
    我开发时javafx的sdk的api用的是17的,但运行jar时sdk用的是16,所以会看到下面的警告信息。。

javafxsdk下载

过期请联系我
链接:https://pan.baidu.com/s/1fES9WodOAtvFuS-2ErM__g?pwd=3v9l
提取码:3v9l

下载了sdk需要jar启动的时候指定位置

./java --module-path D:\AAAA_WORK\java\openjfx\openjfx-16_windows-x64_bin-sdk\javafx-sdk-16\lib --add-modules javafx.controls,javafx.fxml -jar E:\AAAA_CODE\new-eclipse-workspace\to-github\opencar\target\opencar-0.0.1-SNAPSHOT.jar
./java --module-path D:\AAAA_WORK\java\openjfx\openjfx-16_windows-x64_bin-sdk\javafx-sdk-16\lib --add-modules javafx.controls,javafx.fxml -jar E:\AAAA_CODE\new-eclipse-workspace\to-github\opencar\target\opencar-0.0.1-SNAPSHOT.jar
11月 30, 2022 11:33:02 上午 javafx.fxml.FXMLLoader$ValueElement processValue
警告: Loading FXML document with JavaFX API of version 17 by JavaFX runtime of version 16

pom

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.far.demo</groupId>
    <artifactId>opencar</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>opencar</name>
    <description>opencar</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.fazecast</groupId>
            <artifactId>jSerialComm</artifactId>
            <version>[2.0.0,3.0.0)</version>
        </dependency>


        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>17</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>17.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>17.0.1</version>
        </dependency>

    </dependencies>

    <build>


        <resources>

            <resource>
                <!--resources下一级的所有.xml .properties文件复制到config目录下-->
                <targetPath>${project.build.directory}/config</targetPath>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>

            </resource>
        </resources>



        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.2</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <useUniqueVersions>false</useUniqueVersions>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>com.far.vms.opencar.Run</mainClass> <!-- 此处为主入口-->
                        </manifest>
                    </archive>
                </configuration>
            </plugin>


            <plugin>
                <groupId>com.zenjava</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>8.8.3</version>
                <configuration>
                    <mainClass>com.liumiaocn.javademo.Main</mainClass>
                </configuration>
            </plugin>

<!--            <plugin>-->
<!--                <groupId>org.openjfx</groupId>-->
<!--                <artifactId>javafx-maven-plugin</artifactId>-->
<!--                <version>0.0.8</version>-->
<!--            -->
<!--                <configuration>-->
<!--                    <mainClass>com.far.vms.opencar.Run</mainClass>-->
<!--                </configuration>-->
<!--            </plugin>-->

        </plugins>
    </build>

</project>

本节参考

https://www.jianshu.com/p/51f818878eda
https://blog.csdn.net/qq_34444097/article/details/126556823
https://blog.csdn.net/weixin_44480167/article/details/120361097

结尾

javafx坑参考
https://zhuanlan.zhihu.com/p/30386925

posted @ 2022-11-29 23:27  方东信  阅读(369)  评论(0编辑  收藏  举报