Wie kann ich meine Keylistener Klasse unterscheiden lassen, von welcher "Quelle" der Input kommt?

phili3

Mitglied
Hallo,
ich habe eine KeyListener Klasse erstellt, die die KeyEvents des Programs bearbeitet:
Java:
public class KeyBoardListener implements KeyListener {


    private Panel panel;

    public KeyBoardListener(Panel panel){
        this.panel=panel;
    }


    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {

        if(!panel.isTimerOn()){
            panel.startTimer();
        }

        panel.wordsHandler.createWordToCompare(e);  //creates the word which will be compared to the current word and will decide if color will be red or green
        panel.wordsHandler.switchWords(e);

    }


    @Override
    public void keyReleased(KeyEvent e) {


    }


}


Nun habe ich zwei verschiedene JTextFields.
Wenn ich anfange, in dem ersten zu schreiben, soll ein Timer gestartet werden.
Wenn ich in dem zweiten Textfield etwas tippe, dann soll dieser Timer nicht gestartet werden.

Code:
   private void addKeyListener(){
        KeyBoardListener keyBoardListener = new KeyBoardListener(this);
        mainTextField.requestFocus();
        mainTextField.addKeyListener(keyBoardListener);
        addWordsField.addKeyListener(keyBoardListener);
    }

Meine Idee wäre, das ich dem Constructor des KeyboardListeners einen String hinzufüge und anhand dem wird dann entschieden, welche Funktion ausgeführt wird, wenn eine Taste gedrückt wird...
Also mit einer switch Abfrage zb.

Ich kann mir aber vorstellen, dass das eleganter geht.
Danke schonmal
 
Also erst einmal haben die Events alle ein Source. Über den KeyEvent Parameter kannst Du also die Quelle heraus bekommen.

Generell ist aber immer die Frage, in wie weit sowas Sinn macht oder nicht. Wenn Du eine eigene Klasse hast für den KeyListener, dann sollte diese eigentlich möglichst unabhängig sein. Das wird hier aber vermutlich recht schwer, da Du eine sehr hohe Kopplung hast. Daher macht es hier Sinn, sowas als innere Klasse zu bauen. (Bei anderen Events hat man ein funktionales Interface, aber der KeyListener hat leider direkt 3 abstrakte Methoden was das leider unmöglich macht.)

Aber dennoch kann man hier einiges verbessern. Ich will Dir dazu aber einfach nur ein paar Anregungen geben:

a) KeyAdapter statt KeyListener: Wenn Du statt dem Interface KeyListener die Klasse KeyAdapter nutzt, dann musst Du nur noch keyPressed überschreiben. Damit wird der Code gleich etwas kürzer.

b) Methoden sollten nur das machen, was der Name auch sagt. Deine Methode addKeyListener() fügt nicht nur KeyListener hinzu sondern setzt auch einen Fokus. Sowas sollte man immer vermeiden.

c) Deine Idee mit einem Parameter ist schon gut. Nur eben solltest Du da keinen String nehmen sondern ein Boolean. Also ein Parameter wie startTimerOnKeypress oder so. Dann müsstest Du nicht mehr prüfen, von wo etwas kommt, sondern Du hast dann etwas wie:
Java:
   private void addKeyListener(){
        mainTextField.addKeyListener(new KeyBoardListener(this, true));
        addWordsField.addKeyListener(new KeyBoardListener(this, false));
    }

d) Deine Klasse KeyBoardListener hat einen blöden Namen. Der besagt nicht wirklich etwas aus zu der Funktionalität.

e) Die Klasse muss zu viele Internas der übergebenen Klasse kennen. Und die Klasse Panel ist deine Klasse Panel. Der Name ist auch wieder viel zu allgemein. Hier wäre es deutlich besser, wenn man da Funktionalität in deiner Klasse Panel lassen würde (Die ein besseren Namen hätte. Wenigstens GamePanel oder so, damit man merkt: Das ist programmspezifisch!).

Eine Idee könnte sein, dass man da etwas hätte wie:
Java:
public class KeyBoardListener extends KeyHandler {


    private Consumer<KeyEvent> eventConsumer;

    public KeyBoardListener(Consumer<KeyEvent> eventConsumer){
        this.eventConsumer=eventConsumer;
    }


    @Override
    public void keyPressed(KeyEvent e) {
        if (eventConsumer != null) {
            eventConsumer.accept(e);
        }
    }
}

Dann kannst Du das über Methodenreferenzen oder Lambda Ausdrücke nutzen, weil Du da in der Klasse als Parameter ein funktionales Interface hast:
Java:
   private void addKeyListener(){
        mainTextField.addKeyListener(new KeyBoardListener(this::someMethod)); // Methodenreferenz
        addWordsField.addKeyListener(new KeyBoardListener( e -> someMethod(e) )); // Lambda Expression
    }

Statt someMethod hättest Du dann zwei Methoden, die halt etwas machen und die Du da schreiben könntest. handleKeyEventMainTextField von mir aus oder etwas besseres an Funktionalität. Damit hast Du dann die Methode auch direkt in der Klasse, in der auch Dinge verändert werden.

Das nur einmal ganz auf die Schnelle etwas Feedback.
 
Bei anderen Events hat man ein funktionales Interface, aber der KeyListener hat leider direkt 3 abstrakte Methoden was das leider unmöglich macht.
Wobei man das natürlich auftrennen könnte.
Java:
interface KeyPressedHandler extends KeyListener  {
    @Override  default void keyTyped(KeyEvent e) {}
    @Override  default void keyReleased(KeyEvent e) {}
}
Nicht, dass das einen sittlichen Mehrwert hätte:
Java:
mainTextField.addKeyListener((KeyPressedHandler) e -> doSomething());

Aber Deine Idee gefällt mir, die lässt sich schön ausbauen, zum Beispiel:
Java:
class KeyHandler implements KeyListener {
    private Consumer<KeyEvent> keyPressedConsumer;
    private Consumer<KeyEvent> keyReleasedConsumer;
    private Consumer<KeyEvent> keyTypedConsumer;

    @Override
    public void keyPressed(KeyEvent e) {
        if (keyPressedConsumer != null) {
            keyPressedConsumer.accept(e);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        if (keyReleasedConsumer != null) {
            keyReleasedConsumer.accept(e);
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
        if (keyTypedConsumer != null) {
            keyTypedConsumer.accept(e);
        }
    }

    public void installOn(Component c) {
        c.addKeyListener(this);
    }

    public static Builder builder() {
        return new Builder();
    }

    static class Builder {
        private boolean handleKeyPressed;
        private boolean handleKeyReleased;
        private boolean handleKeyTyped;

        private Builder() {}

        public Builder handlePressed() {
            handleKeyPressed = true;
            return this;
        }

        public Builder handleReleased() {
            handleKeyReleased = true;
            return this;
        }

        public Builder handleTyped() {
            handleKeyTyped = true;
            return this;
        }

        public KeyHandler by(Consumer<KeyEvent> consumer) {
            if ( !(handleKeyPressed | handleKeyReleased | handleKeyTyped) ) {
                throw new IllegalStateException("Won't build a KeyHandler that handles nothing.");
            }

            KeyHandler handler = new KeyHandler();
            if (handleKeyPressed) handler.keyPressedConsumer = consumer;
            if (handleKeyReleased) handler.keyReleasedConsumer = consumer;
            if (handleKeyTyped) handler.keyTypedConsumer = consumer;
            return handler;
        }
    }
}

Damit kann man dann schöne Texte schreiben:
Java:
KeyHandler.builder().handlePressed().handleReleased().by(e -> System.out.println(e)).installOn(textField);
oder
Java:
KeyHandler handler = KeyHandler.builder().handlePressed().handleReleased().by(e -> System.out.println(e));
myTextFields.forEach(handler::installOn);
 

Neue Themen


Zurück
Oben