Vergleich vieler Strings ohne ellenlange Argumentierten

Fiedlerdan

Mitglied
Hallo zusammen.

War erstmal schwierig eine passende Überschrift zu finden. // Edit: und nun ist auch noch ein Schreibfehler drin -.-

Ich stehe vor einem kleinem Problem, auf dass ich so spezifisch im Netz keine Antwort finde.

In der unten eingebetteten Klasse befindet sich eine Tabelle die aus einer SQL Datenbank die Daten zieht.
Wenn eine Zeile angeklickt wird, wird das gewählte Objekt in der Variable "cache" zwischengespeichert, in den Textfields darüber wiedergegeben, und kann dort bearbeitet und über den Speicherbutton wieder in die Datenbank geschrieben werden.

Nun zu meinem "Problem":
Ich möchte, dass der Speicherbutton erst dann aktiv wird, wenn irgendetwas in den Zeilen geändert wird.

Ich weiß, dies könnte ich über einen textChangeListener bewerkstelligen. Ich will aber vermeiden, in dem dazu nötigen boolean insgesamt 26 Argumente vergleichen zu müssen (14 Textfelder via .getText() mit 14 variablen aus dem PersonData Objekt).

Meine Java Kenntnisse habe ich mir weitestgehend selbst angeeignet, daher bin ich da etwas unbeholfen und der Code wird wohl auch etwas umständlich wirken, daher diese Fragestellung hier Also seht mir die ein oder anderen Konventionsbrüche bitte nach.

Hier der Code (nur aus der betreffenden Klasse). Dieser ist auch noch nicht fertig, also nicht über die leeren Methoden wundern

Ich danke euch im Voraus für die Ratschlage!

Java:
package de.morpheus.modules.baseData;

import java.net.URL;
import java.util.ResourceBundle;
import de.morpheus.core.CSSContainer;
import de.morpheus.core.Selectable;
import de.morpheus.core.system.XString;
import de.morpheus.core.ui.Confirmator;
import de.morpheus.core.ui.Dialog;
import de.morpheus.core.ui.XWindow;
import de.morpheus.core.ui.XWindow.ConfirmType;
import de.morpheus.core.ui.XWindow.OfType;
import de.morpheus.init.Core;
import de.morpheus.library.PersonData;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;

public class PersonBrowserController implements Initializable, CSSContainer, Selectable {

  private BaseDataModel baseDataModel = new BaseDataModel();

  private PersonData cache = null;

  @FXML
  private ComboBox<String> cboxSalut;

  @FXML
  private Button butExit;

  @FXML
  private Button butNew;

  @FXML
  private Button butErase;

  @FXML
  private Button butSearch;

  @FXML
  private TextField inLastName;

  @FXML
  private TextField inFirstName;

  @FXML
  private TextField inAdress1;

  @FXML
  private TextField inAdress2;

  @FXML
  private TextField outID;

  @FXML
  private TextField inZipCode;

  @FXML
  private TextField inCity;

  @FXML
  private ComboBox<String> cboxState;

  @FXML
  private TextField inPhone;

  @FXML
  private TextField inEmailAdress;

  @FXML
  private TextField inIban;

  @FXML
  private TextField inBic;

  @FXML
  private TextField inBank;

  @FXML
  private TableView<PersonData> outputTable;

  @FXML
  private TableColumn<PersonData, Integer> colId;

  @FXML
  private TableColumn<PersonData, String> colLastName;

  @FXML
  private TableColumn<PersonData, String> colFirstName;

  @FXML
  private TableColumn<PersonData, String> colAdress;

  @FXML
  private TableColumn<PersonData, String> colCountry;

  @FXML
  private TableColumn<PersonData, Integer> colPLZ;

  @FXML
  private TableColumn<PersonData, String> colCity;

  @FXML
  private TableColumn<PersonData, String> colPhone;

  @FXML
  private TableColumn<PersonData, String> colEMail;

  @FXML
  private Button butSave;

  @FXML
  void clearAllFields(ActionEvent event) {

  }

  @FXML
  void doQuery(ActionEvent event) {
    refreshTable();
  }

  @FXML
  void execute(MouseEvent event) {

  }

  @FXML
  void exitWindow(ActionEvent event) {
    XWindow.close(butExit);
  }

  @FXML
  void openAddWindow(ActionEvent event) {

  }

  @FXML
  void savePerson(ActionEvent event) {

    boolean accept = Confirmator.open("Alle Änderungen speichern?", ConfirmType.STANDARD);

    if (accept) {
      cache.setLastName(XString.read(inLastName));
      cache.setFirstName(XString.read(inFirstName));
      cache.setAdress1(XString.read(inAdress1));
      cache.setAdress2(XString.read(inAdress2));
      cache.setZipCode(Integer.parseInt(inZipCode.getText()));
      cache.setCity(XString.read(inCity));
      cache.setEmailAdress(XString.read(inEmailAdress));
      cache.setPhone(XString.read(inPhone));
      cache.setIban(XString.read(inIban));
      cache.setBic(XString.read(inBic));
      cache.setBank(XString.read(inBank));

      String transSalut = cboxSalut.getSelectionModel().getSelectedItem();
      String transState = cboxState.getSelectionModel().getSelectedItem();
      cache.setSalutation(transSalut);
      cache.setState(transState);

      boolean write = false;
      try {
        write = baseDataModel.updateSQLDataset(Core.database.getStatement(), cache);
      } catch (Exception e) {
        Dialog.open(e, "PersonBrowserController.java");
      }

      if (write) {
        Dialog.openSimple(OfType.SUCCESS, "Datensatz aktualisiert");
        refreshTable();
      } else {
        Dialog.openSimple(OfType.ERROR, "Fehlgeschlagen");
      }
     
    }

  }

  private void refreshTable() {
    Core.getPersons();
    outputTable.setItems(Core.persons);
  }

  private void showData() {

    outID.setText(String.valueOf(cache.getId()));

    for (String string : SALUT_STRING) {
      if (string.equalsIgnoreCase(cache.getSalutation())) {
        int index = SALUT_STRING.indexOf(string);
        cboxSalut.getSelectionModel().select(index);
      }
    }

    for (String string : COUNTRY_CHOOSER) {
      if (string.equalsIgnoreCase(cache.getState())) {
        int index = COUNTRY_CHOOSER.indexOf(string);
        cboxState.getSelectionModel().select(index);
      }
    }

    inLastName.setText(cache.getLastName());
    inFirstName.setText(cache.getFirstName());
    inAdress1.setText(cache.getAdress1());
    inAdress2.setText(cache.getAdress2());
    inZipCode.setText(String.valueOf(cache.getZipCode()));
    inCity.setText(cache.getCity());
    inEmailAdress.setText(cache.getEmailAdress());
    inPhone.setText(cache.getPhone());
    inIban.setText(cache.getIban());
    inBic.setText(cache.getBic());
    inBank.setText(cache.getBank());

  }

  @Override
  public void initialize(URL location, ResourceBundle resources) {

    colId.setCellValueFactory(new PropertyValueFactory<>("id"));
    colLastName.setCellValueFactory(new PropertyValueFactory<>("lastName"));
    colFirstName.setCellValueFactory(new PropertyValueFactory<>("firstName"));
    colAdress.setCellValueFactory(new PropertyValueFactory<>("adress1"));
    colCountry.setCellValueFactory(new PropertyValueFactory<>("state"));
    colPLZ.setCellValueFactory(new PropertyValueFactory<>("zipCode"));
    colCity.setCellValueFactory(new PropertyValueFactory<>("city"));
    colPhone.setCellValueFactory(new PropertyValueFactory<>("phone"));
    colEMail.setCellValueFactory(new PropertyValueFactory<>("emailAdress"));

    cboxSalut.setItems(FXCollections.observableArrayList(SALUT_STRING));
    cboxState.setItems(FXCollections.observableArrayList(COUNTRY_CHOOSER));
    cboxState.getSelectionModel().select(2);
    refreshTable();

    outputTable.setOnMouseClicked(new EventHandler<MouseEvent>() {

      @Override
      public void handle(MouseEvent event) {

        if (event.getClickCount() == 2) {

        } else {

          cache = outputTable.getSelectionModel().getSelectedItem();

          showData();

        }

      }
    });


  }

}
 

looparda

Top Contributor
Hier eine schreckliche Eigenimplementierung. Es werden zwei Maps geführt.. eine mit den Initialwerten der Textfelder und die andere mit den Initialwerten der Textfelder + Änderungen an ihnen. Wenn die Maps nicht mehr gleich sind, dann gab es eine Änderung.
Java:
import javafx.beans.binding.ObjectBinding;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextField;

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

public class ChangeBindingProperty extends ObjectBinding<Boolean> {
    private final Map<TextField, String> mapInitial;
    private final Map<TextField, String> mapAfter;

    public ChangeBindingProperty(TextField... textfields) {
        this.mapInitial = new HashMap<>();
        this.mapAfter = new HashMap<>();

        for (TextField textfield : textfields) {
            mapInitial.put(textfield, textfield.getText());
            mapAfter.put(textfield, textfield.getText());
            textfield.textProperty().addListener(new ChangeListener(textfield));
        }
    }

    @Override
    protected Boolean computeValue() {
        return !mapInitial.equals(mapAfter);
    }

    private class ChangeListener implements javafx.beans.value.ChangeListener<String> {
        TextField textField;

        public ChangeListener(final TextField textField) {
            this.textField = textField;
        }

        @Override
        public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
            mapAfter.put(textField, newValue);
            invalidate();
        }
    }

}
Java:
public class Controller implements Initializable {

    @FXML
    private Button butSave;

    @FXML
    private TextField inFirstName;

    @FXML
    private TextField inLastName;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // Simulate loading Database entries
        inFirstName.setText("Firstname");
        inLastName.setText("Lastname");
        // Binding
        butSave.disableProperty().bind(new ChangeBindingProperty(inFirstName, inLastName));
    }
}
 
Zuletzt bearbeitet:

looparda

Top Contributor
return !mapInitial.equals(mapAfter);
Und natürlich habe ich die Negation versemmelt. Wenn die Maps gleich sind und equals somit true, dann soll die disabledProperty auf true stehen.

Außerdem ist die Oberklasse BooleanBinding besser geeignet und lässt sich somit mit der Bindings API nutzen.
Damit sind Sachen möglich wie (hier wieder die Negation in ChangeBindingProperty vorausgesetzt):
Java:
BooleanBinding textFieldsChanged = new ChangeBindingProperty(inFirstName, inLastName);
butSave.disableProperty()
       .bind(Bindings.not(Bindings.and(textFieldsChanged, inLastName.textProperty()
                                                                    .length()
                                                                    .greaterThan(8))));
 

lam_tr

Top Contributor
Und natürlich habe ich die Negation versemmelt. Wenn die Maps gleich sind und equals somit true, dann soll die disabledProperty auf true stehen.

Außerdem ist die Oberklasse BooleanBinding besser geeignet und lässt sich somit mit der Bindings API nutzen.
Damit sind Sachen möglich wie (hier wieder die Negation in ChangeBindingProperty vorausgesetzt):
Java:
BooleanBinding textFieldsChanged = new ChangeBindingProperty(inFirstName, inLastName);
butSave.disableProperty()
       .bind(Bindings.not(Bindings.and(textFieldsChanged, inLastName.textProperty()
                                                                    .length()
                                                                    .greaterThan(8))));

Zwei Fragen dazu, wenn du schon dabei bist :)
* Das mit dem computeValue habe ich noch nicht verstanden. Soll es nicht so sein, dass er die zwei Maps auswertet, wenn die nicht gleich sind, dann hat da ein Change stattgefunden?
* wofür wird die invalidate() Methode aufgerufen, etwa zum internen Aufrufen des computeValue()?
 

Fiedlerdan

Mitglied
Hey Danke, das mit den Maps ist ja schonmal ein richtig guter Wink mit dem Zaunpfahl.
Im Prinzip sollen ja die Werte der TextFelder mit den Werten im Objekt verglichen werden.

Kann ich nicht auch im Prinzip ein zweites PersonData Objekt erstellen, welches immer dann per setter angepasst wird, sobald ein textChange vorliegt? Dann spare ich mir lange Argumente und kann praktisch dann so vorgehen:

Code:
private PersonData cache1 = new PersonData();
private PersonData cache2 = new PersonData();

textField.textProperty().addListener((observable, oldValue, newValue) -> {
    cache2.setFirstName = textfield.getText();
   
    if (!chache1.equals(cache2)){
    butSave.setDisable(false);
    } else{
    butSave.setDisable(true);
    }
});

wäre das so in etwa möglich? (Wenn man davon ausgeht das die ausgewählte Tabellenzeile in cache1 geladen wurde)
 

mrBrown

Super-Moderator
Mitarbeiter
Ein extra Objekte dafür zu nutzen wäre mMn der Way to go.

Für die genaue Umsetzung gibts X Varianten, man könnte auch ein explizites PersonViewModel und ein Person-Objekt einführen. Letzteres ist dann jeweils da, was aus der DB kommt, in der DB gespeichert wird. Erstes wird an die View gebunden („spiegelt“ diese), wrappt eine Person und kann dann jeweils auf Gleichheit prüfen und das als Property bereit stellen. Ein Beispiel kann ich dazu gleich schreiben wenn ich am Rechner bin.

Kannst statt einem expliziten ViewModel natürlich zwei „normale“ Objekte nutzen, wie du das in deinem letzten Beispiel ja auch machst, mit einer expliziten Trennung geht aber einiges schöner.
 

looparda

Top Contributor
* Das mit dem computeValue habe ich noch nicht verstanden. Soll es nicht so sein, dass er die zwei Maps auswertet, wenn die nicht gleich sind, dann hat da ein Change stattgefunden?
Genau, die Methode sollte true zurückgeben, wenn es eine Änderung gab, um sich "erwartungsgemäß" zu verhalten. Da im Controller jedoch disableProperty genutzt wird (true für deaktiv, false für aktiv), also das Deaktivieren des Button hat man hier schon eine Negation der Logik. Das hatte ich im zweiten Beitrag verbessert.
* wofür wird die invalidate() Methode aufgerufen, etwa zum internen Aufrufen des computeValue()?
Im Controller wird die disableProperty an die changeBindingProperty gebunden. Um die Observer über Änderungen zu benachrichtigen, muss man das Objekt auf invalid setzten.

Kann ich nicht auch im Prinzip ein zweites PersonData Objekt erstellen, welches immer dann per setter angepasst wird, sobald ein textChange vorliegt?
Vom Prinzip her ja, aber du hast massenweise Listener und ähnlichen Code.

Ich würde ebenfalls ein ViewModel bevorzugen und dort gehört dann auch das "dirty"/"changed"-Flag rein.
1. Man könnte die ChangeBindingProperty evtl. so anpassen, dass sie für alle Controls anwendbar ist.
2. Die Properties, die im View an die Properties aus dem ViewModel gebunden werden kapseln, um Änderungen abzufangen/mitzubekommen. Also einfache Wrapper, die das dirty-Flag setzen und dann wieder an die eigentliche Property delegieren.
3. wie von @mrBrown vorgeschlagen die Gleichheit von ViewModel und Model prüfen.
 

Fiedlerdan

Mitglied
Hier eine schreckliche Eigenimplementierung. Es werden zwei Maps geführt.. eine mit den Initialwerten der Textfelder und die andere mit den Initialwerten der Textfelder + Änderungen an ihnen. Wenn die Maps nicht mehr gleich sind, dann gab es eine Änderung.

Ich hab mir das jetzt mal genauer angeschaut, und diese Implementierung wird für meinen Zweck nicht funktionieren.
Die Daten aus der Eingabe werden ja mit den Daten aus dem Objekt verglichen. Das wäre kein problem wenn ich zweierlei varargs im Argument nutzen könnte, aber java erlaubt da logischerweise nur ein Array.

Ich glaube fast ich werde nicht drum herum kommen die Strings einzeln zu vergleichen...
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
D Swing Warum erhalte ich keine Ausgabe? Funktioniert der equals-vergleich in actionPeformed nicht richtig? AWT, Swing, JavaFX & SWT 3
W Vergleich zweier Strings und schreiben in Textarea AWT, Swing, JavaFX & SWT 12
Spin Fehler in Vergleich AWT, Swing, JavaFX & SWT 6
B NullPointerException bei String vergleich AWT, Swing, JavaFX & SWT 5
L Vectorinhalt vergleich in Bedingun AWT, Swing, JavaFX & SWT 13
J Frage zu JTextField - Vergleich AWT, Swing, JavaFX & SWT 2
U suchverfahren vergleich einbinden von panels ? in applet ;) AWT, Swing, JavaFX & SWT 11
S Vergleich: SWT vs. Swing AWT, Swing, JavaFX & SWT 11
D 3D-Grafik Erstellen vieler gleicher Würfel AWT, Swing, JavaFX & SWT 5
T Zeichnen vieler Messwerte --> Zeichnung wird nie fertig AWT, Swing, JavaFX & SWT 4
G JList, einzelne Strings hinzufügen AWT, Swing, JavaFX & SWT 9
Justin09 Strings prüfen AWT, Swing, JavaFX & SWT 4
H setToolTipText ignoriert HTML-Formatierung des anzuzeigenden Strings AWT, Swing, JavaFX & SWT 4
G Button Strings Effektiver setzen AWT, Swing, JavaFX & SWT 3
P GUI Ausgabe des Strings AWT, Swing, JavaFX & SWT 3
D JComboBox Strings aus JTextFields zuordnen AWT, Swing, JavaFX & SWT 2
B ArrayList, Strings anzeigen AWT, Swing, JavaFX & SWT 1
M Strings im JTextPane vergleichen und mit StyledDocument formatieren AWT, Swing, JavaFX & SWT 3
J Swing Strings werden nicht in Textfeld geschrieben AWT, Swing, JavaFX & SWT 8
MrSnake Tabelle nach 2 Strings Filtern AWT, Swing, JavaFX & SWT 3
MiMa Umlaute in JavaFX GUI Strings AWT, Swing, JavaFX & SWT 5
J Swing Vertikales Zeichnen eines Strings mit Java2D AWT, Swing, JavaFX & SWT 1
B jTextfield Übergabe des Strings AWT, Swing, JavaFX & SWT 16
G Mehrere Strings um Kreis zeichnen und positionieren AWT, Swing, JavaFX & SWT 0
vodkaz JTextField && Strings AWT, Swing, JavaFX & SWT 2
R FontMetrics - Ausmaße eines Strings AWT, Swing, JavaFX & SWT 4
A Swing Anpassen der Spaltenbreite durch die länge eines Strings AWT, Swing, JavaFX & SWT 3
M breite eines "drawString()-Strings" AWT, Swing, JavaFX & SWT 2
J Swing JList... mehrere Strings in Zeile AWT, Swing, JavaFX & SWT 8
M SWT Jface Action und localised Strings AWT, Swing, JavaFX & SWT 6
F Pixelhöhe eines Strings AWT, Swing, JavaFX & SWT 2
R Pixelmaße eines zu zeichnenden Strings AWT, Swing, JavaFX & SWT 3
ElViZ End-Position eines gezeichneten Strings ermitteln. AWT, Swing, JavaFX & SWT 2
Pithecanthropus [gelöst] JTree, aber nicht mit Strings, sondern mit Objects? AWT, Swing, JavaFX & SWT 2
K Strings aus jTextArea in Liste oder Tabelle übertragen- wie? AWT, Swing, JavaFX & SWT 4
A Strings an Textarea anderer Klasse append(en) AWT, Swing, JavaFX & SWT 12
D Einzelnen Worte eines Strings farbig machen AWT, Swing, JavaFX & SWT 11
M Farbe eines strings in JTextArea AWT, Swing, JavaFX & SWT 2
A Strings in JTable fett ausgeben - ohne! HTML AWT, Swing, JavaFX & SWT 10
N Pixelbreite eines Strings AWT, Swing, JavaFX & SWT 11
B Länge eines Strings in Pixel AWT, Swing, JavaFX & SWT 2
K Problem mit TextField Strings AWT, Swing, JavaFX & SWT 2
G Verzeichnis Strings im JTree AWT, Swing, JavaFX & SWT 9
R Strings aus einer .txt laden AWT, Swing, JavaFX & SWT 8
M Höhe von eines Strings in Pixeln (nicht Höhe einer Zeile) AWT, Swing, JavaFX & SWT 12
W Strings diagonal zeichnen AWT, Swing, JavaFX & SWT 2
TRunKX Kommentarfeld schneidet die Strings ab AWT, Swing, JavaFX & SWT 13
N Laenge eines Strings in Pixel AWT, Swing, JavaFX & SWT 2
R Steuerzeichen eines Strings (z.B. ENTER) zurückwandeln AWT, Swing, JavaFX & SWT 3
S Anzahl ActionEvents (Strings in eine ArrayList einfügen) AWT, Swing, JavaFX & SWT 4
N Checkbox mit mehreren Strings belegen? AWT, Swing, JavaFX & SWT 6

Ähnliche Java Themen

Neue Themen


Oben