焦点遍历似乎仅适用于已启用的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);
}
});
}
}
答案 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}}
我不认为有可能实现我原来想要的东西,但这非常接近和简单。