JComboBox 向上弹出
写一个东西用到了 JComboBox,缺省情况下下拉框的列表是向下弹出的(否则也不会叫“下拉框”了,呵呵),但现在想要实现让它往上弹出这么一个功能。首先要看的就是 JComboBox 本身是否支持,比如有一个属性可以直接指定让它往下或者往上弹出,很遗憾,没有找到。它弹出其实是一个类似于弹出菜单的一个东西,就想着是不是能在弹出之前设定一下位置,就可以指定在哪里弹出了,跟弹出菜单的原理一样。
首先一个问题是怎么才能知道弹出列表就要弹出呢?运气还不错,JComboBox 里面有 addPopupMenuListener 这个方法,PopupMenuListener 里有 popupMenuWillBecomeVisible() 应该就是在弹出列表显示之前会调用的了。
下一个问题就是怎么才能拿到这个弹出列表的引用,本以为 JComboBox 本身就提供了,没想到竟然没找到,Google 也没找出来,只好自己看代码,从 javax.swing.JComboBox:setPopupVisible() 方法知道,弹出列表通过 UI 来控制的,然后在 javax.swing.plaf.basic.BasicComboBoxUI:installUI() 找到了创建的代码 popup = createPopup();,返回的对象是 BasicComboPopup,是从 JPopupMenu 继承下来的。那怎么才能拿到 popup 的引用呢?在这个类里直接查找“popup”,最后找到了 getAccessibleChild() 这个方法,里面的注释也说明第 0 个子控件就是我们千辛万苦所要寻找的弹出列表了,哈哈。
这下就好办了,加一个监听器,将 popup 的位置往上挪整个控件的高度这么大的距离,就可以达到我们的目的。
PopupComboBox.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JComboBox;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.basic.BasicComboPopup;
/**
* @author SuperMMX
*/
class PopupComboBox {
public static void main(String[] args) {
Object[] values = { "Zhang San", "Li Si", "Wang Wu", "Sun Liu"};
JFrame frame = new JFrame("Pop *UP* JComboBox");
JComboBox comboBox = new JComboBox(values);
comboBox.addPopupMenuListener(new PopupMenuListener() {
public void popupMenuCanceled(PopupMenuEvent e) {
// Don't care
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
// Don't care
}
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
JComboBox comboBox = (JComboBox)e.getSource();
BasicComboPopup popup = (BasicComboPopup)comboBox.getUI().getAccessibleChild(comboBox, 0);
int height = popup.getHeight();
if (height == 0) {
height = popup.getPreferredSize().height;
}
popup.setLocation(comboBox.getLocationOnScreen().x, comboBox.getLocationOnScreen().y - height);
}
}
);
frame.add(comboBox, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(250, 150);
frame.setVisible(true);
}
}
结果如图所示:
第 25 行,在弹出列表第一次显示之前,高度其实是不知道的,所以如果直接用 height 的话第一次弹出位置是不对的,第二次才会正确。
错误的结果如图所示:
这样写起来是可以用了,但有几个空方法放着很不爽,继续研究 JComboBox,发现它里面直接支持 firePopupMenuWillBecomeVisible(),这样只要继承 JComboBox,重写 firePopupMenuWillBecomeVisible() 方法,将以上相关代码写进去就可以了。这样看起来清爽一些。代码如下:
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JComboBox;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.basic.BasicComboPopup;
/**
* @author SuperMMX
*/
class PopupComboBox extends JComboBox {
public PopupComboBox(Object[] values) {
super(values);
}
public static void main(String[] args) {
Object[] values = { "Zhang San", "Li Si", "Wang Wu", "Sun Liu"};
JFrame frame = new JFrame("Pop *UP* JComboBox");
JComboBox comboBox = new PopupComboBox(values);
frame.add(comboBox, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(250, 150);
frame.setVisible(true);
}
public void firePopupMenuWillBecomeVisible() {
// Pop *UP*
BasicComboPopup popup = (BasicComboPopup)getUI().getAccessibleChild(this, 0);
int height = popup.getHeight();
if (height == 0) {
height = popup.getPreferredSize().height;
}
popup.setLocation(getLocationOnScreen().x, getLocationOnScreen().y - height);
super.firePopupMenuWillBecomeVisible();
}
}