Problem mit TextField.requestFocus();

Juelin

Bekanntes Mitglied
Halllo,
ich habe ein TextField in dem ich eine Eingabe machen möchte wenn ich einen Button drücke.
Das TextField soll die Backgroundfarebe GRAY haben wenn es nicht eingabebereit ist und
die Farbe WHITE wenn es eingabebereit ist.
Zu diesem Zweck habe ich eine Routine (wert = boolean, ist True=eingabebereit und False=nicht eingabebereit). (edit1=TextField).
Java:
            edit1.setEditable(wert);
            if (wert)
                {
                edit1.setBackground(new Background(new BackgroundFill(Color.WHITE,  CornerRadii.EMPTY, Insets.EMPTY)));
                s = edit1.getText();
                l = s.length();
                edit1.positionCaret(l);
                edit1.requestFocus();
                }
            else
                {
                edit1.setBackground(new Background(new BackgroundFill(Color.GRAY,  CornerRadii.EMPTY, Insets.EMPTY)));
                s = edit1.getText();
                l = s.length();
                edit1.positionCaret(l);
                Ereignis.fensterFelder.einfeldLauf = 0;
                }

Nun zu meinem Problem:
Wenn ich den Befehl: edit1.requestFocus(); drin habe wird das TextField nicht Weiss.
Wenn ich den Befehl: edit1.requestFocus(); raus nehme wird das TextField Weiss

Kann mir das jemand erklären?.
Den Befehl edit1.requestFocus(); brauche ich ja, damit der Cursor im TextField steht wenn es eingabebereit ist
und man es nicht extra anklicken muß.

Danke und Gruß
Jürgen
 
Ich habe das einmal nachgebaut und kann das Verhalten nicht nachvollziehen. Kannst Du mehr von Deinem Code zeigen? Hast noch irgendwelche anderen Styles gesetzt oder so?

Mein Controller:
Java:
package de.kneitzel;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.paint.Color;

import java.net.URL;
import java.util.ResourceBundle;

public class MainWindow implements Initializable {

    private boolean wert = false;
    @FXML
    private TextField textField;
    public Button button;
    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
    }

    @FXML
    private void onButtonClick(ActionEvent actionEvent) {
        wert = !wert;
        textField.setEditable(wert);
        if (wert)
        {
            textField.setBackground(new Background(new BackgroundFill(Color.WHITE,  CornerRadii.EMPTY, Insets.EMPTY)));
            String s = textField.getText();
            int l = s.length();
            textField.positionCaret(l);
            textField.requestFocus();
        }
        else
        {
            textField.setBackground(new Background(new BackgroundFill(Color.GRAY,  CornerRadii.EMPTY, Insets.EMPTY)));
            String s = textField.getText();
            int l = s.length();
            textField.positionCaret(l);
        }
    }
}

Mein fxml:
XML:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane prefHeight="127.0" prefWidth="209.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.kneitzel.MainWindow">
   <children>
      <Button fx:id="button" layoutX="44.0" layoutY="70.0" mnemonicParsing="false" onAction="#onButtonClick" text="Button" />
      <TextField fx:id="textField" layoutX="14.0" layoutY="24.0" />
   </children>
</AnchorPane>

Und die Hintergrundfarbe ändert sich bei jedem Click auf den Button.
 
Und da ich ja immer auf Stylesheet hinweise: Hier wäre ein mögliches CSS etwas wie:
CSS:
.text-field {
    -fx-background-color: white;
}

.text-field:readonly {
    -fx-background-color: grey;
}
 
Zuletzt bearbeitet:
Das Pseudoattribut dürfte in FX readonly (ohne Bindestrich) heißen.
Du hast natürlich Recht. Das sagt mir sogar IntelliJ (mehr oder weniger) wenn ich es zum ausprobieren ins Projekt nehme: "Unknown pseudo selector 'read-only'"

Danke für den Hinweis und ich korrigiere es direkt auch in meinem Post.
 
Hallo Konrad,
Danke für deine Bemühungen.
ZIP-File anbei.
Ist im File Ereignis2Controller Routine setEingabeEditable.
Gruß
Jürgen
 

Anhänge

Hallo Konrad,
mir ist noch was aufgefallen.
Wenn ich den Befehl edit1.requestFocus(); raus nehme, so wird das TextField zwar Weiss, hat aber keinen Focus.
Wenn ich dann auf das TextField klicke wird es wieder Grau.
Das hilf vielleicht.
Gruß
Jürgen.
 
Halllo,
ich habe ein TextField in dem ich eine Eingabe machen möchte wenn ich einen Button drücke.
Das TextField soll die Backgroundfarebe GRAY haben wenn es nicht eingabebereit ist und
die Farbe WHITE wenn es eingabebereit ist.
Zu diesem Zweck habe ich eine Routine (wert = boolean, ist True=eingabebereit und False=nicht eingabebereit). (edit1=TextField).
Java:
            edit1.setEditable(wert);
            if (wert)
                {
                edit1.setBackground(new Background(new BackgroundFill(Color.WHITE,  CornerRadii.EMPTY, Insets.EMPTY)));
                s = edit1.getText();
                l = s.length();
                edit1.positionCaret(l);
                edit1.requestFocus();
                }
            else
                {
                edit1.setBackground(new Background(new BackgroundFill(Color.GRAY,  CornerRadii.EMPTY, Insets.EMPTY)));
                s = edit1.getText();
                l = s.length();
                edit1.positionCaret(l);
                Ereignis.fensterFelder.einfeldLauf = 0;
                }

Nun zu meinem Problem:
Wenn ich den Befehl: edit1.requestFocus(); drin habe wird das TextField nicht Weiss.
Wenn ich den Befehl: edit1.requestFocus(); raus nehme wird das TextField Weiss

Kann mir das jemand erklären?.
Den Befehl edit1.requestFocus(); brauche ich ja, damit der Cursor im TextField steht wenn es eingabebereit ist
und man es nicht extra anklicken muß.

Danke und Gruß
Jürgen
Probiere mal: Platform.runLater(() -> edit1.requestFocus());
 
Frage welche Farbe hat den ein Standard Textfeld wenn es den Fokus hast zur Eingabe?

Sehe in deinem Code auch das du zb in der „Feldersetzen“ Methode auch Styles machst.
Schaue doch mal ob du nicht etwa wo anders die Farben änderst beim Fokus ändern.
 
Dein Code ist extrem schwer zu lesen, da Du leider immer noch - unnötiger Weise - zu sehr viel Code neigst. Lange Methoden, mit tief verschachtelten if Befehlen.

Es wäre an der Stelle schön, wenn man es wenigstens selbst testen könnte, aber das geht leider auch nicht, denn wir haben ja die Datenbank nicht.

Was auffällt: in Ereignis versuchst Du - bei einem Fehler beim Öffnen der Datenbank - den Text von label2 zu setzen. Du hast aber kein label2. Du bist ja nicht in einem Controller sondern in der Klasse, die von Application erbt. Ein @FXML Label label2; wird Dir daher kein Label geben.

Mein Tipp wäre daher weiterhin: arbeite doch mit Styles. Dann musst Du so viele Dinge nicht machen.

Und dann werde diese Zugriffe per Nummer los. Also dieses if (einfeldNum == 1) { /* verändere edit1 *} und das dann auch für == 2, ==3, ...

Einfach einmal ein paar Dinge, die Du ändern könntest:

Wenn du mehrfach immer das Gleiche machst, dann kannst Du es in eine Methode packen und dann das, was ich unterscheidet, in einen Parameter packen. So hast Du für edit1, edit2, edit3, ... in einer Methode immer dies hier:
Java:
if (einfeldNum == 3)
            {
            if (Ereignis.mitLoggen)
                {
                Ereignis.logger1.info("Logging <Ereignis2Controller.java><setEingabeEditable><edit3 begin>");   
                }
            edit3.setEditable(wert);
            if (wert)
                {
                edit3.setBackground(new Background(new BackgroundFill(Color.WHITE,  CornerRadii.EMPTY, Insets.EMPTY)));
                s = edit3.getText();
                l = s.length();
                edit3.positionCaret(l);
                }
            else
                {
                edit3.setBackground(new Background(new BackgroundFill(Color.GRAY,  CornerRadii.EMPTY, Insets.EMPTY)));
                Ereignis.fensterFelder.einfeldLauf = 0;
                }
            }

Das kannst Du in eine Methode packen. Das wäre dann eine Methode wie:
Java:
void setEingabeEditable(TextField textField, Boolean wert) {
    if (Ereignis.mitLoggen) {
        Ereignis.logger1.info("Logging <Ereignis2Controller.java><setEingabeEditable><edit1 begin>");   
    }
    
    textField.setEditable(wert);
    
    if (wert) {
        if (Ereignis.mitLoggen) {
            Ereignis.logger1.info("Logging <Ereignis2Controller.java><setEingabeEditable><edit1 WEIS>");   
        }
        
        textField.setBackground(new Background(new BackgroundFill(Color.WHITE,  CornerRadii.EMPTY, Insets.EMPTY)));
        String s = edit1.getText();
        int l = s.length();
        textField.positionCaret(l);
//      textField.requestFocus();
    } else {
        if (Ereignis.mitLoggen) {
            Ereignis.logger1.info("Logging <Ereignis2Controller.java><setEingabeEditable><edit1 GRAU>");   
        }
        
        textField.setBackground(new Background(new BackgroundFill(Color.GRAY,  CornerRadii.EMPTY, Insets.EMPTY)));
        Ereignis.fensterFelder.einfeldLauf = 0;
    }
    if (Ereignis.mitLoggen) {
        Ereignis.logger1.info("Logging <Ereignis2Controller.java><setEingabeEditable><edit1 ende>");   
    }
}

Wenn Du dann die Methode immer noch mit dem int haben willst (Statt 1 könntest Du beim Aufruf aber doch auch schon edit1 übergeben?), dann wäre dies nur noch etwas wie:
Java:
void setEingabeEditable(int einfeldNum, Boolean wert) {
    switch (einfeldNum) {
        case 1:
            setEingabeEditable(edit1, wert);
            break;
        case 2:
            setEingabeEditable(edit2, wert);
            break;
        case 3:
            setEingabeEditable(edit3, wert);
            break;
        case 4:
            setEingabeEditable(edit4, wert);
            break;
        case 5:
            setEingabeEditable(edit5, wert);
            break;
    }
}

==> Schon wäre der ganze Code deutlich kürzer....

Was das Logging angeht: Du logst jetzt komplett über die Klasse Ereignis. Das sollte aber so nicht sein. Jede Klasse - also auch deine Controller und so - sollten einen eigenen Logger bekommen.

Also in Ereignis2Controller gehört dann ein:
private static final Logger LOG = LoggerFactory.getLogger(Ereignis2Controller.class);

Dann wird das Loglevel durch eine Konfiguration des Logging Frameworks konfiguriert. Du brauchst also die Variable mitLoggen nicht mehr.
(Sprich: Du hast die Datei simplelogger.properties, die Du anpassen kannst - sowohl das defaultLogLevel als auch die Level für einzelne Namespaces / Klassen.)

Somit wird dann aus:
Java:
        if (Ereignis.mitLoggen)
            {
            Ereignis.logger1.info("Logging <Ereignis2Controller.java><setEingabeEditable><Programmbegin> "+String.valueOf(wert));   
            }

nur noch ein einfaches:
LOG.info("<setEingabeEditable><Programmbegin> "+String.valueOf(wert));

Beziehungsweise: Es gibt teilweise ein BestPractice, dass man dennoch vorab prüft - das spart dann paar Aufrufe und so. In dem Beispiel würde z.B. der String zusammen gesetzt und so. Das wäre dann aber ein:
Java:
if (LOG.isInfoEnabled()) {
    LOG.info(.....);
}

Und wie Du sehen kannst: Einiges musst Du nicht loggen. Dass es ein Logging ist, ist doch bei dem Aufruf auf dem Logger klar. Und was erwartest Du in einem Logfile? Das Logging 🙂
Und das es im Ereignis2Controller ist, ist im Logger selbst mit drin. Bei der Erstellung des Loggers haben wir das ja bereits gesagt.

Das nur einmal als kleine Rückmeldung.
 
Um das Problem zu lösen: Kannst Du uns evtl. ein SQL Script geben, um eine passende Datenbank zu erzeugen mit ein paar Testdaten? Dann könnten wir das Programm ggf. starten und den Fehler aktiv suchen.
Oder mach eine Art Datenbank Mock, d.h. bau eine Datenbank Klasse, die halt nicht auf die Datenbank zugreift sondern einfach nur paar Dummy Daten liefert.

Wichtig wäre halt, dass wir:
a) das Projekt starten können
b) wir eine Beschreibung haben, wie wir das nachvollziehen können
 
Hallo Konrad,
Danke für deine Hilfe.
Aber du liegst falsch.
Ich habe das problem gefunden.
Da muss man erst mal drauf komme, ist ja Java.
Also ich initialisiere beim Start der Anwendung alle Label, Button und Textfield mit Farben.
Im Scenebuilder kann ma ja keine Hintergrundfarbe setzen (sollte man mal ändern).
Java:
        edit1.setStyle("-fx-font: 18px \"system\";"+
                        "-fx-font-weight: bold;"+
                        "-fx-text-fill: #0000ff;"+
                        "-fx-background-color: #808080;");
Das ist halt dein geliebtes CSS.
Nachteil, wenn man so die Felder initialiesert funktioniert der Befehl:
edit1.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
nicht mehr. Warum auch immer, ist halt Java.
Anstatt
edit1.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
die Farbänderung mit
Java:
        edit1.setStyle("-fx-font: 18px \"system\";"+
                        "-fx-font-weight: bold;"+
                        "-fx-text-fill: #0000ff;"+
                        "-fx-background-color: #ffffff;");
mahen, dann klappt es.

Trotzdem Danke und Gruß
Jürgen
 
LOG.info("<setEingabeEditable><Programmbegin> "+String.valueOf(wert));

Beziehungsweise: Es gibt teilweise ein BestPractice, dass man dennoch vorab prüft - das spart dann paar Aufrufe und so. In dem Beispiel würde z.B. der String zusammen gesetzt und so. Das wäre dann aber ein:
Java:
if (LOG.isInfoEnabled()) {
    LOG.info(.....);
}
Wozu? Alle aktuellen Logger unterstützen Platzhalter. Falls wert.toString() (abhängig von dessen Klasse) bzw. der Stringzusammenbau tatsächlich irgendwie performancekritisch sein sollten, würde dieser Mechanismus sicherstellen, dass nur wenn das Loglevel auch aktiv ist z.B. aus LOG.info("<setEingabeEditable><Programmbegin> {}", wert); auch die entsprechende Message zusammengebaut wird.
 
PMD Regel GuardLogStatement: https://pmd.github.io/pmd/pmd_rules_java_bestpractices.html

GuardLogStatement ✏️️


Since: PMD 5.1.0

Priority: Medium High (2)

Whenever using a log level, one should check if the loglevel is actually enabled, or otherwise skip the associate String creation and manipulation.

An alternative to checking the log level are substituting parameters, formatters or lazy logging with lambdas. The available alternatives depend on the actual logging framework.

This rule is defined by the following Java class: net.sourceforge.pmd.lang.java.rule.bestpractices.GuardLogStatementRule

Example(s):

// Add this for performance
if (log.isDebugEnabled()) {
log.debug("log something" + param1 + " and " + param2 + "concat strings");
}

// Avoid the guarding if statement with substituting parameters
log.debug("log something {} and {}", param1, param2);

// Avoid the guarding if statement with formatters
log.debug("log something %s and %s", param1, param2);

// Avoid the guarding if statement with lazy logging and lambdas
log.debug("log something expensive: {}", () -> calculateExpensiveLoggingText());

This rule has the following properties:

NameDefault ValueDescription
logLevelstrace , debug , info , warn , error , log , finest , finer , fine , info , warning , severeLogLevels to guard
guardsMethodsisTraceEnabled , isDebugEnabled , isInfoEnabled , isWarnEnabled , isErrorEnabled , isLoggableMethod use to guard the log statement
Use this rule with the default properties by just referencing it:

<rule ref="category/java/bestpractices.xml/GuardLogStatement" />

Use this rule and customize it:

<rule ref="category/java/bestpractices.xml/GuardLogStatement">
<properties>
<property name="logLevels" value="trace,debug,info,warn,error,log,finest,finer,fine,info,warning,severe" />
<property name="guardsMethods" value="isTraceEnabled,isDebugEnabled,isInfoEnabled,isWarnEnabled,isErrorEnabled,isLoggable" />
</properties>
</rule>

Aber wie Du siehst, ist das kein Gegensatz zu dem, was Du schreibst. Deine Alternative ist bereits eine, die explizit auch genannt wurde. Du hast den Fall, einer teuren Aufbereitung eines Parameters nicht bedacht - das ist dann der Punkt lazy logging bei PMD.

Daher widerspreche ich Dir in keiner Weise! Der TE hat nur eben String Concatenation verwendet im Aufruf des Loggers und da triggert dann halt diese PMD Regel. Und das triggert dann bei mir solche Anmerkungen.
 
Aber wie Du siehst, ist das kein Gegensatz zu dem, was Du schreibst. Deine Alternative ist bereits eine, die explizit auch genannt wurde. Du hast den Fall, einer teuren Aufbereitung eines Parameters nicht bedacht - das ist dann der Punkt lazy logging bei PMD.
Quatsch. Natürlich kenne ich das, und würde das auch vorschlagen - wenn hier auch nur ansatzweise ein solcher Spezialfall vorliegen würde (und auch nur genau dann). Tut er aber nicht. Oder siehst du in dem genannten Log-Statement (oder in den anderen von Jürgen) irgendwas teures was so einen Guard unbedingt notwendig machen würde, und was man nicht (deutlich einfacher) auch mittels der bereits genannten Platzhalter lösen könnte?
Daher widerspreche ich Dir in keiner Weise! Der TE hat nur eben String Concatenation verwendet im Aufruf des Loggers und da triggert dann halt diese PMD Regel. Und das triggert dann bei mir solche Anmerkungen.
Was wiederum bei mir die Anmerkung triggert, dass ein dreizeiliges Statement ziemlicher Overkill ist, wenn auch locker ein Einzeiler reicht. Vorallem wenn man diesen Spezialfall einem Anfänger als allgemeine BestPractice verkauft was im Extremfall nur dafür sorgt, dass Jürgen (falls er denn tatsächlich mal was von den Verbesserungsvorschlägen umsetzen sollte, und nicht bloß abwehrt) jetzt überall fröhlich diese Guard-Statements einbaut, ohne wirklich zu verstehen, dass er damit nur unnötig den Code weiter aufbläht, statt einfach nur die Logaufrufe selbst etwas zu verbessern.
 
Zuletzt bearbeitet von einem Moderator:
Jetzt habe ich ehrlich gesagt massive Probleme, Deine Reaktion zu verstehen.

Ich habe lediglich - ganz nebenbei - diesen Punkt angesprochen. Und das einfach nur auf Grund der Tatsache, dass
a) der TE eben genau dieses Zusammensetzen von Strings verwendet
b) er ganz offensichtlich irgend so einen Check für wichtig erachtet (sonst hätte er den bereits bei ersten Hinweisen zu seiner Logging Praxis verändert)
c) ich davon ausgehe, dass er Hinweise hin zu der Nutzung der Format Methoden (oder gar zu der fluent API von slf4j) auch nicht folgen wird
was dann zu
d) PMD wird den Code entsprechend der dann später genannten Regel, anmeckern

Das sollte auch Dir zumindest eine gewisse Relevanz aufzeigen, oder?

Ist es in diesem Fall aber nicht. Oder siehst du in dem genannten Log-Statement irgendwas teures was so einen Guard unbedingt notwendig machen würde, und was man nicht (deutlich einfacher) auch mittels der bereits genannten Platzhalter lösen könnte?
Und wo bitte siehst Du in meinen Aussagen genau dies? Wo habe ich auch nur in irgend einer Weise behauptet, dass genau dieses genannte Statement teuer wäre oder dass ein Guard unbedingt notwendig wäre? Das irritiert mich, da ich ja explizit vorher vorgeschlagen habe, dass man das if Bedingung ganz weglassen könnte. Man kann gerne diskutieren, was für Optionen es gibt oder nicht. Das ist selbstverständlich legitim und das habe ich bestätigt. (Wobei ich dazu nicht einfach gesagt habe: "Das sehe ich auch so" sondern sogar die PMD Regel genannt habe... Über die man auch per Google weitere Ausführungen und Diskussionen finden könnte ...)

Vorallem wenn man diesen Spezialfall einem Anfänger als BestPractice verkauft was im Extremfall nur dafür sorgt, dass Jürgen (falls er denn tatsächlich mal was von den wiederholt an ihn gegebenen Verbesserungsvorschlägen umsetzen sollte) jetzt überall diese Guard-Statements einbaut.
Ich sehe in dem Hinweis kein "Einem Anfänger ein BestPractice verkaufen" sondern nur eine Erwähnung nebenbei. Und er hat schlicht derzeit überall diese "Guard-Statements" in seinem Code. Denn er logt nichts ohne ein if (Ereignis.mitLoggen). Und damit wird dies einfach ein: Wenn er die unbedingt haben will, dann sollte er diese als übliches Guard-Statement ausführen.

Aber ich werde dies nicht weiter mit Dir diskutieren, den es geht schlicht um keine sachliche Thematik. Sachlich sind wir doch einer Meinung. Und Du kannst Dich gerne so viel Aufregen oder meine Wortwahl nicht gut finden oder sonst irgendwas. Das könnten wir auch gerne privat diskutieren. Aber diese Art und Weise hier im Forum werde ich nicht mit Dir weiter führen. Danke!
 

Zurück
Oben