JComboBox实现下拉框向上显示

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);
   }
} 

结果如图所示:
JComboBox实现下拉框向上显示


第 25 行,在弹出列表第一次显示之前,高度其实是不知道的,所以如果直接用 height 的话第一次弹出位置是不对的,第二次才会正确。
错误的结果如图所示:
JComboBox实现下拉框向上显示

这样写起来是可以用了,但有几个空方法放着很不爽,继续研究 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();
    }
}

你可能感兴趣的:(UI,swing,Google,UP,sun)