2014-10-06 29 views
2

我在我的應用程序中有一個彈出式菜單,我想用自定義的替換,以使其與應用程序的其餘部分的外觀&相匹配。實質上,我不想在彈出窗口中使用正常的菜單項,而是要重用應用程序中其他位置已存在的組件,以便以「分頁」方式瀏覽項目層次結構,而不是使用子菜單。因此,如果您單擊包含子項目的列表中的項目,則會顯示下一個頁面,將列表中的當前項目替換爲所單擊項目的子項列表。更新JPopupMenu高度,同時可見

使用「分頁」組件的好處是它可以很好地適應應用程序的其他部分(它已經用於其他不是彈出窗口的地方),並且在瀏覽時有一些漂亮的動畫效果頁面。

我遇到的問題是分頁組件的首選高度在顯示不同的頁面時發生變化(列表中項目的數量發生變化),我希望彈出菜單高度更新以便它適合分頁組件確切地說,但到目前爲止,我們嘗試更新彈出式菜單的高度時卻發現失敗。

下面是一個示例應用程序演示該問題:

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JPopupMenu; 

public class PopupSizeTestFrame extends JFrame { 

    private static final int PREFFERED_WIDTH = 300; 
    private static JPanel resizingPanel; 
    private static JPopupMenu popupMenu; 
    private static PopupSizeTestFrame frame; 

    private static void resizePopupMenu() { 
     // What should I do here so that the size of the popup menu is updated? 

     /** 
     * Calling pack() works in this example, but I cannot call pack in the 
     * actual application since there is a smooth transition animation when 
     * clicking on the inner panel and pack() essentially re-adds the 
     * components of the popup menu (removeNotify and addNotify is called), 
     * which interferes with the animation and causes the menu to flicker. 
     * The flickering when clicking the inner panel can also be seen in this 
     * example when uncommenting the pack() call. 
     */ 
     //popupMenu.pack(); 
     // 
     // I tried combinations of the following, without success: 
     //popupMenu.invalidate(); 
     //popupMenu.revalidate(); 
     //popupMenu.validate(); 
     //popupMenu.doLayout(); 

     // Repaint 
     popupMenu.repaint(); 
    } 

    public static void main(String args[]) { 
     // Create popup child panel with height that changes when clicked 
     resizingPanel = new JPanel(new BorderLayout()); 
     int initialHeight = 30; 
     final JLabel label = new JLabel("Click me (" + initialHeight + "px)"); 
     resizingPanel.add(label); 
     resizingPanel.setBackground(Color.GREEN); 
     resizingPanel.setPreferredSize(new Dimension(PREFFERED_WIDTH, initialHeight)); 
     resizingPanel.addMouseListener(new MouseAdapter() { 
      private int clickCount = 0; 

      @Override 
      public void mouseClicked(MouseEvent e) { 
       int height = ((clickCount % 3) + 1) * 50; 
       resizingPanel.setPreferredSize(new Dimension(PREFFERED_WIDTH, height)); 
       clickCount++; 
       label.setText("Click me (" + height + "px)"); 
       resizePopupMenu(); 
      } 
     }); 

     // Create popup 
     popupMenu = new JPopupMenu(); 
     popupMenu.setLayout(new BorderLayout()); 
     popupMenu.add(new JLabel("Header"), BorderLayout.NORTH); 
     popupMenu.add(resizingPanel, BorderLayout.CENTER); 
     popupMenu.add(new JLabel("Footer"), BorderLayout.SOUTH); 

     // Create frame 
     frame = new PopupSizeTestFrame(); 
     frame.setLayout(new BorderLayout()); 
     frame.setPreferredSize(new Dimension(400, 400)); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.addMouseListener(new MouseAdapter() { 

      @Override 
      public void mouseReleased(MouseEvent e) { 
       if (e.isPopupTrigger()) { 
        popupMenu.show(frame, e.getX(), e.getY()); 
       } 
      } 
     }); 

     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 

運行例如當上面,你會看到,如果它被關閉和被點擊的內板後打開彈出式菜單尺寸被更新,但它在彈出窗口不可見時不會更新。

我可以在resizePopupMenu()中做些什麼來更新JPopupMenu的高度?

+2

必須爲包容器調用JPopupMemu(BasicPopupSomething,可從Window或SwingUtilities訪問) – mKorbel 2014-10-06 10:29:47

+0

@mKorbel非常感謝你,這個技巧:'SwingUtilities.getWindowAncestor(popupMenu).pack();'。如果您將其添加爲答案,我會將其標記爲正確。 – ughzan 2014-10-06 12:43:30

+0

正確/正確的方式之一+1 :-) – mKorbel 2014-10-06 12:54:17

回答

1

對於問題的彈出始終是一個重量級的組件測試程序和下面的工作(如mKorbel建議)

private static void resizePopupMenu() { 
    SwingUtilities.getWindowAncestor(popupMenu).pack(); 
} 

對於實際的應用程序時,彈出的是邊界是在應用程序內創建爲輕量級組件,防止其被調整爲pack(),並且阻止其調整超出應用程序邊界的大小。

我試圖設置該屬性...

JPopupMenu.setDefaultLightWeightPopupEnabled(false); 

但隨後中質成分被創造,它仍然不會調整過去的應用程序邊界。

所以我只好先逼「所有者」組件的所有彈出窗口是重量級與下面的一段不幸的代碼

// Set owner to component on which popup is shown or any of its parents 
final Component owner = ...; 
AccessController.doPrivileged(new PrivilegedAction<Void>() { 
    @Override 
    public Void run() { 
     try { 
      Field field; 
      if (System.getProperty("java.version").startsWith("1.6.0")) { 
       Class clazz = Class.forName("javax.swing.PopupFactory"); 
       field = clazz.getDeclaredField("forceHeavyWeightPopupKey"); 
      } else { //JDK 1.7.0, 1.8.0 
       Class clazz = Class.forName("javax.swing.ClientPropertyKey"); 
       field = clazz.getDeclaredField("PopupFactory_FORCE_HEAVYWEIGHT_POPUP"); 
      } 
      field.setAccessible(true); 
      owner.putClientProperty(field.get(null), Boolean.TRUE); 
     } catch (Exception ex) { 
      Exceptions.printStackTrace(ex); 
     } 
     return null; 
    } 
}); 

強制彈出菜單後永遠是一個重量級的組件此

SwingUtilities.getWindowAncestor(popupMenu).pack(); 

也可以更新彈出窗口的大小,甚至超出應用程序的範圍。

0

我認爲你可以在彈出菜單中添加PopupMenuListener,並處理menuWillBecomeVisible(...)事件來設置彈出窗口的大小。

+0

彈出窗口的大小必須在其可見之後發生變化,當其中一個子項的首選大小發生變化時。 – ughzan 2014-10-07 06:19:10