Как действовать при нажатии «Enter» при нажатии кнопки «Отмена» в JFileChooser? - PullRequest
1 голос
/ 31 июля 2010

У меня есть JFileChooser в JFrame.Я добавил ActionListener к JFileChooser, чтобы при нажатии работала кнопка «Отмена».Я также могу перейти на кнопку «Отмена», но когда я нажимаю клавишу «Ввод», ничего не происходит (т. Е. ActionListener не вызывается с командой события JFileChooser.CANCEL_SELECTION).Что я должен делать с JFileChooser, чтобы нажатие клавиши «Ввод» при нажатии кнопки «Отмена» эквивалентно нажатию кнопки «Отмена»?

Вот простой пример (неправильного)поведение, которое я вижу:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFileChooser;
import javax.swing.JFrame;

public final class TestApp {
    public static void main(final String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    final JFileChooser chooser = new JFileChooser();
                    chooser.addActionListener(new ActionListener() {
                        public void actionPerformed(final ActionEvent e) {
                            System.exit(0);
                        }
                    });
                    final JFrame frame = new JFrame();
                    frame.add(chooser);
                    frame.pack();
                    frame.setVisible(true);
                }
                catch (final Throwable t) {
                    t.printStackTrace();
                }
            }
        });
    }
}

Чтобы увидеть (не) поведение, запустите программу, перейдите на вкладку «Отмена», а затем нажмите клавишу «Ввод».Программа не завершает свою работу на моей платформе - хотя это происходит, когда я нажимаю кнопку «Отмена».

Расширение JFileChooser и переопределение cancelSelection() также не работают (очевидно, эта функция нене вызывается при нажатии клавиши «Ввод» при нажатии кнопки «Отмена».

Поведение (неправильное) происходит в моей системе Fedora 10 x86_64 с Java 5, 6 и 7.

ADDENDUM: следующее добавляет KeyEventPostProcessor к текущему KeyboardFocusManager и, кажется, делает то, что я хочу:

import java.awt.Component;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;

public final class TestApp {
    public static void main(final String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    final JFileChooser chooser = new JFileChooser();
                    chooser.addActionListener(new ActionListener() {
                        public void actionPerformed(final ActionEvent e) {
                            System.out.println(e.paramString());
                            System.exit(0);
                        }
                    });
                    final KeyboardFocusManager kfm = KeyboardFocusManager
                            .getCurrentKeyboardFocusManager();
                    kfm.addKeyEventPostProcessor(new KeyEventPostProcessor() {
                        @Override
                        public boolean postProcessKeyEvent(final KeyEvent e) {
                            if (e.getID() == KeyEvent.KEY_RELEASED
                                    && e.getKeyCode() == KeyEvent.VK_ENTER) {
                                final Component comp = e.getComponent();
                                if (chooser.isAncestorOf(comp)) {
                                    if (!(comp instanceof JButton)) {
                                        chooser.approveSelection();
                                    }
                                    else {
                                        final JButton button = (JButton) comp;
                                        if ("Cancel".equals(button.getText())) {
                                            chooser.cancelSelection();
                                        }
                                        else {
                                            chooser.approveSelection();
                                        }
                                    }
                                }
                            }
                            return false;
                        }
                    });
                    final JFrame frame = new JFrame();
                    frame.add(chooser);
                    frame.pack();
                    frame.setVisible(true);
                }
                catch (final Throwable t) {
                    t.printStackTrace();
                }
            }
        });
    }
}

Однако кажется, что нужно проделать большую работу, просто чтобы иметь возможностьразличайте нажатие клавиши ввода на кнопке «Отмена» и в любом другом месте.

Видите ли вы какие-либо проблемы с ним?

ОТКРЫТОЕ РЕШЕНИЕ: Настройка GUI Look and Feel для родного длямоя система (Linux) делает то, что я хочу, без необходимости чего-либо еще.Это то, что я не знал и что искал.Решение заключается в следующем

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

в качестве первого исполняемого оператора метода main().Затем можно обойтись без всех слушателей фокусировки, ключевых обработчиков событий и т. Д.

Я присудил 100 баллов самому полезному респонденту.

1 Ответ

3 голосов
/ 02 августа 2010

Программа не заканчивается на моей платформе.

Я вижу нормальную работу в Mac OS X 10.5, Ubuntu 10 и Windows 7 с использованием (по-разному) Java 5 и 6. Я заменил ваш exit() на println(), чтобы увидеть событие:

System.out.println(rootDirChooser.getSelectedFile().getName() + e.paramString());

Может помочь указать вашу платформу и версию; если возможно, проверьте правильность установки.

Я не уверен, что понимаю вашу цель; но, в качестве альтернативы, рассмотрим переопределение approveSelection():

private static class MyChooser extends JFileChooser {

    @Override
    public void approveSelection() {
        super.approveSelection();
        System.out.println(this.getSelectedFile().getName());
    }
}

Добавление:

Цель состоит в том, чтобы действие нажатия клавиши «Ввод» при нажатии кнопки «Отмена» было идентичным нажатию кнопки «Отмена».

Как обсуждено в Связывания клавиш , вы можете изменить действие, связанное с VK_ENTER.

KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
InputMap map = chooser.getInputMap(JFileChooser.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
map.put(enter, "cancelSelection");

Если вы хотите, чтобы изменение происходило только тогда, когда кнопка «Отмена» имеет фокус, вам нужно сделать это в Слушателе фокуса .

Добавление:

Я нашел решение, которое использует KeyboadFocusManager. Что ты думаешь?

Я вижу плюсы и минусы в каждом случае, поэтому обрисовал в общих чертах оба ниже. Использование KeyboadFocusManager находит все кнопки, но не предлагает независимого от локали способа различать их; Focus Listener подход может видеть только кнопку подтверждения, и это зависит от пользовательского интерфейса. Тем не менее, вы можете объединить подходы для достижения лучших результатов. Второе мнение не будет не в порядке.

Добавление:

Я обновил приведенный ниже код, чтобы избавить от необходимости знать локализованное имя кнопки «Отмена» и использовать привязки клавиш.

import java.awt.EventQueue;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
import javax.swing.plaf.metal.MetalFileChooserUI;

public final class FileChooserKeys
    implements ActionListener, FocusListener, PropertyChangeListener {

    private final JFileChooser chooser = new JFileChooser();
    private final MyChooserUI myUI = new MyChooserUI(chooser);
    private final KeyStroke enterKey =
        KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);

    private void create() {
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        chooser.addActionListener(this);
        myUI.installUI(chooser);
        myUI.getApproveButton(chooser).addFocusListener(this);
        KeyboardFocusManager focusManager =
            KeyboardFocusManager.getCurrentKeyboardFocusManager();
        focusManager.addPropertyChangeListener(this);

        frame.add(chooser);
        frame.pack();
        frame.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.paramString());
    }

    @Override
    public void focusGained(FocusEvent e) {
        System.out.println("ApproveButton gained focus.");
    }

    @Override
    public void focusLost(FocusEvent e) {
        System.out.println("ApproveButton lost focus.");
    }

    @Override
    public void propertyChange(PropertyChangeEvent e) {
        Object o = e.getNewValue();
        InputMap map = chooser.getInputMap(
            JFileChooser.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        if (o instanceof JButton) {
            if ("focusOwner".equals(e.getPropertyName())) {
                JButton b = (JButton) o;
                String s = b.getText();
                boolean inApproved = b == myUI.getApproveButton(chooser);
                if (!(s == null || "".equals(s) || inApproved)) {
                    map.put(enterKey, "cancelSelection");
                } else {
                    map.put(enterKey, "approveSelection");
                }
            }
        }
    }

    private static class MyChooserUI extends MetalFileChooserUI {

        public MyChooserUI(JFileChooser b) {
            super(b);
        }

        @Override
        protected JButton getApproveButton(JFileChooser fc) {
            return super.getApproveButton(fc);
        }
    }

    public static void main(final String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new FileChooserKeys().create();
            }
        });
    }
}
...