Sta*_*lav 7 java swing jwindow jcolorchooser
我正在编写一个带有工具栏颜色的下拉组件.所以我从'Swing hacks'一书中提出了想法,稍微改变了构思并添加了Swing的标准JColorChooser.行为应该遵循:我点击一个按钮,出现一个带有颜色选择器的窗口; 我选择一种颜色,然后关闭下拉窗口,按钮的文字将颜色变为所选颜色.总的来说一切正常,但有一个令人不快的错误.在这些操作之后,UI冻结,按钮甚至不接受鼠标事件,如"鼠标悬停".这发生在我点击之前.然后,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();
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
我确信JColorChooser和选择模型有一些东西.但我无法理解这个想法.我尝试了requestFocus()和requestFocusInWindow().没有成功.我试图使用JDialog而不是JWindow.当我在对话框上按[x]时,一切都按照需要,但当我选择颜色时,UI也会冻结!
还有一点!如果我在下拉窗口中使用标签而不是颜色选择器并处理标签上的点击,一切正常:窗口关闭,没有冻结!
我将_dropDown.hideWindow()放在SwingUtilities.invokeLater()中.并没有成功.
我错过了什么?
与您问题的其他评论一样,我无法重现用户界面的冻结。我在 Windows 7、Sun JDK 7 和 Linux Mint、OpenJDK 7 上尝试了您的代码。但是,我认为您的代码需要改进。首先,对于它试图做的事情来说,它似乎相当冗长。其次,您正在使用一些最好避免的方法。
在你的第一段中,你说你的用户界面会冻结,直到你点击某个地方。这听起来很矛盾。如果它冻结了,您应该无法单击使其再次工作。所以我认为你只是有注意力问题?如我错了请纠正我。事实上,选择颜色后按钮会失去焦点,因此您必须单击两次才能再次打开颜色选择器。所以你的更改侦听器应该如下所示:
public void stateChanged(ChangeEvent ce) {
button.setForeground(colorChooser.getColor());
DropDownWindow.this.setVisible(false);
// the drop down window had the focus while being displayed
// so after it closes, return focus to the button
button.requestFocus();
}
Run Code Online (Sandbox Code Playgroud)
其次,直接在 AWT 事件调度线程上注册一个侦听器来捕获一些鼠标事件。我不明白为什么你要这样做而不是使用 UI 组件的普通鼠标侦听器。AWT 事件线程只能用于观察事件,以进行分析、测试和调试等目的。切勿使用它来更改 UI 的状态或向其推送昂贵的代码。对于 UI 更改,您始终在 UI 组件上使用特定的事件侦听器,或者SwingWorkers进行更昂贵的计算。
根据您所使用的平台或 Java 运行时实现,您对 AWT 事件线程的使用可能会导致 UI 变得有些反应迟钝,因为您正在从中更改窗口的可见性状态。
另外,您使用invokeLater创建下拉窗口不会改变任何内容,因为这只是将代码放置在 AWT 事件线程中,无论如何它都会结束。这些方法invokeLater及其友元invokeAndWait用于将 Java 所谓的初始线程(其中一个执行该main方法,因此称为“主”线程)与事件分派线程同步。您不使用它们从 UI 线程异步运行代码。例如,如果您运行一个创建窗口或框架的 main 方法,如下所示
public static void main (String[] args) {
new JFrame().setVisible(true);
}
Run Code Online (Sandbox Code Playgroud)
您始终可以将其视为由 Java 运行时环境推迟到事件线程。所以基本上发生的事情是这样的:
public static void main (String[] args) {
// JRE 'starts' Swing by creating an event thread and then
SwingUtilities.invokeLater(new Runnable() {
public void run (Runnable r) {
new JFrame().setVisible(true);
});
}
Run Code Online (Sandbox Code Playgroud)
因此,您的 main 方法将该代码封装在另一个代码中Runnable,这基本上具有相同的效果,但不能解决您的问题。
我重写了你的程序并缩短了一点。我不确定它是否完全符合您想要做的事情。我在 Windows 和 Linux 上尝试过这段代码,没有任何问题。无论您是否打开颜色选择器,您都可以看到按钮上的鼠标事件仍在处理中。
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JWindow;
import javax.swing.colorchooser.AbstractColorChooserPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
class DropDownWindow extends JWindow {
static void setColorChooserPanels (JColorChooser jcc, String name) {
for (AbstractColorChooserPanel p : jcc.getChooserPanels()) {
if (!p.getDisplayName().equals(name)) {
jcc.removeChooserPanel(p);
}
}
}
final JColorChooser colorChooser;
DropDownWindow (JFrame ownerFrame, final JButton button) {
super(ownerFrame);
colorChooser = new JColorChooser();
setColorChooserPanels(colorChooser, "Swatches");
colorChooser.setVisible(true);
colorChooser.getSelectionModel().addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent ce) {
button.setForeground(colorChooser.getColor());
DropDownWindow.this.setVisible(false);
button.requestFocus();
}
});
add(colorChooser);
setSize(colorChooser.getPreferredSize());
pack();
button.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent e) {
Point pt = button.getLocationOnScreen();
pt.translate(0, button.getHeight());
DropDownWindow.this.setLocation(pt);
DropDownWindow.this.setVisible(true);
}
});
}
}
class MyFrame extends JFrame {
final JButton button;
MyFrame () {
super();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 100);
setLocation(500, 300);
button = new JButton("Choose Color");
button.addMouseListener(new MouseAdapter() {
public void mouseEntered (MouseEvent event) {
System.out.println("mouse entered at: (" + event.getXOnScreen() +
", " + event.getYOnScreen() + ")");
}
});
button.addMouseListener(new MouseAdapter() {
public void mouseExited (MouseEvent event) {
System.out.println("mouse exited at: (" + event.getXOnScreen() +
", " + event.getYOnScreen() + ")");
}
});
add(button);
DropDownWindow ddw = new DropDownWindow(this, button);
setVisible(true);
}
}
public class Test {
public static void main(String[] args) {
new MyFrame();
}
}
Run Code Online (Sandbox Code Playgroud)
请尝试此代码并告诉我您的问题是否消失。如果没有,请进一步详细说明您所经历的影响。UI真的会冻结吗?您有响应能力问题吗?或者这只是一个焦点问题,需要您单击比您想要的更多的次数。
| 归档时间: |
|
| 查看次数: |
913 次 |
| 最近记录: |