我正在编写一个带有工具栏颜色的下拉组件。所以我从“Swing hacks”一书中吸取了一些想法,稍微改变了概念,并将 Swing 的标准 JColorChooser 添加到下拉列表中。行为应该如下:我单击一个按钮,然后出现一个带有颜色选择器的窗口;我选择一种颜色,下拉窗口关闭,按钮的文本将颜色更改为选择的颜色。总的来说,一切正常,但有一个令人不快的错误。在这些操作之后,用户界面冻结,按钮甚至不接受鼠标事件,如“鼠标悬停”。这会发生,直到我点击。然后 UI 的行为如所愿。
这是带有概念的代码。
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.colorchooser.AbstractColorChooserPanel;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.metal.MetalComboBoxIcon;
class DropDownComponent2 {
private JWindow _window;
private boolean _windowShouldBeShown = false;
private JComponent _component;
private AbstractButton _button;
private JFrame _ownerFrame;
public DropDownComponent2(JFrame ownerFrame, JComponent component, AbstractButton button) {
_ownerFrame = ownerFrame;
_component = component;
_button = button;
_button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
_window.setVisible(false);
Point pt = _button.getLocationOnScreen();
pt.translate(0, _button.getHeight());
_window.setLocation(pt);
showWindow();
_windowShouldBeShown = true;
}
});
_button.addAncestorListener(new AncestorListener() {
public void ancestorAdded(AncestorEvent event){
_window.setVisible(false);
}
public void ancestorRemoved(AncestorEvent event){
_window.setVisible(false);
}
public void ancestorMoved(AncestorEvent event){
if (event.getSource() != _window) {
System.out.println("Ansestor moved");
_window.setVisible(false);
}
}
});
Toolkit.getDefaultToolkit().addAWTEventListener(
new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
if (event.getID() == MouseEvent.MOUSE_CLICKED) {
if ( !_window.getBounds().contains( MouseInfo.getPointerInfo().getLocation() )) {
if (_windowShouldBeShown)
_windowShouldBeShown = false;
else {
_window.setVisible(false);
}
}
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
_window = new JWindow(_ownerFrame);
_window.getContentPane().add(component);
_window.addWindowFocusListener(new WindowAdapter() {
public void windowLostFocus(WindowEvent evt) {
System.out.println("window lost focus");
_window.setVisible(false);
}
});
_window.pack();
}
private Rectangle getScreenRect() {
return new Rectangle(java.awt.Toolkit.getDefaultToolkit().getScreenSize());
}
public void showWindow() {
Rectangle screenRect = getScreenRect();
Rectangle windowRect = _window.getBounds();
int sx1 = screenRect.x;
int sx2 = screenRect.x + screenRect.width;
int sy1 = screenRect.y;
int sy2 = screenRect.y + screenRect.height;
int wx1 = windowRect.x;
int wx2 = windowRect.x + windowRect.width;
int wy1 = windowRect.y;
int wy2 = windowRect.y + windowRect.height;
if (wx2 > sx2) {
_window.setLocation(wx1-(wx2-sx2), _window.getY());
}
if (wx1 < sx1) {
_window.setLocation(0, _window.getY());
}
if (wy2 > sy2) {
_window.setLocation(_window.getX(), wy1-(wy2-wy1));
}
if (wy2 < sy1) {
_window.setLocation(_window.getX(), 0);
}
_window.setVisible(true);
}
public void hideWindow() {
_window.setVisible(false);
}
}
public class DropDownFrame extends JFrame {
JButton _button;
JColorChooser _colorChooser;
DropDownComponent2 _dropDown;
JWindow _window;
public DropDownFrame() {
_colorChooser = new JColorChooser();
_colorChooser.setPreviewPanel(new JPanel());
_colorChooser.setColor(Color.RED);
// Remove panels other than Swatches
AbstractColorChooserPanel[] panels = _colorChooser.getChooserPanels();
for (int i=0; i<panels.length; i++) {
if (!panels[i].getDisplayName().equals("Swatches"))
_colorChooser.removeChooserPanel(panels[i]);
}
_colorChooser.getSelectionModel().addChangeListener(new ChangeListener() {
// ### I think the key point is there
@Override
public void stateChanged(ChangeEvent e) {
_dropDown.hideWindow();
_button.setForeground(_colorChooser.getColor());
}
});
_button = new JButton("Show JWindow");
_button.setIcon(new MetalComboBoxIcon());
_button.setHorizontalTextPosition(SwingConstants.LEFT);
this.getContentPane().add(_button);
_dropDown = new DropDownComponent2(DropDownFrame.this, _colorChooser, _button);
pack();
setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new DropDownFrame();
}
});
}
}
我确定 JColorChooser 和选择模型有一些东西。但我无法理解这个想法。我尝试了 requestFocus() 和 requestFocusInWindow()。没有成功。我尝试使用 JDialog 而不是 JWindow。当我在对话框上按 [x] 时,一切都如愿以偿,但是当我选择颜色时,UI 也会冻结!
还有一点!如果我在下拉窗口中使用标签而不是颜色选择器并单击标签,一切正常:窗口关闭,并且没有冻结!
我将 _dropDown.hideWindow() 放在 SwingUtilities.invokeLater() 中。并且没有成功。
我错过了什么?