Swing Wie Fokus von JTextField entfernen?

Hag2bard

Bekanntes Mitglied
Hallo,

ich habe eine Gui, bei der man mit den Pfeiltasten (und auch mit der Maus) verschiedene Felder selektieren kann.

1648217887890.png

In einem Textfeld kann man einstellen wie viele Felder in x und y Richtung gleichzeitig ausgewählt werden können.
D.h. bei einer Eingabe von 2 werden 2x2 Blöcke ausgewählt.
Aber sobald ich einmal in ein TextFeld geklickt habe und mit den Pfeiltasten meine selektierten Blöcke bewegen möchte, wird zusätzlich auch der Cursor im Textfeld bewegt.
Dies möchte ich aber nicht, nachdem das TextFeld die actionPerformed Methode aufgerufen hat.
Wie kann ich den Focus auf eine andere Komponente zurück lenken?

Danke im Voraus
 
Dies möchte ich aber nicht, nachdem das TextFeld die actionPerformed Methode aufgerufen hat.
Wie kann ich den Focus auf eine andere Komponente zurück lenken?
Eine Variante wäre z.B. die Klasse JTextField erweitern.
1) Nur ganze Zahlen erlauben.
2) actionListener hinzufügen und einen Listener mit dem neuem Wert aufrufen.
3) der Listener kann dann den neuen Wert verarbeiten und z.B. für sich den Focus anfordern.
[CODE lang="java" title="NumberInput" highlight="5,9-13,17"]import javax.swing.JTextField;

@SuppressWarnings("serial")
public class NumberInput extends JTextField {
private IntInputListener listener; // listener is called after ActionEvent

public NumberInput(int value) {
super("" + value);
setDocument(new IntDocument(2)); // allow only integer up to 99.
addActionListener(e -> {
if (listener != null)
listener.valueEntered(Integer.parseInt(getText()));
});
}

public void setIntInputListener(IntInputListener listener) {
this.listener = listener;
}
}[/CODE]
[CODE lang="java" title="IntDocument" highlight="15-20"]import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

@SuppressWarnings("serial")
public class IntDocument extends PlainDocument {
public static final int NO_LIMIT = -1;
private int maxSize = NO_LIMIT;

public IntDocument(int maxSize) {
this.maxSize = maxSize;
}

@Override
public void insertString(final int offset, String text, final AttributeSet attributeSet)
throws BadLocationException {
text = text.replaceAll(".*[^0-9]", ""); // remove all non digits
if (isNewLengthOk(text))
super.insertString(offset, text, attributeSet);
}

protected boolean isNewLengthOk(final String text) {
return maxSize == NO_LIMIT || getLength() + text.length() <= maxSize);
}

/**
* Limits input to given length.
*
* @param maxSize
*/
public void setMaxSize(int maxSize) {
if (maxSize < 1)
return;
this.maxSize = maxSize;
}
}[/CODE]
[CODE lang="java" title="NoTextFocusPanel" highlight="25-28"]import java.awt.Color;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class NoTextFocusPanel extends JPanel {
private NumberInput input;
private JLabel label;

public NoTextFocusPanel(int width, int height) {
setPreferredSize(new Dimension(width, height));
input = new NumberInput(1);
label = new JLabel();
label.setBorder(BorderFactory.createLineBorder(Color.RED, 3));
label.setFocusable(false);
int objHeight = input.getFont().getSize() * 2;
int objWidth = (int) (width / 2 * .9);
input.setPreferredSize(new Dimension(objWidth, objHeight));
label.setPreferredSize(new Dimension(objWidth, objHeight));
label.setHorizontalAlignment(JLabel.CENTER);
add(input);
add(label);
input.setIntInputListener(value -> {
label.setText(value + "");
requestFocusInWindow();
});
}
}[/CODE]
[CODE lang="java" title="TestFocus" highlight="7"]import javax.swing.JFrame;

public class TestFocus {
public static void main(String[] args) {
JFrame frame = new JFrame("No text focus on enter");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new NoTextFocusPanel(500, 50));
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
}[/CODE]
[CODE lang="java" title="IntInputListener"]public interface IntInputListener {
public void valueEntered(int value);
}[/CODE]
 
Zuletzt bearbeitet:
Du könntest innerhalb der actionPerformed-Methode den Focus woanders setzen...wäre das eine Möglichkeit?

Ist zwar ein plumper Holzhammer der jeder Eleganz entbehrt, aber was solls...
 
Erstmal danke für die vielen Tipps, ich möchte jetzt erstmal auf die Frage von mihe antworten:

Die Pfeiltasten sollen nur die markierten Felder auf dem TilePanel bewegen. Ich habe dem Textfeld den Keylistener des TilePanels mitgegeben, da sämtliche Versuche den Fokus auf das TilePanel zurück zu lenken nachdem die ActionPerformed aufgerufen wurde fehlgeschlagen sind und ich nach einer Eingabe leider nicht mehr mit den Pfeiltasten auf dem TilePanel navigieren konnte. Wenn es dafür eine bessere Lösung gibt sollte man da vielleicht erstmal ansetzen?
 
Deine Komponente muss fokussierbar sein, dann sollte das ganz normal funktionieren:
Java:
import java.awt.Component;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;

public class Test {


    public void run() {
        Random rand = new Random();
        
        FocusListener focused = new FocusAdapter() {
            public void focusGained(FocusEvent e) {
                if (!e.isTemporary()) {
                    JTextField textField = (JTextField) e.getSource();
                    textField.selectAll();
                }
            }
        };
                
        ActionListener listener = e -> {
            JComponent component = (JComponent) e.getSource();
            Component[] candidates = component.getParent().getComponents();
            candidates[rand.nextInt(candidates.length)].requestFocusInWindow();
        };
                
        
        JPanel panel = new JPanel();
        JTextField[] fields = new JTextField[10];        
        for (int i = 0; i < fields.length; i++) {
            fields[i] = new JTextField(10);
            fields[i].setText(Integer.toString(rand.nextInt()));
            fields[i].addActionListener(listener);
            fields[i].addFocusListener(focused);
            panel.add(fields[i]);
        }
            
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(panel);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().run());
    }
}
 
Die Pfeiltasten sollen nur die markierten Felder auf dem TilePanel bewegen.
Meinst Du in etwa so ?
1648334319418.png
Auf das Textfeld klicken und die Größe eingeben. Nach dem Enter wird das Quadrat auf die gewünschte Größe gesetzt und kann mit den Pfeiltasten bewegt werden.
Zum Testen kannst Du die jar herunterladen.


[CODE lang="java" title="StartMoveArea" highlight="10"]import javax.swing.JFrame;

public class StartMoveArea {
public static void main(String[] args) {
MoveSquarePanel panel = new MoveSquarePanel(600, 600);
JFrame frame = new JFrame("Arrows move Square, entered value resize square");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setResizable(false);
frame.getContentPane().addKeyListener(panel.getKeyBoard());
frame.pack();
frame.setVisible(true);
frame.getContentPane().requestFocus();
}
}[/CODE]
[CODE lang="java" title="MoveSquarePanel" highlight="19-21,27-43"]import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class MoveSquarePanel extends JPanel {
private NumberInput input;
private Square square = new Square();

public MoveSquarePanel(int width, int height) {
setPreferredSize(new Dimension(width, height));
input = new NumberInput(1);
input.setPreferredSize(new Dimension(width / 12, input.getFont().getSize() * 2));
add(input);
input.setIntInputListener(value -> {
square.setDim(value);
getParent().requestFocusInWindow();
repaint();
});
square.setPos(100, 100);
setBackground(Color.DARK_GRAY);
}

public KeyAdapter getKeyBoard() {
return new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_UP)
square.moveUp();
if (key == KeyEvent.VK_LEFT)
square.moveLeft();
if (key == KeyEvent.VK_RIGHT)
square.moveRight();
if (key == KeyEvent.VK_DOWN)
square.moveDown();
repaint();
}
};
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
square.draw(g);
}

}[/CODE]
[CODE lang="java" title="Square"]import java.awt.Color;
import java.awt.Graphics;

public class Square {
private int x;
private int y;
private int scale = 20;
private int dim = 1;
private Color color = Color.RED;

public void draw(Graphics g) {
g.setColor(color);
g.fillRect(x, y, getSize(), getSize());
}

public int getSize() {
return scale * dim;
}

public void moveDown() {
y += scale;
}

public void moveLeft() {
x -= scale;
}

public void moveRight() {
x += scale;
}

public void moveUp() {
y -= scale;
}

public void setDim(int dim) {
this.dim = dim;
}

public void setScale(int scale) {
this.scale = scale;
}

public void setPos(int x, int y) {
this.x = x;
this.y = y;
}

}[/CODE]
[CODE lang="java" title="NumberInput" highlight="13,7"]import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.JTextField;

@SuppressWarnings("serial")
public class NumberInput extends JTextField {
private IntInputListener listener; // listener is called after ActionEvent

public NumberInput(int value) {
setDocument(new IntDocument(2)); // allow only integer up to 99.
addActionListener(e -> {
if (listener != null)
listener.valueEntered(Integer.parseInt(getText()));
});
setText(value + "");
addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
selectAll();
}
});
}

public void setIntInputListener(IntInputListener listener) {
this.listener = listener;
}
}[/CODE]
[CODE lang="java" title="IntDocument"]import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

@SuppressWarnings("serial")
public class IntDocument extends PlainDocument {
public static final int NO_LIMIT = 0;
private int maxSize = NO_LIMIT;

public IntDocument(int maxSize) {
this.maxSize = maxSize;
}

@Override
public void insertString(final int offset, String text, final AttributeSet attributeSet)
throws BadLocationException {
text = text.replaceAll(".*[^0-9]", ""); // remove all non digits
if (isNewLengthOk(text))
super.insertString(offset, text, attributeSet);
}

protected boolean isNewLengthOk(final String text) {
return maxSize == NO_LIMIT || getLength() + text.length() <= maxSize;
}

/**
* Limits input to given length.
*
* @param maxSize
*/
public void setMaxSize(int maxSize) {
if (maxSize < 1)
return;
this.maxSize = maxSize;
}
}[/CODE]
[CODE lang="java" title="IntInputListener"]public interface IntInputListener {
public void valueEntered(int value);
}
[/CODE]
 

Anhänge

Bevor ich mir diese wirklich umfangreichen Ideen zu Gemüte führe hätte ich eine Frage zu einer einfacheren Methode.
Die Methode setFocusable(false) bezweckt, dass man eine Komponente nicht mehr fokusieren kann.
Wenn ich nacheinander txtField.setFocusable(false); und txtField.setFocusable(true); aufrufe, verliert das Textfeld den Fokus, was schonmal gut ist. Das Problem ist nun aber, dass eine andere Komponente den Fokus bekommt(eine Checkbox) und dieser könnte ich jetzt einen KeyListener hinzufügen, aber das halte ich auch für eine schlechte Idee.

Mit welcher Methode kann ich denn nicht die Fokusierbarkeit einstellen, sondern den Fokus auf eine Komponente lenken ohne 2 oder 3 Klassen dazu zu schreiben?

edit: googeln hilft manchmal doch: requestFocus() wäre die Antwort gewesen. Ich hab schonmal intensiv gegoogelt aber nichts dazu gefunden.
 
Mit welcher Methode kann ich denn nicht die Fokusierbarkeit einstellen, sondern den Fokus auf eine Komponente lenken ohne 2 oder 3 Klassen dazu zu schreiben?
Du musst dem Textfeld nur einen ActionListener geben der die Komponente kennt, die dann mittels .requestFocusInWindow(); den Focus anfordert.
So ähnlich wie es @mihe7 gemacht hat.
Du brauchst aber keine Klassen schreiben. Kopiere einfach folgende Klassen und verwende sie einfach.

Vorteil: Die Eingabe beim Textfeld lässt ausschließlich Zahlen zu was du ja bei deiner Eingabe vermutlich bezweckst.
In deinem Programm benützt du die Klassen dann folgendermaßen.
Java:
        input = new NumberInput(1);        
        ..
        add(input);
        input.setIntInputListener(value -> {
            // verarbeite den Wert value hier
            focusComponent.requestFocusInWindow(); // focusComponent soll den Focus erhalten            
        });


Diese Klassen per Copy und Paste in dein Projekt integrieren.
[CODE lang="java" title="NumberInput"]import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.JTextField;

@SuppressWarnings("serial")
public class NumberInput extends JTextField {
private IntInputListener listener; // listener is called after ActionEvent

public NumberInput(int value) {
setDocument(new IntDocument(2)); // allow only integer up to 99.
addActionListener(e -> {
if (listener != null)
listener.valueEntered(Integer.parseInt(getText()));
});
setText(value + "");
addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
selectAll();
}
});
}

public void setIntInputListener(IntInputListener listener) {
this.listener = listener;
}
}[/CODE]
[CODE lang="java" title="IntDocument"]import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

@SuppressWarnings("serial")
public class IntDocument extends PlainDocument {
public static final int NO_LIMIT = 0;
private int maxSize = NO_LIMIT;

public IntDocument(int maxSize) {
this.maxSize = maxSize;
}

@Override
public void insertString(final int offset, String text, final AttributeSet attributeSet)
throws BadLocationException {
text = text.replaceAll(".*[^0-9]", ""); // remove all non digits
if (isNewLengthOk(text))
super.insertString(offset, text, attributeSet);
}

protected boolean isNewLengthOk(final String text) {
return maxSize == NO_LIMIT || getLength() + text.length() <= maxSize;
}

/**
* Limits input to given length.
*
* @param maxSize
*/
public void setMaxSize(int maxSize) {
if (maxSize < NO_LIMIT)
return;
this.maxSize = maxSize;
}
}[/CODE]
[CODE lang="java" title="IntInputListener"]public interface IntInputListener {
public void valueEntered(int value);
}[/CODE]
 

Zurück
Oben