Java 弹出按钮
注意:您可能需要编译并运行我的示例才能完全理解我的问题。如果这不符合犹太洁食,我提前道歉。
我正在尝试创建一个基于 JToggleButton
和 JPopupMenu
的 Swing 控件。
如果弹出菜单可见,则选择切换按钮;如果弹出菜单不可见,则取消选择切换按钮。因此,其行为类似于 JComboBox,只不过弹出窗口可以包含任意组件。
下面的代码是我如何创建控件的示例(除了它在自己的类中......类似于 JPopupToggleButton )。不幸的是,它在不同的外观和感觉下表现出不同的行为(我已经使用 Metal 和 Nimbus 对其进行了测试)。
此处发布的代码在 Metal 中的行为符合预期,但在 Nimbus 中则不然。使用 Nimbus 时,只需通过重复单击切换按钮来显示和隐藏弹出窗口,您就会明白我的意思。
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class PopupButtonExample extends JFrame
{
public static void main( String[] args )
{
java.awt.EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
PopupButtonExample example = new PopupButtonExample();
example.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
example.setVisible( true );
}
});
}
public PopupButtonExample()
{
super( "Components in Popup" );
JPanel popupPanel = new JPanel();
popupPanel.setLayout( new BorderLayout() );
popupPanel.add( new JLabel( "This popup has components" ),
BorderLayout.NORTH );
popupPanel.add( new JTextArea( "Some text", 15, 20 ),
BorderLayout.CENTER );
popupPanel.add( new JSlider(), BorderLayout.SOUTH );
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add( popupPanel );
final JToggleButton popupButton = new JToggleButton( "Show Popup" );
popupButton.addActionListener( new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
if( popupButton.isSelected() )
popupMenu.show( popupButton, 0, popupButton.getHeight() );
}
});
popupMenu.addPopupMenuListener( new PopupMenuListener()
{
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent pme) {}
@Override
public void popupMenuCanceled(PopupMenuEvent pme) {}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent pme) {
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
Point componentLoc = popupButton.getLocationOnScreen();
mouseLoc.x -= componentLoc.x;
mouseLoc.y -= componentLoc.y;
if( !popupButton.contains( mouseLoc ) )
popupButton.setSelected( false );
}
});
JPanel toolBarPanel = new JPanel();
toolBarPanel.add( popupButton );
JToolBar toolBar = new JToolBar();
toolBar.add( toolBarPanel );
setLayout( new BorderLayout() );
add( toolBar, BorderLayout.PAGE_START );
setPreferredSize( new Dimension( 640, 480 ) );
pack();
}
}
注释掉以下几行会使代码在 Nimbus 中按预期运行,但在 Metal 中则不然。再次,只需继续单击切换按钮即可明白我的意思。
// Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
// Point componentLoc = popupButton.getLocationOnScreen();
// mouseLoc.x -= componentLoc.x;
// mouseLoc.y -= componentLoc.y;
// if( !popupButton.contains( mouseLoc ) )
所以这是我的两个问题:
(1)在 Nimbus 中,为什么隐藏弹出面板的点击不会像 Metal 那样传递到切换按钮?
(2) 我怎样才能解决这个问题,使其适用于所有外观和感觉?
Note: You may have to compile and run my example to fully understand my question. If this is not kosher, I apologize in advance.
I am trying to create a Swing control that is based on a JToggleButton
and a JPopupMenu
.
The toggle button is selected iff the popup menu is visible, and the toggle button is deselected iff the popup menu is not visible. Thus, the behavior is similar to a JComboBox
, except that the popup can contain arbitrary components.
The code that follows is an example of how I would create the control (except that it would be in its own class... something like a JPopupToggleButton
). Unfortunately, it exhibits different behavior under different look and feels (I have tested it with Metal and Nimbus).
The code as posted here behaves as expected in Metal, but not in Nimbus. When using Nimbus, just show and hide the popup by repeatedly clicking the toggle button and you will see what I mean.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class PopupButtonExample extends JFrame
{
public static void main( String[] args )
{
java.awt.EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
PopupButtonExample example = new PopupButtonExample();
example.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
example.setVisible( true );
}
});
}
public PopupButtonExample()
{
super( "Components in Popup" );
JPanel popupPanel = new JPanel();
popupPanel.setLayout( new BorderLayout() );
popupPanel.add( new JLabel( "This popup has components" ),
BorderLayout.NORTH );
popupPanel.add( new JTextArea( "Some text", 15, 20 ),
BorderLayout.CENTER );
popupPanel.add( new JSlider(), BorderLayout.SOUTH );
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add( popupPanel );
final JToggleButton popupButton = new JToggleButton( "Show Popup" );
popupButton.addActionListener( new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
if( popupButton.isSelected() )
popupMenu.show( popupButton, 0, popupButton.getHeight() );
}
});
popupMenu.addPopupMenuListener( new PopupMenuListener()
{
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent pme) {}
@Override
public void popupMenuCanceled(PopupMenuEvent pme) {}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent pme) {
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
Point componentLoc = popupButton.getLocationOnScreen();
mouseLoc.x -= componentLoc.x;
mouseLoc.y -= componentLoc.y;
if( !popupButton.contains( mouseLoc ) )
popupButton.setSelected( false );
}
});
JPanel toolBarPanel = new JPanel();
toolBarPanel.add( popupButton );
JToolBar toolBar = new JToolBar();
toolBar.add( toolBarPanel );
setLayout( new BorderLayout() );
add( toolBar, BorderLayout.PAGE_START );
setPreferredSize( new Dimension( 640, 480 ) );
pack();
}
}
Commeting out the following lines makes the code behave as expected in Nimbus, but not in Metal. Again, just keep clicking the toggle button to see what I mean.
// Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
// Point componentLoc = popupButton.getLocationOnScreen();
// mouseLoc.x -= componentLoc.x;
// mouseLoc.y -= componentLoc.y;
// if( !popupButton.contains( mouseLoc ) )
So here are my two questions:
(1) In Nimbus, why does the click that hides the popup panel not get passed to the toggle button, as it does with Metal?
(2) How can I solve this problem so that it works with all look and feels?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Nimbus 问题太多(开发在中间某个地方结束)我发现与 Metal 相比,您需要在 JToggleButton 上单击三次鼠标
每个标准 L&F 都有自己的特定问题,尤其是 SystemLookAndFeel
使用 JWindow 而不是 JPopup,因为使用 JPopup 也存在另一个错误,例如 JPopup 与 JCombobox
Nimbus is too buggy (and development ended somewhere in the middle) I see that you need three mouse click to the JToggleButton in compare with Metal
every standard L&F have got own specific issues, especially SystemLookAndFeel
use JWindow rather that JPopup, because with JPopup there are another Bugs too e.g. JPopup with JCombobox
经过一番调查,我找到了 Nimbus 和 Metal 之间差异的原因。使用以下标志(至少
BasicPopupMenuUI
)来控制弹出窗口关闭时事件的消耗:使用 Nimbus 时,此标志返回
true
。使用 Metal 时,返回false
。因此,方法popupMenuWillBecomeInvisible
应定义如下:After some investigation, I found the cause for the difference between Nimbus and Metal. The following flag is used (at least by
BasicPopupMenuUI
) to control the consumption of events when a popup is closed:When using Nimbus, this returns
true
. When using Metal, this returnsfalse
. Thus, the methodpopupMenuWillBecomeInvisible
should be defined as follows: