简体   繁体   English

防止出现 JComboBox 弹出窗口。 传递 InputVerifier

[英]Prevent a JComboBox popup from appearing. Passing InputVerifier

Given the following scenario:鉴于以下情况:

  • one has a JComboBox and a JTextField一个有一个 JComboBox 和一个 JTextField
  • the latter has an InputVerifier providing its own error message.后者有一个 InputVerifier 提供自己的错误消息。
  • the textField is the current focus owner and its input does not satisfy the inputVerifier (Here: Less than 3 characters). textField 是当前焦点所有者,其输入不满足 inputVerifier(此处:少于 3 个字符)。
  • Now you click on the combo, which will fire the InputVerifier.现在单击组合,它将触发 InputVerifier。
  • The InputVerifier in turn will show its error message. InputVerifier 将依次显示其错误消息。
  • You acknowledge the error message and, lo and behold, the combo's popup appears, ready to accept a selection.您确认了错误消息,你瞧,组合的弹出窗口出现,准备好接受选择。

To me this is an undesired behaviour, and I would prefer to have no other component change its input until the inputVerifier is satisfied.对我来说,这是一种不受欢迎的行为,我希望在满足 inputVerifier 之前没有其他组件更改其输入。 Paticularly in the described scenario the JComboBox could have its own ActionListener changing the whole situation in case of a new selection.特别是在所描述的场景中,JComboBox 可以拥有自己的 ActionListener 以在新选择的情况下改变整个情况。

The following code solves this problem by applying a PopupMenuListener, which checks a boolean in the verifier telling its state, and then decides whether to show the popup or not.下面的代码通过应用一个 PopupMenuListener 解决了这个问题,它在验证器中检查一个布尔值来告知其状态,然后决定是否显示弹出窗口。

What I would like to do now is to write an extended JComboBox class implementing this functionality and accepting all kinds of (extended) InputVerifiers.我现在想做的是编写一个扩展的 JComboBox 类来实现这个功能并接受各种(扩展的)InputVerifiers。 But I am already stuck in my test code here, for I don't see a way how to give the PopupMenuListener access to the verifers' boolean 'satisfied' in a general way.但是我已经在这里的测试代码中陷入困境,因为我看不到如何让 PopupMenuListener 以一般方式访问验证者的布尔值“满意”。 Passing just an InputVerifier as parameter would need casting later, which unmakes the generality.仅传递 InputVerifier 作为参数将需要稍后进行强制转换,这会破坏一般性。 - Or is there any "variable" casting? - 或者是否有任何“变量”铸造?

Any idea is welcome, even if it is a completely different approach to the initial problem.欢迎任何想法,即使它是针对初始问题的完全不同的方法。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;

public class DisableComboPopupByVerifier extends JFrame {
  public static final long serialVersionUID = 100L;

  JComboBox<String> cmb;

  public DisableComboPopupByVerifier() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(450, 240);
    setLocationRelativeTo(null);
    setLayout(new FlowLayout(FlowLayout.LEFT, 2, 2));

    cmb= new JComboBox<>(new String[]{"AA", "BB", "CC"});
    cmb.setPreferredSize(new Dimension(43, 20));
    MyMinLenVerifier verifier= new MyMinLenVerifier(this, 3);
    MyPopupListener popupListener= new MyPopupListener(verifier);
    cmb.addPopupMenuListener(popupListener);
    add(cmb);
    JTextField tf1= new JTextField(5);
    tf1.setInputVerifier(verifier);
    add(tf1);
    JTextField tf2= new JTextField(5);
    tf2.setInputVerifier(verifier);
    add(tf2);
    SwingUtilities.invokeLater(() -> tf1.requestFocusInWindow());
    setVisible(true);
  }


  static public void main(String args[]) {
    EventQueue.invokeLater(DisableComboPopupByVerifier::new);
  }


  class MyMinLenVerifier extends InputVerifier {
    boolean satisfied;
    int minlen;
    Component parent;

    public MyMinLenVerifier(Component parent, int minlen) {
      this.minlen= minlen;
      this.parent= parent;
    }

    public boolean verify(JComponent input) {
      String s= ((JTextField)input).getText();
      if (s.length()>=minlen) {
        satisfied= true;
      }
      else {
        satisfied= false;
        JOptionPane.showMessageDialog(parent,
                    "Enter at least "+minlen+" characters.",
                    "Error", JOptionPane.ERROR_MESSAGE);
      }
      return satisfied;
    }
  }


  class MyPopupListener implements PopupMenuListener {
//** To accept all kinds of verifiers the specific one here needs to be replaced
 by the super class InputVerifier, which, however, makes casting necessary to access the boolean 'satisfied' below.
**//
    MyMinLenVerifier verifier;

    public MyPopupListener(MyMinLenVerifier verifier) {
      this.verifier= verifier;
    }

    public void popupMenuCanceled(PopupMenuEvent e) {
    }
    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    }
    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
//**  boolean 'satisfied' is a requirement in all passed verifiers. **//
      if (!verifier.satisfied) {
        BasicComboPopup popup= (BasicComboPopup)cmb.getUI()
                                        .getAccessibleChild(cmb, 0);
        SwingUtilities.invokeLater(() -> {
          popup.setVisible(false);
//        This restauration of the button background is not reliable!
          MetalComboBoxButton btn= (MetalComboBoxButton)cmb.getComponent(0);
          btn.getModel().setPressed(false);
          btn.repaint();
          ((JComboBox)e.getSource()).repaint();
        });
      }
    }
  }

}

For lack of a better idea I overrode toString() in MyMinLenVerifier由于缺乏更好的主意,我在 MyMinLenVerifier 中覆盖了 toString()

    @Override
    public String toString() {
      return "satisfied:"+satisfied;
    }

And the MyPopupListener class looks now like this: MyPopupListener 类现在看起来像这样:

  class MyPopupListener implements PopupMenuListener {
    InputVerifier verifier;

    public MyPopupListener(InputVerifier verifier) {
      this.verifier= verifier;
    }

.
.
.
    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
      if (verifier.toString().endsWith("false")) {
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM