【问题标题】:Adding vertical scroll to a JPopupMenu?向 JPopupMenu 添加垂直滚动?
【发布时间】:2012-03-06 11:56:33
【问题描述】:

我想添加一种方法来滚动浏览 JPopupMenu 中的菜单项,就像滚动浏览 JComboBox 中的项目列表一样。

假设我有 10 个菜单项。我想一次只显示 5 个菜单项,我会使用JPopupMenu 底部或顶部的垂直滚动按钮来显示未列出的菜单项并隐藏我刚刚看到的菜单项。

有可能吗?我正在使用 JIDE Software 的JideSplitButton,单击时会显示JPopupMenu。我试图保持我放置JideSplitButton 的命令栏的外观和感觉,所以我不想用JComboBox 替换它,除非我真的必须这样做。

【问题讨论】:

    标签: java swing jpopupmenu


    【解决方案1】:

    【讨论】:

    【解决方案2】:

    基本上你可以将任何JComponents添加到JPopupMenu,你可以通过将JPanel / JList与另一个JComponents嵌套来将JScrollpane添加到JPopup,

    注意但是有一个规则是swing GUI不允许同时出现两个轻量级的弹出窗口,最好的例子是common Bug in Swing about JComboBox in the JPopup

    您已经查看了 JWindow,创建一次并将其重新用于另一个操作,没有什么比检查弹出式 JWindow 对JCalendar by Kai Toedter 真正起作用的最佳方法了

    【讨论】:

      【解决方案3】:

      这是我使用滚动条创建的版本。这只是一个简单的示例,因此请根据您的需要进行调整:

      import java.awt.Component;
      import java.awt.Container;
      import java.awt.Dimension;
      import java.awt.Graphics;
      import java.awt.Insets;
      import java.awt.LayoutManager;
      import java.awt.event.AdjustmentEvent;
      import java.awt.event.AdjustmentListener;
      import java.awt.event.MouseWheelEvent;
      import java.awt.event.MouseWheelListener;
      import javax.swing.JPopupMenu;
      import javax.swing.JScrollBar;
      
      public class JScrollPopupMenu extends JPopupMenu {
          protected int maximumVisibleRows = 10;
      
          public JScrollPopupMenu() {
              this(null);
          }
      
      
          public JScrollPopupMenu(String label) {
              super(label);
              setLayout(new ScrollPopupMenuLayout());
      
              super.add(getScrollBar());
              addMouseWheelListener(new MouseWheelListener() {
                  @Override public void mouseWheelMoved(MouseWheelEvent event) {
                      JScrollBar scrollBar = getScrollBar();
                      int amount = (event.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
                                   ? event.getUnitsToScroll() * scrollBar.getUnitIncrement()
                                   : (event.getWheelRotation() < 0 ? -1 : 1) * scrollBar.getBlockIncrement();
      
                      scrollBar.setValue(scrollBar.getValue() + amount);
                      event.consume();
                  }
              });
          }
      
          private JScrollBar popupScrollBar;
          protected JScrollBar getScrollBar() {
              if(popupScrollBar == null) {
                  popupScrollBar = new JScrollBar(JScrollBar.VERTICAL);
                  popupScrollBar.addAdjustmentListener(new AdjustmentListener() {
                      @Override public void adjustmentValueChanged(AdjustmentEvent e) {
                          doLayout();
                          repaint();
                      }
                  });
      
                  popupScrollBar.setVisible(false);
              }
      
              return popupScrollBar;
          }
      
          public int getMaximumVisibleRows() {
              return maximumVisibleRows;
          }
      
          public void setMaximumVisibleRows(int maximumVisibleRows) {
              this.maximumVisibleRows = maximumVisibleRows;
          }
      
          public void paintChildren(Graphics g){
              Insets insets = getInsets();
              g.clipRect(insets.left, insets.top, getWidth(), getHeight() - insets.top - insets.bottom);
              super.paintChildren(g);
          }
      
          protected void addImpl(Component comp, Object constraints, int index) {
              super.addImpl(comp, constraints, index);
      
              if(maximumVisibleRows < getComponentCount()-1) {
                  getScrollBar().setVisible(true);
              }
          }
      
          public void remove(int index) {
              // can't remove the scrollbar
              ++index;
      
              super.remove(index);
      
              if(maximumVisibleRows >= getComponentCount()-1) {
                  getScrollBar().setVisible(false);
              }
          }
      
          public void show(Component invoker, int x, int y){
              JScrollBar scrollBar = getScrollBar();
              if(scrollBar.isVisible()){
                  int extent = 0;
                  int max = 0;
                  int i = 0;
                  int unit = -1;
                  int width = 0;
                  for(Component comp : getComponents()) {
                      if(!(comp instanceof JScrollBar)) {
                          Dimension preferredSize = comp.getPreferredSize();
                          width = Math.max(width, preferredSize.width);
                          if(unit < 0){
                              unit = preferredSize.height;
                          }
                          if(i++ < maximumVisibleRows) {
                              extent += preferredSize.height;
                          }
                          max += preferredSize.height;
                      }
                  }
      
                  Insets insets = getInsets();
                  int widthMargin = insets.left + insets.right;
                  int heightMargin = insets.top + insets.bottom;
                  scrollBar.setUnitIncrement(unit);
                  scrollBar.setBlockIncrement(extent);
                  scrollBar.setValues(0, heightMargin + extent, 0, heightMargin + max);
      
                  width += scrollBar.getPreferredSize().width + widthMargin;
                  int height = heightMargin + extent;
      
                  setPopupSize(new Dimension(width, height));
              }
      
              super.show(invoker, x, y);
          }
      
          protected static class ScrollPopupMenuLayout implements LayoutManager{
              @Override public void addLayoutComponent(String name, Component comp) {}
              @Override public void removeLayoutComponent(Component comp) {}
      
              @Override public Dimension preferredLayoutSize(Container parent) {
                  int visibleAmount = Integer.MAX_VALUE;
                  Dimension dim = new Dimension();
                  for(Component comp :parent.getComponents()){
                      if(comp.isVisible()) {
                          if(comp instanceof JScrollBar){
                              JScrollBar scrollBar = (JScrollBar) comp;
                              visibleAmount = scrollBar.getVisibleAmount();
                          }
                          else {
                              Dimension pref = comp.getPreferredSize();
                              dim.width = Math.max(dim.width, pref.width);
                              dim.height += pref.height;
                          }
                      }
                  }
      
                  Insets insets = parent.getInsets();
                  dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount);
      
                  return dim;
              }
      
              @Override public Dimension minimumLayoutSize(Container parent) {
                  int visibleAmount = Integer.MAX_VALUE;
                  Dimension dim = new Dimension();
                  for(Component comp : parent.getComponents()) {
                      if(comp.isVisible()){
                          if(comp instanceof JScrollBar) {
                              JScrollBar scrollBar = (JScrollBar) comp;
                              visibleAmount = scrollBar.getVisibleAmount();
                          }
                          else {
                              Dimension min = comp.getMinimumSize();
                              dim.width = Math.max(dim.width, min.width);
                              dim.height += min.height;
                          }
                      }
                  }
      
                  Insets insets = parent.getInsets();
                  dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount);
      
                  return dim;
              }
      
              @Override public void layoutContainer(Container parent) {
                  Insets insets = parent.getInsets();
      
                  int width = parent.getWidth() - insets.left - insets.right;
                  int height = parent.getHeight() - insets.top - insets.bottom;
      
                  int x = insets.left;
                  int y = insets.top;
                  int position = 0;
      
                  for(Component comp : parent.getComponents()) {
                      if((comp instanceof JScrollBar) && comp.isVisible()) {
                          JScrollBar scrollBar = (JScrollBar) comp;
                          Dimension dim = scrollBar.getPreferredSize();
                          scrollBar.setBounds(x + width-dim.width, y, dim.width, height);
                          width -= dim.width;
                          position = scrollBar.getValue();
                      }
                  }
      
                  y -= position;
                  for(Component comp : parent.getComponents()) {
                      if(!(comp instanceof JScrollBar) && comp.isVisible()) {
                          Dimension pref = comp.getPreferredSize();
                          comp.setBounds(x, y, width, pref.height);
                          y += pref.height;
                      }
                  }
              }
          }
      }
      

      【讨论】:

      • @Riquochet 你不应该重新实现其他依赖组件索引的方法吗?喜欢insert(Component component, int index)insert(Action a, int index)
      • @andrybak,是的,你应该这样做。我只是想创建一个简单但功能齐全的示例。为了完整起见,我确信还有更多工作要做。
      • 这真的很好 - 我想不通的一件事是,如果使用菜单中的光标键上下导航,如何使显示的菜单项滚动,这样当前选定的项目总是可见吗?
      【解决方案4】:

      除了上面的 JScrollPopupMenu 之外,我还需要一个子菜单中的滚动条(又名“拉右菜单”)。这似乎是一种更常见的情况。所以我改编了一个 JMenu 来使用名为 JScrollMenu 的 JScrollPopupMenu:

      import javax.swing.Action;
      import javax.swing.JButton;
      import javax.swing.JMenu;
      import javax.swing.JMenuItem;
      import javax.swing.JPopupMenu;
      import javax.swing.MenuElement;
      import javax.swing.UIManager;
      import javax.swing.plaf.MenuItemUI;
      import javax.swing.plaf.PopupMenuUI;
      import java.awt.Component;
      import java.awt.ComponentOrientation;
      
      
      
      public class JScrollMenu extends JMenu {
          // Covers the one in the JMenu because the method that creates it in JMenu is private
          /** The popup menu portion of the menu.*/
          private JPopupMenu popupMenu;
      
      
          /**
           * Constructs a new <code>JMenu</code> with no text.
           */
          public JScrollMenu() {
              this("");
          }
      
          /**
           * Constructs a new <code>JMenu</code> with the supplied string as its text.
           *
           * @param s the text for the menu label
           */
          public JScrollMenu(String s) {
              super(s);
          }
      
          /**
           * Constructs a menu whose properties are taken from the <code>Action</code> supplied.
           *
           * @param a an <code>Action</code>
           */
          public JScrollMenu(Action a) {
              this();
              setAction(a);
          }
      
      
          /**
           * Lazily creates the popup menu. This method will create the popup using the <code>JScrollPopupMenu</code> class. 
           */
          protected void ensurePopupMenuCreated() {
              if(popupMenu == null) {
                  this.popupMenu = new JScrollPopupMenu();
                  popupMenu.setInvoker(this);
                  popupListener = createWinListener(popupMenu);
              }
          }
      
      //////////////////////////////
      //// All of these methods are necessary because ensurePopupMenuCreated() is private in JMenu
      //////////////////////////////
          @Override
          public void updateUI() {
              setUI((MenuItemUI) UIManager.getUI(this));
      
              if(popupMenu != null) {
                  popupMenu.setUI((PopupMenuUI) UIManager.getUI(popupMenu));
              }
          }
      
      
          @Override
          public boolean isPopupMenuVisible() {
              ensurePopupMenuCreated();
              return popupMenu.isVisible();
          }
      
      
          @Override
          public void setMenuLocation(int x, int y) {
              super.setMenuLocation(x, y);
              if(popupMenu != null) {
                  popupMenu.setLocation(x, y);
              }
          }
      
          @Override
          public JMenuItem add(JMenuItem menuItem) {
              ensurePopupMenuCreated();
              return popupMenu.add(menuItem);
          }
      
          @Override
          public Component add(Component c) {
              ensurePopupMenuCreated();
              popupMenu.add(c);
              return c;
          }
      
          @Override
          public Component add(Component c, int index) {
              ensurePopupMenuCreated();
              popupMenu.add(c, index);
              return c;
          }
      
      
          @Override
          public void addSeparator() {
              ensurePopupMenuCreated();
              popupMenu.addSeparator();
          }
      
          @Override
          public void insert(String s, int pos) {
              if(pos < 0) {
                  throw new IllegalArgumentException("index less than zero.");
              }
      
              ensurePopupMenuCreated();
              popupMenu.insert(new JMenuItem(s), pos);
          }
      
          @Override
          public JMenuItem insert(JMenuItem mi, int pos) {
              if(pos < 0) {
                  throw new IllegalArgumentException("index less than zero.");
              }
              ensurePopupMenuCreated();
              popupMenu.insert(mi, pos);
              return mi;
          }
      
          @Override
          public JMenuItem insert(Action a, int pos) {
              if(pos < 0) {
                  throw new IllegalArgumentException("index less than zero.");
              }
      
              ensurePopupMenuCreated();
              JMenuItem mi = new JMenuItem(a);
              mi.setHorizontalTextPosition(JButton.TRAILING);
              mi.setVerticalTextPosition(JButton.CENTER);
              popupMenu.insert(mi, pos);
              return mi;
          }
      
          @Override
          public void insertSeparator(int index) {
              if(index < 0) {
                  throw new IllegalArgumentException("index less than zero.");
              }
      
              ensurePopupMenuCreated();
              popupMenu.insert(new JPopupMenu.Separator(), index);
          }
      
      
          @Override
          public void remove(JMenuItem item) {
              if(popupMenu != null){
                  popupMenu.remove(item);
              }
          }
      
          @Override
          public void remove(int pos) {
              if(pos < 0) {
                  throw new IllegalArgumentException("index less than zero.");
              }
              if(pos > getItemCount()) {
                  throw new IllegalArgumentException("index greater than the number of items.");
              }
              if(popupMenu != null){
                  popupMenu.remove(pos);
              }
          }
      
          @Override
          public void remove(Component c) {
              if(popupMenu != null){
                  popupMenu.remove(c);
              }
          }
      
          @Override
          public void removeAll() {
              if(popupMenu != null){
                  popupMenu.removeAll();
              }
          }
      
          @Override
          public int getMenuComponentCount() {
              return (popupMenu == null) ? 0 : popupMenu.getComponentCount();
          }
      
          @Override
          public Component getMenuComponent(int n) {
              return (popupMenu == null) ? null : popupMenu.getComponent(n);
          }
      
          @Override
          public Component[] getMenuComponents() {
              return (popupMenu == null) ? new Component[0] : popupMenu.getComponents();
          }
      
          @Override
          public JPopupMenu getPopupMenu() {
              ensurePopupMenuCreated();
              return popupMenu;
          }
      
          @Override
          public MenuElement[] getSubElements() {
              return popupMenu == null ? new MenuElement[0] : new MenuElement[]{popupMenu};
          }
      
      
          @Override
          public void applyComponentOrientation(ComponentOrientation o) {
              super.applyComponentOrientation(o);
      
              if(popupMenu != null) {
                  int ncomponents = getMenuComponentCount();
                  for(int i = 0; i < ncomponents; ++i) {
                      getMenuComponent(i).applyComponentOrientation(o);
                  }
                  popupMenu.setComponentOrientation(o);
              }
          }
      
          @Override
          public void setComponentOrientation(ComponentOrientation o) {
              super.setComponentOrientation(o);
              if(popupMenu != null) {
                  popupMenu.setComponentOrientation(o);
              }
          }
      }
      

      【讨论】:

      • 这是一个不错的解决方案 :) ...我试图弄清楚如何使用此解决方案强制滚动到子菜单中的特定项目,例如在 JList 中使用 ensureIndexIsVisible(index) ?
      • public void ensureIndexIsVisible(int index) { Rectangle rect = getMenuComponent(index).getBounds(); ((JScrollPopupMenu)popupMenu).getScrollBar().scrollRectToVisible(rect); }
      【解决方案5】:

      您也可以考虑使用 JidePopupMenu:Scrollable JPopupMenu

      【讨论】:

        【解决方案6】:

        由于我需要带有滚动条的弹出菜单,我只是重用了 JComboBox 中的弹出菜单。 诀窍是将 JComboBox 放在 JViewport 中,这样只有箭头按钮可见。您可以将其设置为小一像素甚至更小,并使用来自 JideSplitButton 的事件来打开弹出窗口。

        您可以在github 上找到代码。

        【讨论】:

          【解决方案7】:

          这是另一个我发现非常有用的:https://tips4java.wordpress.com/2009/02/01/menu-scroller/

          它可以像这样在 JMenu 或 JPopupMenu 上调用:

          MenuScroller.setScrollerFor(menuInstance, 8, 125, 3, 1);

          【讨论】:

            猜你喜欢
            • 2021-04-25
            • 2022-01-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-06
            • 2017-06-19
            • 2021-09-04
            相关资源
            最近更新 更多