Vin*_*ini 8 java swing user-experience jcombobox visual-glitch
当你有一个摆动JComboBox并点击它的边框时,弹出窗口会立即显示并消失.当我说点击时,我的意思是按下鼠标左键并立即释放.
它可能被认为是糟糕的用户体验,因为没有用户会期望它发生.单击组合框的边框时,任何用户都会期望以下行为之一:
当然没有用户会期望弹出窗口立即打开和关闭.
用户不会故意点击边框.但是当组合框很小并且他试图快速点击它时,它可能经常发生.
在2000年,有人将此行为注册为openjdk网站中的错误:https://bugs.openjdk.java.net/browse/JDK-4346918
他们已经认识到它是一个错误,但是通过以下观察结果解决了它:"无法修复":
我已经能够重现这个问题,但它并不重要,所以我不打算解决它.问题是,单击边框后释放鼠标时,组合框的下拉部分将隐藏.这个bug没有很大的影响.
我同意他们的看法,它没有产生很大的影响.但我仍然认为这会导致糟糕的用户体验,我想知道是否有一个简单的解决方法是让用户点击其边框时弹出窗口要么保持打开状态,要么根本不打开.
通过单击任何JComboBox边框上的鼠标左键可以重现所描述的行为.请参阅下面一个可以复制的简单代码:
import java.awt.FlowLayout;
import javax.swing.*;
public class JComboBoxUX{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run(){
JComboBox<String> combobox = new JComboBox<String>(
new String[]{"aaaaaaaaaa","bbbbbbbb","ccccccccc"});
JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));
panel.add(combobox);
JFrame frame = new JFrame("JComboBox UX");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.setSize(300, 150);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
问题似乎在于:
class BasicComboPopup extends ... {
private Handler getHandler() {
if (handler == null) {
handler = new Handler();
}
return handler;
}
private class Handler implements ... MouseListener ... {
public void mouseReleased(MouseEvent e) {
//...
Component source = (Component)e.getSource();
Dimension size = source.getSize();
Rectangle bounds = new Rectangle( 0, 0, size.width - 1, size.height - 1 );
if ( !bounds.contains( e.getPoint() ) ) {
//...
comboBox.setPopupVisible(false);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
通过减一size.width和size.height,鼠标落在箭头按钮的范围之外,并在弹出菜单是隐藏的.
解决问题是有问题的.这个Handler类是private,所以我们不能扩展它,getHandler()是的private,所以我们不能覆盖它们BasicComboPopup.
可以扩展MetalComboBoxUI和覆盖createPopup()以返回自定义ComboPopup,例如一个自定义,BasicComboPopup但扩展createMouseListener()以返回类似于Handler上面的类,但没有减去自定义.
哦,为你想支持的每个LAF做同样的事情.育.
从另一个方向攻击问题时,可以扩展MetalComboBoxButton(返回e.getSource())并覆盖getSize()方法,以便在显示菜单时返回两个方向上大一个像素的维度.当然,您仍然需要扩展和覆盖MetalComboBoxUI以创建和安装此自定义按钮.
同样,你需要为你想支持的每个LAF做同样的事情.再次,yuk.
不幸的是,Swing似乎没有必要的钩子来轻松覆盖所需的功能,并且已经将各种类标记为私有内部实现细节,从而阻止了它们的重用(以便在以后如果他们想要更改内部结构时防止破坏).