JavaFX Probleme beim automatischen Konvertieren

friednoodles

Aktives Mitglied
Hi,

ich habe ein Problem mit einer Methode die ich schon eine Weile in meinem Programm habe. Diese soll bei TextField Fokus automatisch den String aus der Zwischenablage holen und gleich konvertieren. Epoch Timestamp <-> normales Datum (yyyy-MM-dd HH:mm:ss) und das Ergebnis in eine Tabelle eintragen.

Das Problem ist nun, dass zwei Zeilen in die Tabelle eingetragen werden nach jedem automatischen Konvertieren. Das sollte nicht passieren.

Java:
    private CheckMenuItem automaticConvert() {
        startBox.textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> arg0, Boolean textAreaFocus,
                    Boolean textFieldFocus) {

                String clipboardContent = clipboard.getString();
                if (autoConvertCheckMenuItem.isSelected() && clipboard.hasString() && textFieldFocus) {
                    startBox.textField.setText(clipboardContent);
                    showResultAndAddLineToTable();
                }
                if (autoCopyCheckMenuItem.isSelected()) {
                    autoCopy();
                }
            }
        });
        return autoConvertCheckMenuItem;
    }

Ich weiß, im Moment sind meine Methoden ein absolutes wirrwarr, weswegen dieser Fehler wohl in erster Linie auch entstanden ist. Die Methode showResultAndAddLineToTable(); hat mehrere Methoden hinter sich, zum einen die Logik&Darstellung der Umwandlung und zum anderen fügt dieser der Tabelle die gewünschte Zeile hinzu.

Java:
    private void showResultAndAddLineToTable() {
        try {
            startBox.textArea.setText(ConverterTools.patternQuery(this.startBox.textField.getText()));
        } catch (PatternQueryException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        printTableLine.add(new Result(startBox.textField.getText(), startBox.textArea.getText()));

    }

Bei mir hängen alle Methoden sehr unschön zusammen. Wenn ich alle Methoden die hier zusammenhängen posten würde, wäre der Beitragsrahmen vermutlich schon gesprengt.

Lässt sich aus meinem Code schon ein Fehler rauslesen? Danke für mögliche Hinweise, und falls mehr Code benötigt wird bitte bescheid sagen!

edit: die Methode autoCopy(); ist die zweite Menüoption neben automatischem Konvertieren. Sie kopiert einfach den Ergebnis String in die Zwischenablage
 
Kannst du auf die schnelle ein kurzes, selbständig lauffähiges Beispiel zusammenstellen? Eine Klasse, bei dem das Problem nachvollziehbar ist?
Wenn ja, werde ich es mir anschauen. Ich habe leider nicht genug Zeit, um mir eine Demo-App selbst zu schreiben...
 
Hi nochmal, ich habe versucht mein Programm so weit wie möglich zu kürzen. Ist aber trotzdem noch ein wenig lang.. Ich suche jetzt nicht unbedingt Hilfe für sonstige Strukturierung in meinem Code, nehme aber alle Tipps dankend an!
Mein Problem ist nach wie vor, dass der Menüpunkt "auto Convert", falls aktiviert, zu viele Zeilen in meine Tabelle macht. Beim Wechsel zwischen der StartBox und InfoBox passiert das ebenfalls, was NICHT der Fall sein sollte.

Meine eigene Überlegung ist, dass irgendwas mit focusedProperty() falsch sein muss. Wüsste aber nicht wie ich das besser machen könnte 🙁

Meine Hauptklasse in der auch die problematische Methode zum automatischen Konvertieren ist:
Java:
package application;


import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.TableColumn;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.stage.Stage;

public class Converter2 extends Application {

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

    Stage stage;
    ConverterMainBox2 mainBox;
    MenuBar menuBar;
    CheckMenuItem autoCopyCheckMenuItem = new CheckMenuItem("Copy result to clipboard");
    CheckMenuItem autoConvertCheckMenuItem = new CheckMenuItem("Auto convert");
    Clipboard clipboard = Clipboard.getSystemClipboard();
    ObservableList<Result2> printTableLine = FXCollections.observableArrayList();

    StartBox2 startBox = new StartBox2();
    InfoBox2 infoBox = new InfoBox2();

    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("Date <-> Epoch Timestamp");
        menuBar = createMenu(stage);
        mainBox = new ConverterMainBox2(stage, menuBar);
        mainBox.showBox(startBox);
        convertButtonActions();
        automConv();
        initTable();

    }

    private void convertButtonActions() {
        startBox.convertButton.setOnAction(e -> {
            showResultAndAddLineToTable();
            autoCopy();

        });
    }

    private MenuBar createMenu(Stage stage) {
        MenuBar menuBar = new MenuBar();

        Menu menuStart = new Menu();
        Label start = new Label("Start");
        menuStart.setGraphic(start);
        setActionOnStart(start);

        Menu menuOptions = new Menu("Options");
        CheckMenuItem automKonv = automConv();
        CheckMenuItem automKop = autoCopy();
        menuOptions.getItems().addAll(automKonv, automKop);

        Menu menuAbout = new Menu();
        Label about = new Label("About");
        menuAbout.setGraphic(about);
        setActionOnAbout(about);

        menuBar.getMenus().addAll(menuStart, menuOptions, menuAbout);
        return menuBar;
    }

    private void setActionOnStart(Label start) {
        start.setOnMouseClicked(new EventHandler<Event>() {
            @Override
            public void handle(Event arg0) {
                mainBox.showBox(startBox);
            }
        });
    }

    private void setActionOnAbout(Label about) {
        about.setOnMouseClicked(new EventHandler<Event>() {
            @Override
            public void handle(Event arg0) {
                mainBox.showBox(infoBox);
            }
        });
    }

    private CheckMenuItem automConv() {
        startBox.textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> arg0, Boolean textAreaFocus,
                    Boolean textFieldFocus) {

                String clipboardContent = clipboard.getString();
                if (autoConvertCheckMenuItem.isSelected() && clipboard.hasString() && textFieldFocus) {
                    startBox.textField.setText(clipboardContent);
                    showResultAndAddLineToTable();
                    autoCopy();
                }
            }
        });
        return autoConvertCheckMenuItem;
    }

    private CheckMenuItem autoCopy() {
        if (autoCopyCheckMenuItem.isSelected()) {
            ClipboardContent clipboardContent = new ClipboardContent();
            String copyText = startBox.textArea.getText();
            clipboardContent.putString(copyText);
            clipboard.setContent(clipboardContent);
        }
        return autoCopyCheckMenuItem;
    }

    private void showResultAndAddLineToTable() {
        String newValue = startBox.textField.getText();
        try {

            startBox.textArea.setText(ConverterTools2.patternQuery(newValue));
        } catch (PatternQueryException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception unhandledError) {
            unhandledError.printStackTrace(); // todo auslagern in logger
        }
        printTableLine.add(new Result2(newValue, startBox.textArea.getText()));

    }

    private void initTable() {
        TableColumn<Result2, String> inputColumn = new TableColumn<>("Eingabe");
        TableColumn<Result2, String> outputColumn = new TableColumn<>("Ausgabe");
        inputColumn.setCellValueFactory(tableInputLambda -> tableInputLambda.getValue().inputProperty());
        outputColumn.setCellValueFactory(tableOutputLambda -> tableOutputLambda.getValue().outputProperty());
        inputColumn.setMinWidth(135);
        outputColumn.setMinWidth(135);
        startBox.table.setMaxHeight(270);
        startBox.table.setMaxWidth(270);
        startBox.table.setItems(printTableLine);
        startBox.table.getColumns().add(inputColumn);
        startBox.table.getColumns().add(outputColumn);
    }

}
Eine Klasse die mir bei Fensterwechsel Hilft alle Elemente wieder zu bekommen
Java:
package application;

import javafx.scene.Scene;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ConverterMainBox2 extends VBox {
    MenuBar _menuBar;
    HBox hbox;

    public ConverterMainBox2(Stage stage, MenuBar menuBar) {
        this._menuBar = menuBar;
        Scene scene = new Scene(this, 515, 300);
        stage.setScene(scene);
        stage.show();
    }

    public void showBox(Pane box) {
        initBox();
        this.getChildren().add(box);
    }

    private void initBox() {
        this.getChildren().clear();
        this.getChildren().add(_menuBar);
    }

}
Eine Klasse mit der Logik für das Konvertieren:
Java:
package application;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Pattern;

public class ConverterTools2 {

    private static final String NUMBERPATTERN = "^[0-9]*$";
    private static final String DATEPATTERN = "^\\d\\d\\d\\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01]) (00?[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$";

    /**
    * 
    * @param toCheck patternQuery checks the users input and matches it with four
    *                different patterns. If matched with either Epoch time or a
    *                date with the pattern "yyyy-MM-dd HH:mm:ss" a conversion will
    *                take place into the other pattern. Following patterns will
    *                deliver return values:
    *                <ol>
    *                <li>""
    *                <li>A date in the format "yyyy-MM-dd HH:mm:ss"
    *                <li>A number pattern containing only digits from 0-9
    *                <li>literally anything else
    *                </ol>
    * @return
    *         <ol>
    *         <li>"Your can't convert from nothing"
    *         <li>Epoch time in milliseconds
    *         <li>Date with the format "yyyy-MM-dd HH:mm:ss"
    *         <li>Value you entered + " is not a valid entry"
    *         </ol>
    */
    public static String patternQuery(String toCheck) throws PatternQueryException {

        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date parsInToDate = new Date();

        if (toCheck.isEmpty()) {
            throw new PatternQueryException(PatternQueryException.Reason.EMPTY_QUERY);
        } else if (Pattern.matches(DATEPATTERN, toCheck)) {
            long epochMillis;
            epochMillis = dateToEpochTime(sdf, toCheck, parsInToDate);
            return epochMillis + "";

        } else if (Pattern.matches(NUMBERPATTERN, toCheck)) {
            Calendar calendar = epochTimeToDate(Long.parseLong(toCheck));
            return sdf.format(calendar.getTime());
        }

        throw new PatternQueryException(PatternQueryException.Reason.INVALID_QUERY);
    }

    private static long dateToEpochTime(SimpleDateFormat sdf, String textFieldInput, Date parsInToDate) {
        long epochMillis;
        try {
            parsInToDate = sdf.parse(textFieldInput);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        epochMillis = parsInToDate.getTime();
        return epochMillis;
    }

    private static Calendar epochTimeToDate(Long timestamp) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(timestamp);
        return calendar;
    }
}
Eine Klasse für eine Infobox (Hersteller, Version, usw.)
Java:
package application;

import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;

public class InfoBox2 extends HBox {

    public InfoBox2() {
        createInfoBox();
    }

    protected void createInfoBox() {
        Label authorLabel = new Label("Hersteller: ");
        Label versionLabel = new Label("Version: \n\n");
        HBox.setMargin(authorLabel, new Insets(20, 20, 20, 20));
        HBox.setMargin(versionLabel, new Insets(20, 20, 20, 20));
        getChildren().addAll(authorLabel, versionLabel);
    }

}
Eine eigene Exception Klasse
Java:
package application;

public class PatternQueryException extends IllegalArgumentException {

    private static final long serialVersionUID = 8696342062082168859L;

    /**
    * 
    * @author xxx
    *
    */

    public enum Reason {
        EMPTY_QUERY("You can not convert an empty input"), INVALID_QUERY("Your input is invalid");

        private String text;

        Reason(String text) {
            this.text = text;
        }

        public String getText() {
            return text;
        }
    };

    private Reason reason;

    public PatternQueryException(Reason reason) {
        super(reason.getText());
        this.reason = reason;
    }

    public Reason getReason() {
        return reason;
    }

}
Eine Klasse die mir hilft Konvertierungsergebnisse in die Tabelle zu bekommen
Java:
package application;

import javafx.beans.property.SimpleStringProperty;

public class Result2 {

    private SimpleStringProperty input;
    private SimpleStringProperty output;

    public Result2(String input, String output) {
        this.input = new SimpleStringProperty(input);
        this.output = new SimpleStringProperty(output);
    }

    public SimpleStringProperty inputProperty() {
        return input;
    }

    public SimpleStringProperty outputProperty() {
        return output;
    }

}
Und zuletzt noch eine Klasse für die Startseite wenn man das Programm öffnet
Java:
package application;

import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;

public class StartBox2 extends HBox {

    public Button convertButton = new Button("Convert");
    public TextField textField = new TextField();
    public TextArea textArea = new TextArea();
    public TableView<Result2> table = new TableView<>();

    public StartBox2() {
        createStartBox();
    }

    /**
    * create() combines all necessary components to build the start page of the
    * Converter.
    */

    protected void createStartBox() {
        VBox leftBox = new VBox();
        HBox buttonBox = new HBox();
        HBox tableBox = new HBox();
        VBox.setMargin(textField, new Insets(40, 20, 20, 20));
        VBox.setMargin(textArea, new Insets(20, 20, 20, 20));
        HBox.setMargin(convertButton, new Insets(20, 20, 20, 20));
        textField.setMaxSize(200, 200);
        textArea.setMaxSize(200, 200);
        textField.setPromptText("Zu konvertierenden Wert eingeben");
        textArea.setPromptText("Ergebnisfeld");
        buttonBox.getChildren().addAll(convertButton);
        buttonBox.setAlignment(Pos.CENTER);
        tableBox.getChildren().addAll(table);
        leftBox.getChildren().addAll(textField, textArea, buttonBox);
        leftBox.setAlignment(Pos.CENTER);
        getChildren().addAll(leftBox, tableBox);
    }

}
 
Du rufst automConv in start und in createMenu auf und registrierst dabei jedesmal einen Listener bei startBox.textField...
Ja stimmt, aber das hätte mein Problem im nachhinein leider nicht gelöst.
Habe einen anderen Weg gefunden falls jemand interessiert ist:
Java:
    private CheckMenuItem automConv() {
        startBox.textField.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {

                final String DATEPATTERN = "^\\d\\d\\d\\d-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01]) (00?[0-9]|1[0-9]|2[0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$";
                final String NUMBERPATTERN = "^[0-9]*$";

                if (autoConvertCheckMenuItem.isSelected() && (newValue.matches(DATEPATTERN)
                        || (newValue.matches(NUMBERPATTERN) && newValue.length() == 13))) {
                    showResultAndAddLineToTable();
                    autoCopy();
                }
            }
        });
        return autoConvertCheckMenuItem;
    }
 

Neue Themen


Zurück
Oben