我正在做期中考试,我们必须制作一个 GUI 购物清单。教授要求我们将列表放入 .txt 文件并从该文件中读取。还有一个删除部分,删除所有我还没有编码的部分。通常,每当我向其中添加新内容时,我都会测试代码。当尝试测试这个时,我注意到它崩溃了。该代码正在写入 Grocery.txt,但未显示写入列表框中的内容。我知道我很可能错过了一些小事。当我查看我的教科书“Murach 的 Java 编程第六版”时,它看起来几乎相同,只是他们使用的示例是从文件中读取三个不同的内容。他们正在使用数组来读取。我试图将每一个都放在自己的线上。我知道现在不会。我正在尝试测试它是否能够读取它,但目前还没有。
更新:我现在更改了一些代码,当我单击“添加”时,我收到一个显示错误的空警报框。已放置更新的代码。我会离开几个小时,然后回来继续工作。谢谢大家的帮助。
这是我当前的代码:
package com.mycompany.zaph_midterm;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
public TextField addGrocery;
public ListView<String> groceryList = new ListView<>();
@Override
public void start(Stage stage) {
stage.setTitle("Grocery List");
//Setting grid
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setPadding(new Insets(15,15,15,15));
grid.setHgap(10);
grid.setVgap(10);
//attaching grid to scene
Scene scene = new Scene(grid);
//Setting the add section
HBox hbox = new HBox();
GridPane addHbox = new GridPane();
addHbox.add(new Label ("Items: "), 0, 0);
addHbox.add(new Label(" "), 1, 0);
addGrocery = new TextField();
addHbox.add(addGrocery, 2, 0);
Button addButton = new Button("Add");
addButton.setOnAction(event -> addButtonClicked());
addHbox.add(new Label(" "), 3, 0);
addHbox.add(addButton, 4, 0);
hbox.getChildren().add(addHbox);
grid.add(hbox, 0, 0);
//Setting list to show
HBox groceryBox = new HBox();
GridPane groceryGrid = new GridPane();
groceryGrid.add(new Label("List: "), 0, 0);
groceryList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
groceryList.getItems().add(grocery.readFromFile(addGrocery.getText()));
groceryGrid.add(groceryList, 2, 0);
groceryBox.getChildren().add(groceryGrid);
grid.add(groceryBox, 0, 1);
stage.setScene(scene);
stage.show();
}
// Add button clicked
private void addButtonClicked() {
//Validation of user Input
Validation v = new Validation();
String errorMsg = "";
errorMsg += v.hasValue(addGrocery.getText());
errorMsg += v.isBlank(addGrocery.getText());
// Using if/else to add to grocery List
if (errorMsg == ""){
GroceryList grocery = new GroceryList();
//Adding to Grocery List
grocery.printToFile(addGrocery.getText());
}else {
Alert wrong = new Alert(Alert.AlertType.ERROR);
wrong.setHeaderText("Error");
wrong.setContentText(errorMsg);
wrong.showAndWait();
}
}
public static void main(String[] args) {
launch();
}
验证类:
package com.mycompany.zaph_midterm;
public class Validation {
private final String lineEnd;
public Validation() {
this.lineEnd = "\n";
}
public Validation(String lineEnd) {
this.lineEnd = lineEnd;
}
public String isBlank(String name) {
String error = "";
if(name.isBlank()) {
error = "Must have groceries in the add text box.";
}
return error;
}// end of isBlank
public String hasValue(String name) {
//if user input is able to be parsed then an error message will return
String error = "";
try {
Double.parseDouble(name);
} catch (NumberFormatException e) {
return error;
}
error ="Text box must only contain letters";
return error;
}
}
杂货清单类:
package com.mycompany.zaph_midterm;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import static java.lang.System.out;
public class GroceryList {
private String grocery;
public GroceryList() {
grocery = " ";
}
public void setGroceryList(String args){
this.grocery = grocery;
}
public String getGroceryList() {
return grocery;
}
public void printToFile(String grocery) {
//creating file
try (PrintWriter out = new PrintWriter(
new BufferedWriter(
new FileWriter("Grocery.txt")))) {
//writing to file
out.print(grocery);
}
//catching error if found
catch (IOException e){
System.out.println(e);
}
}
public void deleteFromFile(String grocery) {
}
public String readFromFile(String grocery) throws IOException{
try (BufferedReader in = new BufferedReader(
new FileReader("Grocery.txt"))){
String line = in.readLine();
while (line != null){
line = grocery;
return grocery;
}
}
catch (IOException e) {
Alert error = new Alert(Alert.AlertType.ERROR);
error.setHeaderText("Didn't work");
error.setContentText("Something is wrong");
error.showAndWait();
}
return grocery;
}
}
}
我尝试将 try(BufferedReader) 移动到 addButtonCLicked() 仍然崩溃,所以我将它移到 //setting 列表下显示,但它仍然崩溃。
您的问题没有重点、不清楚。但是,为了好玩,我会尝试修改您的代码。
编程中的一个非常有用的概念是“关注点分离”。这意味着将您的代码组织成单独的块,每个块专注于特定的工作。 其中一项工作是将购物清单中的项目写入存储,然后将其读回。那应该是在一个特定的班级中。我们将这个类称为
Repository
。
我们需要定义要进入存储库的对象。您的GroceryList
类考虑不周,因为列表只是项目的集合。这是我们需要定义的项目。我们可以简单地通过
记录来做到这一点。该类唯一的工作是表示代表现实世界中的杂货商品所需的验证状态。
public record GroceryItem( String name )
{
public GroceryItem ( final String name )
{
Objects.requireNonNull ( name );
if ( name.isBlank ( ) ) throw new IllegalArgumentException ( "Grocery item name cannot be blank." );
this.name = name;
}
}
存储库需要保存杂货并再次检索。这个类唯一的工作就是持久存储和检索对象的状态。在这里,我们编写了一个简单的伪造实现,足以开始使用。
public class Repository
{
private List < GroceryItem > storedGroceryItems =
new ArrayList <> (
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" )
)
);
public List < GroceryItem > fetchGroceryItems ( )
{
return List.copyOf ( this.storedGroceryItems );
}
public boolean saveGroceryItem ( final GroceryItem groceryItem )
{
if ( this.storedGroceryItems.contains ( groceryItem ) ) return false;
return this.storedGroceryItems.add ( groceryItem );
}
public boolean deleteGroceryItem ( final GroceryItem groceryItem )
{
return this.storedGroceryItems.remove ( groceryItem );
}
public boolean removeAllGroceryItems ( )
{
List < GroceryItem > emptyList = List.of ( );
return this.storedGroceryItems.retainAll ( emptyList );
}
}
编写一些快速测试来验证。
public class TestRepository
{
public static void main ( String[] args )
{
TestRepository.testPreloadedRepository ( );
}
public static void testPreloadedRepository ( )
{
Repository repository = new Repository ( );
boolean success;
List < GroceryItem > list = repository.fetchGroceryItems ( );
System.out.println ( "list has 3 elements: " + ( list.size ( ) == 3 ) );
List < GroceryItem > expectedList =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" )
);
System.out.println ( "list has expected items: " + ( list.equals ( expectedList ) ) );
GroceryItem broccoli = new GroceryItem ( "Broccoli" );
success = repository.saveGroceryItem ( broccoli );
System.out.println ( "success = " + success );
list = repository.fetchGroceryItems ( );
System.out.println ( "list has 4 elements: " + ( list.size ( ) == 4 ) );
expectedList =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" ) ,
broccoli
);
System.out.println ( "list has expected items: " + ( list.equals ( expectedList ) ) );
GroceryItem eggs = new GroceryItem ( "Eggs" );
success = repository.deleteGroceryItem ( eggs );
System.out.println ( "success = " + success );
list = repository.fetchGroceryItems ( );
System.out.println ( "list has 3 elements: " + ( list.size ( ) == 3 ) );
expectedList =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Olive oil" ) ,
broccoli
);
System.out.println ( "list has expected items: " + ( list.equals ( expectedList ) ) );
}
}
list has 3 elements: true
list has expected items: true
success = true
list has 4 elements: true
list has expected items: true
success = true
list has 3 elements: true
list has expected items: true
现在在 JavaFX 中构建初始 GUI。
public class HelloApplication extends Application
{
private Repository repository = new Repository ( );
@Override
public void start ( Stage stage ) throws IOException
{
Pane pane = this.buildPane ();
Scene scene = new Scene ( pane , 500 , 350 );
stage.setTitle ( "Grocery List" );
stage.setScene ( scene );
stage.show ( );
}
private Pane buildPane () {
// Data
List < GroceryItem > groceryItems = this.repository.fetchGroceryItems ( );
ObservableList < GroceryItem > observableList = FXCollections.observableArrayList ( groceryItems );
// Widgets
ListView < GroceryItem > listView = new ListView <> ( observableList );
TextField newGroceryItemField = new TextField ( );
newGroceryItemField.setPromptText ( "new grocery item" );
Button addButton = new Button ( "Add…" );
Button removeButton = new Button ( "Delete" );
// Behavior
addButton.setOnMouseClicked ( ( MouseEvent mouseEvent ) ->
{
String input = newGroceryItemField.getText ( );
if ( input.isBlank ( ) ) return;
GroceryItem groceryItem = new GroceryItem ( input );
if ( observableList.contains ( groceryItem ) ) return;
if ( this.repository.saveGroceryItem ( groceryItem ) )
{
observableList.add ( groceryItem );
}
} );
removeButton.setOnMouseClicked ( ( MouseEvent mouseEvent ) ->
{
GroceryItem groceryItem = listView.getSelectionModel ( ).getSelectedItem ( );
if ( Objects.nonNull ( groceryItem ) )
{
if ( this.repository.deleteGroceryItem ( groceryItem ) )
{
observableList.remove ( groceryItem );
}
}
} );
// Arrange
BorderPane borderPane = new BorderPane ( );
borderPane.setCenter ( listView );
borderPane.setBottom ( new HBox ( newGroceryItemField , addButton , removeButton ) );
return borderPane;
}
public static void main ( String[] args ) { launch ( ); }
}
Repository
。我们可以重写以实际保存到文件中,而不是使用内存中的虚拟列表。
package work.basil.example.exgrocerylist;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
public class Repository
{
private Path dataFilePath = Paths.get ( "/Users/basil_dot_work/GroceryItems.txt" );
public List < GroceryItem > fetchGroceryItems ( )
{
boolean go = this.createAndPopulateFileIfNotExists ( );
try
{
List < String > lines = Files.readAllLines ( this.dataFilePath , StandardCharsets.UTF_8 );
List < GroceryItem > groceryItems = new ArrayList <> ( );
for ( String line : lines )
{
if ( line.isBlank ( ) )
{
continue;
}
GroceryItem groceryItem = new GroceryItem ( line );
groceryItems.add ( groceryItem );
}
return List.copyOf ( groceryItems );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
private boolean createAndPopulateFileIfNotExists ( )
{
if ( Files.exists ( this.dataFilePath ) ) return true;
List < GroceryItem > defaultGroceryItems =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" )
);
List < String > lines =
defaultGroceryItems.stream ( ).map ( GroceryItem :: name ).toList ( );
try
{
Files.write ( this.dataFilePath , lines , StandardCharsets.UTF_8 );
return ( this.fetchGroceryItems ( ).equals ( defaultGroceryItems ) );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
public boolean saveGroceryItem ( final GroceryItem groceryItem )
{
List < GroceryItem > groceryItems = this.fetchGroceryItems ( );
if ( groceryItems.contains ( groceryItem ) ) return false;
try
{
Files.write ( this.dataFilePath , groceryItem.name ( ).getBytes ( ) , StandardOpenOption.APPEND );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
return this.fetchGroceryItems ( ).contains ( groceryItem );
}
public boolean deleteGroceryItem ( final GroceryItem groceryItem )
{
List < GroceryItem > groceryItems = this.fetchGroceryItems ( );
if ( ! groceryItems.contains ( groceryItem ) ) return false;
List < GroceryItem > groceryItemsModifiable = new ArrayList <> ( groceryItems );
boolean go = groceryItemsModifiable.remove ( groceryItem );
if ( ! go )
{
return false;
}
List < String > lines = groceryItemsModifiable.stream ( ).map ( GroceryItem :: name ).toList ( );
try
{
Files.write ( this.dataFilePath , lines , StandardCharsets.UTF_8 );
return true;
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
public boolean removeAllGroceryItems ( )
{
List < String > lines = List.of ( );
try
{
Files.write ( this.dataFilePath , lines , StandardCharsets.UTF_8 );
return ( this.fetchGroceryItems ( ).isEmpty ( ) );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
}
给高级学生的提示:
Repository
应该是一个接口,有两个具体实现:一个用于我们的内存列表,另一个用于我们的文件存储。
注意:这个基于文件的Repository
有一个错误:用户无法添加多个杂货商品。我们可以确定错误存在于这个基于文件的
Repository
实现中,因为我们可以切换回基于内存列表的实现来查看错误消失。我将保留未发现的错误,因为我已经为作业完成了太多工作。