遍历启用和禁用的组件

时间:2015-10-07 14:05:53

标签: java swing

焦点遍历似乎仅适用于已启用的Swing组件(使用 TAB CTRL + TAB 进行制表)。如何将启用和禁用的组件都视为重要组件并启用键盘遍历它们?

为什么我要这个?我有一个表单,其中每个textfield,textarea或checkbox除了其值之外还可以具有set和unset状态。目前用户需要使用鼠标点击设置和取消设置组件(左键单击未设置的组件进行设置,CTRL +左键单击设置组件以取消设置),但我想提供键盘访问以执行相同操作事情也是。我选择这种机制的原因是空值可能有意义 - 空文本组件字符串很重要,不能用于表示" unsetness"。我也不想在每个可设置值之前加一个复选框,因为那样看起来很尴尬。

这是一个示例表单,用于阐明设置/未设置状态(复选框和textarea以未设置开始,而字段设置为):

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;

public class TraverseDisabled extends JFrame {

    public static final String VALUE_NOT_SET_MESSAGE = "Click to set this value.";
    public static final String VALUE_SET_DEFAULT_MESSAGE = "Edit value or use CTRL + click to unset this value.";
    public static final String VALUE_NOT_SET_DEFAULT_VALUE = "<not-set>";

    private JPanel panel;
    private JLabel label1;
    private JTextField textfield1;
    private JCheckBox checkbox1;
    private JLabel label2;
    private JTextArea textarea1;
    private JButton button;

    public TraverseDisabled() {
        setTitle("Form");
        initComponents();

        pack();
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private void initComponents() {
        panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        setLayout(new BorderLayout());
        add(panel);

        GridBagConstraints gbc;

        label1 = new JLabel("Field1:");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.anchor = GridBagConstraints.WEST;
        panel.add(label1, gbc);

        textfield1 = new JTextField(20);
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 0;
        panel.add(textfield1, gbc);
        textfield1.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                onJComponentClicked(e, textfield1, null);
            }

        });

        checkbox1 = new JCheckBox("Checkbox1");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.anchor = GridBagConstraints.WEST;
        panel.add(checkbox1, gbc);
        checkbox1.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                onJComponentClicked(e, checkbox1, false);
            }

        });
        enableEditingForJCheckBoxComponent(checkbox1, false, false);

        label2 = new JLabel("Area1:");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        panel.add(label2, gbc);

        textarea1 = new JTextArea(5, 20);
        JScrollPane scrollPane1 = new JScrollPane(textarea1);
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 2;
        panel.add(scrollPane1, gbc);
        textarea1.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                onJComponentClicked(e, textarea1, null);
            }

        });
        enableEditingForJTextComponent(textarea1, false, VALUE_NOT_SET_DEFAULT_VALUE);

        button = new JButton("Apply");
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 3;
        gbc.anchor = GridBagConstraints.EAST;
        panel.add(button, gbc);
    }

    private Object onJComponentClicked(java.awt.event.MouseEvent evt, JComponent component, Object previousValue) {
        if (!component.isEnabled()) {
            if (component instanceof JCheckBox) {
                enableEditingForJCheckBoxComponent((JCheckBox)component, true, (Boolean)previousValue);
            } else if (component instanceof JTextComponent) {
                enableEditingForJTextComponent((JTextComponent)component, true, (String)previousValue);
            }
            evt.consume();
        } else if (evt.isControlDown()) {
            if (component instanceof JCheckBox) {
                JCheckBox cb = (JCheckBox)component;
                previousValue = !cb.isSelected();
                enableEditingForJCheckBoxComponent((JCheckBox) component, false, (Boolean)previousValue);
            } else if (component instanceof JTextComponent) {
                previousValue = ((JTextComponent)component).getText();
                enableEditingForJTextComponent((JTextComponent)component, false, VALUE_NOT_SET_DEFAULT_VALUE);
            }
            evt.consume();
        } 
        return previousValue;
    }

    private void enableEditingForJTextComponent(JTextComponent textComponent, boolean enable, String text) {
        if (!enable) {
            textComponent.setEnabled(false);
            textComponent.setText(text);
            textComponent.setToolTipText(VALUE_NOT_SET_MESSAGE);
        } else {
            textComponent.setEnabled(true);
            textComponent.setText(text);
            textComponent.setToolTipText(VALUE_SET_DEFAULT_MESSAGE);
            textComponent.requestFocusInWindow();
        }
    }

    private void enableEditingForJCheckBoxComponent(JCheckBox checkBox, boolean enable, boolean value) {
        if (!enable) {
            checkBox.setEnabled(false);
            checkBox.setSelected(value);
            checkBox.setToolTipText(VALUE_NOT_SET_MESSAGE);
        } else {
            checkBox.setEnabled(true);
            checkBox.setSelected(value);
            checkBox.setToolTipText(VALUE_SET_DEFAULT_MESSAGE);
        }
    }

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

            public void run() {
                new TraverseDisabled().setVisible(true);
            }
        });
    }

}

2 个答案:

答案 0 :(得分:4)

我相信Swing使用的默认FocusTraverslPolicy最终将最终调用Component类中具有最终方法的方法:

final boolean canBeFocusOwner() {
    // It is enabled, visible, focusable.
    if (isEnabled() && isDisplayable() && isVisible() && isFocusable()) {
        return true;
    }
    return false;
}

所以它肯定会检查以确保组件已启用。

因此,我建议的唯一选择是尝试创建自己的FocusTraversalPolicy

Customizing Focus Traversal上的Swing教程中的部分提供了一个示例,您可以使用/修改它来满足您的要求。

答案 1 :(得分:1)

我最终考虑了Hovercraft Full Of Eels的建议并使用了替代品。由于表单确实(或可以)由标签组件对组成,因此如果配对组件被禁用,我决定滥用标签,使其可聚焦。然后标签处理键盘事件。

{{1}}

我不认为有可能实现我原来想要的东西,但这非常接近和简单。