简体   繁体   English

从TableView中删除选中的行,以及JavaFX中SceneBuilder的有效使用

[英]Delete selected row from TableView, and the effective use of SceneBuilder in JavaFX

I'm trying to make an application that creates a table from text in a file and then the user can add or delete words.我正在尝试制作一个从文件中的文本创建表格的应用程序,然后用户可以添加或删除单词。 The text file is used else where in my app to populate dropdown boxes so the user can only choose the text in the dropdown boxes.文本文件在我的应用程序中的其他地方用于填充下拉框,因此用户只能选择下拉框中的文本。

My problem is I can't delete from the table only add to it and anything I can find on the net are tables that are made from classes.我的问题是我不能从表中删除只能添加到它,我在网上能找到的任何东西都是由类制成的表。 My table is just a very basic one that has 1 column of strings.我的表只是一个非常基本的表,有 1 列字符串。 For some reason when I print the selected items of the table I always get nothing?出于某种原因,当我打印表格的选定项目时,我总是一无所获? I can't figure out why.我不明白为什么。

I only started javaFx a few weeks ago I really just learning from videos and books but they are very simple compared to the application I'm expected to make.我几周前才开始使用 javaFx,我真的只是从视频和书籍中学习,但与我期望制作的应用程序相比,它们非常简单。

Here is my code:这是我的代码:

public class MaintenWindow {

private static Stage window;
private static TableView<String> table = new TableView<String>();
private static TextField input;
private static ObservableList<String> stringList;
private static BorderPane bP;
private static Scene scene;

private static File types = new File("type.txt");
private static File location = new File("location.txt");
private static File status = new File("status.txt");

public static void display() throws FileNotFoundException  {
    window = new Stage();
    BorderPane bP = new BorderPane();

    window.setTitle("Mainten Window");

    MenuBar menuBar = new MenuBar();

    Menu fileMenu = new Menu("_Add/Remove From DropDowns");
    MenuItem editLocation = new MenuItem("_Edit Locations");
    MenuItem editAtypes = new MenuItem("_Edit Animal Types");
    MenuItem editStatus = new MenuItem("_Edit Status's");
    MenuItem exit = new MenuItem("Exit");
    fileMenu.getItems().addAll(editLocation, editAtypes, editStatus, new SeparatorMenuItem(),exit);

    menuBar.getMenus().addAll(fileMenu);
    editLocation.setOnAction(e -> {
        bP.setTop(menuBar);
        bP.setCenter(vBox("Locations", location));
        window.setScene(scene);
        window.show();              
    });

    editAtypes.setOnAction(e -> {
        bP.setTop(menuBar);
        bP.setCenter(vBox("Animal Types", types));
        window.setScene(scene);
        window.show();              
    });

    editStatus.setOnAction(e -> {
        bP.setTop(menuBar);
        bP.setCenter(vBox("Status", status));
        window.setScene(scene);
        window.show();              
    });


    bP.setTop(menuBar);
    scene = new Scene(bP, 800,800);

    Label label = new Label("welcome to the maintenance window");


    bP.setCenter(label);

    window.setScene(scene);
    window.show();

}

/*public static MenuBar menuBar()
{
    //file tab menu
    Menu fileMenu = new Menu("_Add/Remove From DropDowns");
    MenuItem editLocation = new MenuItem("_Edit Locations");
    MenuItem editAtypes = new MenuItem("_Edit Animal Types");
    MenuItem editStatus = new MenuItem("_Edit Status's");
    MenuItem exit = new MenuItem("Exit");
    fileMenu.getItems().addAll(editLocation, editAtypes, editStatus, new SeparatorMenuItem(),exit);

    editLocation.setOnAction(e -> {
        bP.setTop(menuBar());
        bP.setCenter(vBox("Locations", location));
        window.setScene(scene);
        window.show();              
    });

    editAtypes.setOnAction(e -> {
        bP.setTop(menuBar());
        bP.setCenter(vBox("Animal Types", types));
        window.setScene(scene);
        window.show();              
    });

    editStatus.setOnAction(e -> {
        bP.setTop(menuBar());
        bP.setCenter(vBox("Status", status));
        window.setScene(scene);
        window.show();              
    });

    MenuBar menuBar = new MenuBar();
    menuBar.getMenus().addAll(fileMenu);

    return menuBar;
}*/

public static ObservableList<String> getString(File file) throws FileNotFoundException{
    stringList = FXCollections.observableArrayList();

    Scanner kb = new Scanner(file);
    String item;
    while (kb.hasNext()) {
        item = kb.nextLine();
        stringList.add(item);
    }

    kb.close();
    return stringList;
}

@SuppressWarnings("unchecked")
public static TableView<String> table(String string,File file)
{
    TableColumn<String, String> tColumn = new TableColumn<String, String>(string);
    tColumn.setCellValueFactory(cellData -> 
    new ReadOnlyStringWrapper(cellData.getValue()));
    TableView<String> table = new TableView<String>();
    try {
        table.setItems(getString(file));
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    table.getColumns().addAll(tColumn);

    table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
    return table;
}

public static void addButtonClicked(){

    stringList.add(input.getText());
    input.clear();
}

//Delete button clicked
public static void deleteButtonClicked(){
    ObservableList<String> allStrings, stringSelected;
    allStrings = table.getItems();
    //printElements(allStrings);
    System.out.println(table.getSelectionModel().getSelectedItems());

    stringSelected = table.getSelectionModel().getSelectedItems();
    System.out.println(stringSelected);
    stringSelected.forEach(allStrings::remove);
}

public static HBox textFields() {
    input = new TextField();
    input.setPromptText("Input");
    input.setMinWidth(75);

    HBox hBox = new HBox();
    hBox.setPadding(new Insets(10,10,10,10));
    hBox.setSpacing(10);
    hBox.getChildren().addAll(input);

    return hBox;
}

public static HBox addRemove(File file){
    //Button
    Button addButton = new Button("Add");
    addButton.setOnAction(e -> addButtonClicked());
    Button deleteButton = new Button("Delete");
    deleteButton.setOnAction(e -> deleteButtonClicked());
    Button writeButton = new Button("Write to file");
    try {
        try {
            writeButton.setOnAction(e -> write(file));
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    } catch (Exception e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    Button closeButton = new Button("Close Window");
    closeButton.setOnAction(e -> window.close());

    HBox hBox2 = new HBox();
    hBox2.setPadding(new Insets(10,10,10,10));
    hBox2.setSpacing(10);
    hBox2.getChildren().addAll(addButton, deleteButton, writeButton, closeButton);

    return hBox2;
}

private static void printElements(ObservableList<String> list) {
    System.out.println("Size: " + list.size());
    for (Object o : list) {
        System.out.println(o.toString());
    }
    System.out.println("");
}

public static void write(File file)
{
    PrintWriter outFile = null;
    try {
        outFile = new PrintWriter(file);
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    for(int i = 0; i < stringList.size(); i++)
    {
        outFile.println(stringList.get(i));
    }
    outFile.close();
}

public static VBox vBox(String string, File file){
    Label label = new Label(string);
    label.setFont(new Font("Arial", 20));

    VBox vBox = new VBox();
    vBox.setSpacing(5);
    vBox.setPadding(new Insets(10, 0, 0, 10));
    vBox.getChildren().addAll(label, table(string,file), textFields(), addRemove(file));

    return vBox;
}

} }

I would be greatful for any help.如果有任何帮助,我将不胜感激。 if u have any tips or advice for me please post If you have links to more advanced javafx programs than I'm finding please post them.如果您对我有任何提示或建议,请发布如果您有比我找到的更高级的 javafx 程序的链接,请发布它们。

What's going wrong怎么了

You have a logic error in your code where you create two instances of your table.您在创建表的两个实例的代码中存在逻辑错误。 You declare in your class:你在课堂上声明:

private static TableView<String> table = new TableView<String>();

Then later on in your table() function you declare a new table local to that function然后在你的table()函数中,你声明一个该函数的本地新表

TableView<String> table = new TableView<String>();
table.setItems(getString());
table.getColumns().addAll(tColumn);

return table;

Then you eventually add the table returned by your table() function to the scene:然后你最终将table()函数返回的table()添加到场景中:

vbox.getChildren().addAll(label, table(), textFields(), addRemove());

But elsewhere in your application, such as deleteButtonClicked() , you invoke:但是在应用程序的其他地方,例如deleteButtonClicked() ,您调用:

table.getSelectionModel().getSelectedItems();

But that is going to be working off the instance declared in your class, not the instance declared in your method, so it will always return an empty list.但这将适用于您的类中声明的实例,而不是您的方法中声明的实例,因此它将始终返回一个空列表。

How to fix it如何修复

To fix this, only create a single new TableView, not two.要解决此问题,只需创建一个新的 TableView,而不是两个。

Here is some simplified sample code:下面是一些简化的示例代码:

import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.text.Font;
import javafx.stage.Stage;

public class MaintenanceWindow extends Application {
    private TableView<String> table;
    private TextField input;
    private Stage myStage;

    private static final String[] ITEMS = { "apples", "oranges", "peaches", "pears" };

    @Override
    public void start(Stage stage) throws Exception {
        myStage = stage;
        final Label label = new Label("list");
        label.setFont(new Font("Arial", 20));

        VBox vbox = new VBox(
                5,
                label, table(), textFields(), addRemove()
        );
        vbox.setPadding(new Insets(10, 0, 0, 10));

        BorderPane bP = new BorderPane();
        bP.setTop(menuBar());
        bP.setCenter(vbox);

        stage.setScene(new Scene(bP, 700, 800));
        stage.show();
    }

    private MenuBar menuBar() {
        Menu fileMenu = new Menu("_Add/Remove From DropDowns");
        MenuItem locationAdd = new MenuItem("Location DropDowns Add");
        locationAdd.setOnAction(e -> addButtonClicked());
        MenuItem locationRemove = new MenuItem("Location DropDowns Remove");
        locationRemove.setOnAction(e -> deleteButtonClicked());
        MenuItem exit = new MenuItem("Exit");
        exit.setOnAction(e -> closeApp());
        fileMenu.getItems().addAll(
                locationAdd,
                new SeparatorMenuItem(),
                locationRemove,
                new SeparatorMenuItem(),
                exit
        );

        MenuBar menuBar = new MenuBar();
        menuBar.getMenus().addAll(fileMenu);
        return menuBar;
    }

    private TableView<String> table() {
        TableColumn<String, String> tColumn = new TableColumn<>("String");
        tColumn.setMinWidth(250);
        tColumn.setCellValueFactory(
                cellData -> new ReadOnlyStringWrapper(cellData.getValue())
        );
        table = new TableView<>();
        table.getItems().addAll(ITEMS);
        table.getColumns().add(tColumn);

        return table;
    }

    private void addButtonClicked() {
        if (!(input.getText() == null || "".equals(input.getText().trim())) {
            table.getItems().add(input.getText());
            input.clear();
        }
    }

    private void deleteButtonClicked() {
        table.getItems().removeAll(
                table.getSelectionModel().getSelectedItems()
        );
    }

    private HBox textFields() {
        input = new TextField();
        input.setPromptText("Input");
        input.setMinWidth(75);

        HBox hBox = new HBox();
        hBox.setPadding(new Insets(10, 10, 10, 10));
        hBox.setSpacing(10);
        hBox.getChildren().addAll(input);

        return hBox;
    }

    private HBox addRemove() {
        Button addButton = new Button("Add");
        addButton.setOnAction(e -> addButtonClicked());
        Button deleteButton = new Button("Delete");
        deleteButton.setOnAction(e -> deleteButtonClicked());
        Button closeButton = new Button("Close Window");
        closeButton.setOnAction(e -> closeApp());

        HBox hBox2 = new HBox(
                10,
                addButton, deleteButton, closeButton
        );
        hBox2.setPadding(new Insets(10, 10, 10, 10));
        hBox2.setSpacing(10);

        return hBox2;
    }

    private void closeApp() {
        myStage.close();
    }
}

图片


Unrelated advice for future questions:对未来问题的不相关建议:

Those not looking for advice can skip reading the rest of this answer as it doesn't really apply directly to the question.那些不寻求建议的人可以跳过阅读本答案的其余部分,因为它并不直接适用于问题。

  • Create an mcve .创建一个mcve
  • Follow the steps for debugging small programs .按照 调试小程序的步骤进行操作。 If you have a large program and can't work out what is going on, just create a small program.如果您有一个大程序并且无法弄清楚发生了什么,只需创建一个小程序。
  • As your program gets larger, either design it with multiple classes from the start or refactor it to use multiple classes.随着程序变大,要么从一开始就用多个类设计它,要么重构它以使用多个类。
  • Use FXML and SceneBuilder for complex layouts such as this rather than hand coding them.FXMLSceneBuilder用于诸如此类的复杂布局,而不是手动编码它们。
  • Go through the makery JavaFX tutorial and study it very closely (it seems pretty similar to what you are trying to achieve here).仔细阅读制造 JavaFX 教程并仔细研究它(它似乎与您在此处尝试实现的目标非常相似)。
  • Just ask a single question per question on StackOverflow and isolate the code that addresses each single question to a specific mcve that only addresses that.只需在 StackOverflow 上针对每个问题提出一个问题,并将解决每个问题的代码隔离到仅解决该问题的特定 mcve。 eg for your original additional question on file exceptions (which it looks like somebody has now edited out of your question), just post that as a seperate question.例如,对于您关于文件异常的原始附加问题(看起来有人现在已经编辑了您的问题),只需将其作为单独的问题发布即可。
  • When you have a program that works (but is not too large), post its code to codereview for feedback on general programming techniques, conventions and design approaches (for example they will certainly give you feedback not use static methods everywhere).当你有一个可以运行的程序(但不是太大)时,把它的代码发布到codereview以获得关于一般编程技术、约定和设计方法的反馈(例如,它们肯定会给你反馈,而不是到处使用静态方法)。 For an example of the kinds of things you should post to code review and the kind of feedback you can expect, see the coderview for An alarm application in Java(FX) .有关您应该发布到代码审查的类型以及可以期望的反馈类型的示例,请参阅Java(FX) 中的警报应用程序的 coderview。
  • Stackoverflow isn't really a tips and advice site, it just pure Q&A. Stackoverflow 并不是一个真正的提示和建议网站,它只是纯粹的问答。

Would u recommend scenebuilder for novice javafx programmers?你会为新手javafx程序员推荐scenebuilder吗?

Yes.是的。 I used to not not recommend it for beginners, but have come around to thinking it is a good idea to use that tool for initial experimentation and later development, even for novice JavaFX programmers.我曾经不建议初学者使用它,但后来认为使用该工具进行初始实验和后期开发是一个好主意,即使对于新手 JavaFX 程序员也是如此。 Using the SceneBuilder tool rather than directly programming against the Java API, it is probably easier and more efficient for a new developer to get insight into how layouts work and what properties are exposed on nodes, regions and various controls.使用 SceneBuilder 工具而不是直接针对 Java API 进行编程,对于新开发人员来说,了解布局如何工作以及在节点、区域和各种控件上公开哪些属性可能更容易、更有效。

However, learning SceneBuilder and FXML alone is no substitute for actually learning how to program against the Java API, for which you need to write code.但是,仅学习 SceneBuilder 和 FXML 并不能替代实际学习如何针对 Java API 进行编程,您需要为此编写代码。 So definitely do both things, play around with stuff in SceneBuilder and write code against the Java API.所以一定要做这两件事,在 SceneBuilder 中玩弄东西针对 Java API 编写代码。 At first do those things separate, by exploring JavaFX controls and layouts only in scene builder and reviewing the FXML it generates and by writing small standalone programs that are coded just against the Java API, with no use of FXML.首先将这些事情分开,通过仅在场景构建器中探索 JavaFX 控件和布局并查看它生成的 FXML,并编写仅针对 Java API 编码的小型独立程序,不使用 FXML。 Then, only once you are relatively comfortable with both of those things, tackle a project like that in the Makery tutorial linked earlier, that combines Scene Builder for definition of FXML based UIs and usage of those FXML files in an intermediate sized application that also includes code against the Java API.然后,只有当您对这两件事都比较满意后,才能处理前面链接的 Makery 教程中的项目,该项目结合了 Scene Builder 以定义基于 FXML 的 UI 以及在中等大小的应用程序中使用这些 FXML 文件,该应用程序还包括针对 Java API 的代码。

Also, neither playing with SceneBuilder nor playing around with the API, is a substitute for working through the JavaFX tutorials andunderstanding layout fundamentals such as Groups, layout panes and Regions , which must be done to get a reasonable grasp on the technology.此外,无论是使用 SceneBuilder 还是使用 API,都不能替代 通过 JavaFX 教程理解布局基础知识(如组、布局窗格和区域)的替代方法,必须这样做才能合理地掌握该技术。

No JavaFX program can rely on FXML alone, if you use FXML, you need a combination of FXML and Java code to get a working program.没有任何 JavaFX 程序可以单独依赖 FXML,如果您使用 FXML,则需要 FXML 和 Java 代码的组合才能得到一个可以工作的程序。 Any significant JavaFX program that uses a lot of controls and layouts is best off being coded with FXML layout, CSS for the style and Java (or other language) code for the logic.任何使用大量控件和布局的重要 JavaFX 程序最好使用 FXML 布局、样式的 CSS 和逻辑的 Java(或其他语言)代码进行编码。 So, eventually you need to learn all three technologies (FXML, CSS and JavaFX API) and how to make them function together in order to proceed beyond trivial programs.因此,最终您需要学习所有这三种技术(FXML、CSS 和 JavaFX API)以及如何使它们协同工作,以便超越琐碎的程序。

One issue with SceneBuilder which I see new JavaFX developers encounter, is that is is relatively simple to create a complex UI in SceneBuilder, but it is relatively hard for a novice to take that complex UI and add correct logic to it to make the application actually functional as it should be.我看到新的 JavaFX 开发人员遇到的 SceneBuilder 的一个问题是,在 SceneBuilder 中创建复杂的 UI 相对简单,但对于新手来说,采用复杂的 UI 并为其添加正确的逻辑以实际制作应用程序相对困难应有的功能。 So, don't make the mistake of defining the complete UI for your application in SceneBuilder before you gain a decent understanding of both how to use the JavaFX Java API and how to add logic to some very basic FXML files using the JavaFX Java API.因此,在您充分了解如何使用 JavaFX Java API 以及如何使用 JavaFX Java API 向一些非常基本的 FXML 文件添加逻辑之前,不要错误地在 SceneBuilder 中为您的应用程序定义完整的 UI。

Also once you start coding up multiple FXML files, a problems which everybody runs into is:此外,一旦您开始编写多个 FXML 文件,每个人都会遇到的一个问题是:

i choose not to use it as i thought it looked very complicated since u would need an xml and controller我选择不使用它,因为我认为它看起来很复杂,因为你需要一个 xml 和控制器

Yeah, it is (relatively speaking), more complicated to understand and you have to have more skills to initially create an FXML/CSS based application than something written against a pure Java API.是的,它(相对而言)更难理解,并且与针对纯 Java API 编写的应用程序相比,您必须拥有更多技能来最初创建基于 FXML/CSS 的应用程序。 However, once you have built one, my guess is that you will find an intermediate sized FXML/CSS based application easier to understand and maintain than an equivalent application written purely against the Java API.然而,一旦你构建了一个,我猜你会发现一个中等大小的基于 FXML/CSS 的应用程序比纯粹针对 Java API 编写的等效应用程序更容易理解和维护。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM