JFormattedTextField не очищается должным образом

Я выполняю это задание, сделать программу, которая решает судоку. У меня есть панель с сеткой SudokuTextBox, расширяющей JFormattedTextField. У меня есть MaskFormatter, поэтому он принимает только одно целое число для каждого текстового поля. Затем на моей панели у меня есть этот код, когда ключ высвобождается.

 public void keyReleased(KeyEvent e) {
  SudokuTextBox tb = (SudokuTextBox) e.getSource();
  int row = tb.getRow();
  int col = tb.getCol();
  int value = toInteger(tb.getText());
  //System.out.println(value);
  if(sudoku.isValid(row, col, value)) {
   sudoku.set(row, col, value);
  }
  else {
   sudoku.set(row, col, 0);
   tb.setText(null);
  }
  tb.setCaretPosition(0);
  sudoku.print();
 }

Дело в том, что если я помещаю допустимое значение в текстовое поле, затем я возвращаюсь и ввожу недопустимое значение (по правилам судоку), текстовое поле очищается. Но затем, когда я ввожу вперед, предыдущее действительное значение отображается в текстовом поле. Моя судокуматрица, содержащая все введенные числа, очищает значение, как и должно быть, поэтому оно находится только в соответствующем текстовом поле.

Чтобы сделать ситуацию еще более запутанной, когда я меняю «SudokuTextBox extends JFormattedTextField» на «SudokuTextBox extends JTextField», это работает как шарм. Но я не могу установить размер JTextField так, чтобы он был квадратным, и я не могу применить только одно целое число для каждого текстового поля.

Я упускаю что-то действительно очевидное?


person evading    schedule 10.11.2010    source источник


Ответы (3)


Вот пример компонента с изменяемым размером, который может подойти для такой игры. Хотя он не содержит игровой логики, он достаточно хорошо обрабатывает ввод. Щелчок мышью или нажатие клавиши пробела вызывает всплывающее меню, а клавиши табуляции и цифровые клавиши работают должным образом. В частности, Digit.EMPTY является допустимым значением.

альтернативный текст

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.EnumSet;
import javax.swing.*;

/** @see http://stackoverflow.com/questions/4148336 */
public class CellTest extends JPanel {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            //@Override
            public void run() {
                createGUI();
            }
        });
    }

    public static void createGUI() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new GridLayout(3, 3));
        for (Digit d : Digit.digits) {
            frame.add(new CellTest(d));
        }
        frame.pack();
        frame.setVisible(true);
    }

    CellTest(Digit digit) {
        this.setLayout(new BorderLayout());
        this.setBorder(BorderFactory.createLineBorder(Color.black, 1));
        this.setBackground(new Color(0x00e0e0));

        JLabel candidates = new JLabel("123456789");
        candidates.setHorizontalAlignment(JLabel.CENTER);
        this.add(candidates, BorderLayout.NORTH);

        JDigit cellValue = new JDigit(digit);
        add(cellValue, BorderLayout.CENTER);
    }
}

class JDigit extends JButton {

    private static final int SIZE = 128;
    private static final int BASE = SIZE / 32;
    private static final Font FONT = new Font("Serif", Font.BOLD, SIZE);
    private JPopupMenu popup = new JPopupMenu();
    private Digit digit;
    private Image image;
    private int width, height;

    public JDigit(Digit digit) {
        this.digit = digit;
        this.image = getImage(digit);
        this.setPreferredSize(new Dimension(64, 64));
        this.setBackground(new Color(0xe0e000));
        this.setForeground(Color.black);
        this.setBorderPainted(false);
        this.setAction(new ButtonAction());
        this.addFocusListener(new FocusHandler());
        for (Digit d : Digit.values()) {
            Action select = new SelectAction(d);
            JMenuItem item = new JMenuItem(select);
            getInputMap().put(KeyStroke.getKeyStroke(
                KeyEvent.VK_0 + d.value(), 0), d.toString());
            getInputMap().put(KeyStroke.getKeyStroke(
                KeyEvent.VK_NUMPAD0 + d.value(), 0), d.toString());
            getActionMap().put(d.toString(), select);
            popup.add(item);
        }
    }

    public Digit getDigit() {
        return digit;
    }

    public void setDigit(Digit digit) {
        this.digit = digit;
        this.image = getImage(digit);
        this.repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        int w = this.getWidth();
        int h = this.getHeight();
        g.setColor(this.getBackground());
        int dx1 = w * width / height / 4;
        int dx2 = w - dx1;
        g.fillRect(dx1, 0, dx2 - dx1, h);
        g.drawImage(image,
            dx1, 0, dx2, h,
            0, 0, width, height, null);
    }

    private Image getImage(Digit digit) {
        BufferedImage bi = new BufferedImage(
            SIZE, SIZE, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = bi.createGraphics();
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(this.getForeground());
        g2d.setFont(FONT);
        FontMetrics fm = g2d.getFontMetrics();
        width = fm.stringWidth(digit.toString());
        height = fm.getAscent();
        g2d.drawString(digit.toString(), 0, height - BASE);
        g2d.dispose();
        return bi;
    }

    private class ButtonAction extends AbstractAction {
        //@Override
        public void actionPerformed(ActionEvent e) {
            popup.show(JDigit.this, getWidth() - width, getHeight() / 2);
        }
    }

    private class SelectAction extends AbstractAction {

        private Digit digit;

        public SelectAction(Digit digit) {
            this.digit = digit;
            this.putValue(Action.NAME, digit.toString());
        }

        //@Override
        public void actionPerformed(ActionEvent e) {
            setDigit(digit);
        }
    }

    private class FocusHandler implements FocusListener {

        private Color background = getBackground();

        //@Override
        public void focusGained(FocusEvent e) {
            setBackground(background.brighter());
        }

        //@Override
        public void focusLost(FocusEvent e) {
            setBackground(background);
        }
    }
}

enum Digit {

    EMPTY(0, " "), ONE(1, "1"), TWO(2, "2"), THREE(3, "3"), FOUR(4, "4"),
    FIVE(5, "5"), SIX(6, "6"), SEVEN(7, "7"), EIGHT(8, "8"), NINE(9, "9");
    public static EnumSet<Digit> digits = EnumSet.range(Digit.ONE, Digit.NINE);
    private int i;
    private String s;

    Digit(int i, String s) {
        this.i = i;
        this.s = s;
    }

    @Override
    public String toString() {
        return s;
    }

    public int value() {
        return i;
    }
}
person trashgod    schedule 11.11.2010
comment
Извините, что не принял ваш ответ. Однако меня больше интересовало, почему я не мог заставить JFormattedTextField делать то, что я хотел, чем то, как свернуть мой собственный компонент. Спасибо хоть! - person evading; 13.11.2010
comment
@refuser: нет проблем; Я изучал привязку клавиш. - person trashgod; 13.11.2010

Итак, теперь я нашел это: «Один из недостатков средства форматирования маски заключается в том, что в текущей реализации (Java 5) он не поддерживает возможность возврата пользователем поля к пустому значению (начальное значение поля до любого пользовательского ввода) после того, как они покинут поле в любой момент».

Так как я использую MaskFormatter, я не могу очистить поле.

person evading    schedule 10.11.2010

Хотя это и не тот же вопрос, я просматривал (слишком много) вопросов, подобных этому. В моем случае я хотел иметь возможность заполнить два других поля (jtfStarePatReq и jtfStarePatFound, которые являются JTextFields), либо просматривая индекс напрямую, либо используя некоторые счетчики и создавая строку, а затем просматривая эту строку (хорошо, может быть, это слишком расплывчато, но я думаю, что контекста достаточно). Я хотел, чтобы, если пользователь удалил или очистил значение в JFormattedTextField jftfStareOpsIndex, два других поля также были очищены. Я использовал метод .isEmpty() в JFormattedTextField, чтобы решить, следует ли мне использовать это поле ИЛИ использовать более длинный вычисляемый метод поиска. Поэтому мне НУЖНО, чтобы он был пустым, если кто-то переходит от поиска своего собственного индекса к тому, чтобы программное обеспечение искало индекс. В любом случае, я попытался поймать исключение commitEdit() и установить значение null, и, похоже, это помогло.

public class stareOpsIndexListener extends KeyAdapter implements FocusListener {

    public void focusGained(FocusEvent e) {
    }

    public void focusLost(FocusEvent e) {
        try {
            JFormattedTextField jftf = (JFormattedTextField) e.getComponent();
            jftf.commitEdit();
            updateStareOpsIndex(jftf);
        } catch (ParseException ex) {
                jtfStarePatReq.setText("");
                jtfStarePatFound.setText("");
                jftfStareOpsIndex.setValue(null);
        }
    }

    public void keyPressed(KeyEvent e) {

        int key = e.getKeyCode();

        if (key == KeyEvent.VK_ENTER) {
            try {
                JFormattedTextField jftf = (JFormattedTextField) e.getComponent();
                jftf.commitEdit();
                updateStareOpsIndex(jftf);
            } catch (ParseException ex) {
                jtfStarePatReq.setText("");
                jtfStarePatFound.setText("");
                jftfStareOpsIndex.setValue(null);
            }
        }
    }
}
person evernoob    schedule 24.07.2013