2015-02-24 62 views
0

我將默認的MenuMenuItem類擴展爲向其添加一些動畫效果。如何在JavaFX中獲取Menu或MenuItem寬度?

問題是我需要知道我正在處理的MenuMenuItem的寬度和高度。此類不延伸NodeRegion,所以沒有公開的方法來獲得它們的大小。尺寸由MenuItem中的文字大小加上相應​​的默認填充值組成,我可以計算文本需要多少空間,但我無法獲得MenuItem有多少填充。

有一種方法稱爲impl_styleableGetNode(),它返回一個Node但它總是爲我返回null。

無論如何獲得大小? MenuBar也似乎沒有公開任何有用的方法。

編輯:

這裏是我的課,我想實現this材料設計按鈕進入Menu類。基本上我使用setGraphic()方法渲染所有按鈕。它的工作完美,但我使用Pane寬度,它沒有考慮到Menu的填充,所以效果不完整。

public class MaterialDesignMenu extends Menu { 

    private Pane stackPane = new Pane(); 
    private Label label = new Label(); 

    private Circle circleRipple; 
    private Rectangle rippleClip = new Rectangle(); 
    private Duration rippleDuration = Duration.millis(250); 
    private double lastRippleHeight = 0; 
    private double lastRippleWidth = 0; 
    private Color rippleColor = new Color(1, 0, 0, 0.3); 

    public MaterialDesignMenu() { 
     init(""); 
    } 

    public MaterialDesignMenu(String text) { 
     init(text); 
    } 

    public MaterialDesignMenu(String text, Node graphic) { 
     init(text); 
    } 

    private void init(String text){ 
     label.setText(text); 
     createRippleEffect(); 

     stackPane.getChildren().addAll(circleRipple, label); 
     setGraphic(stackPane); 
    } 

    private void createRippleEffect() { 
     circleRipple = new Circle(0.1, rippleColor); 
     circleRipple.setOpacity(0.0); 
     // Optional box blur on ripple - smoother ripple effect 
     //circleRipple.setEffect(new BoxBlur(3, 3, 2)); 
     // Fade effect bit longer to show edges on the end of animation 
     final FadeTransition fadeTransition = new FadeTransition(rippleDuration, circleRipple); 
     fadeTransition.setInterpolator(Interpolator.EASE_OUT); 
     fadeTransition.setFromValue(1.0); 
     fadeTransition.setToValue(0.0); 
     final Timeline scaleRippleTimeline = new Timeline(); 
     final SequentialTransition parallelTransition = new SequentialTransition(); 
     parallelTransition.getChildren().addAll(
       scaleRippleTimeline, 
       fadeTransition 
     ); 
     // When ripple transition is finished then reset circleRipple to starting point 
     parallelTransition.setOnFinished(event -> { 
      circleRipple.setOpacity(0.0); 
      circleRipple.setRadius(0.1); 
     }); 

     stackPane.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> { 
      parallelTransition.stop(); 
      // Manually fire finish event 
      parallelTransition.getOnFinished().handle(null); 
      circleRipple.setCenterX(event.getX()); 
      circleRipple.setCenterY(event.getY()); 

      // Recalculate ripple size if size of button from last time was changed 
      if (stackPane.getWidth() != lastRippleWidth || stackPane.getHeight() != lastRippleHeight) { 
       lastRippleWidth = stackPane.getWidth(); 
       lastRippleHeight = stackPane.getHeight(); 
       rippleClip.setWidth(lastRippleWidth); 
       rippleClip.setHeight(lastRippleHeight); 
       /* 
       // try block because of possible null of Background, fills ... 
       try { 
        rippleClip.setArcHeight(stackPane.getBackground().getFills().get(0).getRadii().getTopLeftHorizontalRadius()); 
        rippleClip.setArcWidth(stackPane.getBackground().getFills().get(0).getRadii().getTopLeftHorizontalRadius()); 
        circleRipple.setClip(rippleClip); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       }*/ 

       circleRipple.setClip(rippleClip); 
       // Getting 45% of longest button's length, because we want edge of ripple effect always visible 
       double circleRippleRadius = Math.max(stackPane.getHeight(), stackPane.getWidth()) * 0.45; 
       final KeyValue keyValue = new KeyValue(circleRipple.radiusProperty(), circleRippleRadius, Interpolator.EASE_OUT); 
       final KeyFrame keyFrame = new KeyFrame(rippleDuration, keyValue); 
       scaleRippleTimeline.getKeyFrames().clear(); 
       scaleRippleTimeline.getKeyFrames().add(keyFrame); 
      } 
      parallelTransition.playFromStart(); 
     }); 
    } 

    public void setRippleColor(Color color) { 
     circleRipple.setFill(color); 
    } 
} 
+0

你能表現出一定的代碼?這可能很難做到;你可以使用'MenuItem.getGraphic()。getWidth()'來工作,但這取決於你設置的東西。 – 2015-02-24 16:32:35

回答

1

首先,你要聽的MenuItemparentPopupProperty變化。當你得到的父窗口的實例比註冊監聽器的skinPropertyContextMenu(parentPopup)。當你得到皮膚,那麼你可以得到MenuItemContainer這是Node相當於MenuItem,你可以聽widthPropertyheightProperty更改MenuItemContainer

注意:skinProperty更改是在屏幕上顯示ContextMenu之前觸發的。

自定義類擴展MenuItem類:

public class CstMenuItem extends MenuItem { 

    public CstMenuItem() { 
     // Create custom menu item listener. 
     new CstMenuItemListener(this); 
    } 

    /* 
    * Returns MenuItemContainer node associated with this menu item 
    * which can contain: 
    * 1. label node of type Label for displaying menu item text, 
    * 2. right node of type Label for displaying accelerator text, 
    *  or an arrow if it's a Menu, 
    * 3. graphic node for displaying menu item icon, and 
    * 4. left node for displaying either radio button or check box. 
    * 
    * This is basically rewritten impl_styleableGetNode() which 
    * should not be used since it's marked as deprecated. 
    */ 
    public ContextMenuContent.MenuItemContainer getAssociatedNode() { 
     ContextMenu contextMenu = getParentPopup(); 
     ContextMenuSkin skin = (ContextMenuSkin) contextMenu.getSkin(); 
     ContextMenuContent content = (ContextMenuContent) skin.getNode(); 
     // Items container contains MenuItemContainer nodes and Separator nodes. 
     Parent itemsContainer = content.getItemsContainer(); 
     List<Node> children = itemsContainer.getChildrenUnmodifiable(); 
     for (Node child : children) { 
      if (child instanceof ContextMenuContent.MenuItemContainer) { 
       ContextMenuContent.MenuItemContainer menuItemContainer = 
         (ContextMenuContent.MenuItemContainer) child; 
       if (menuItemContainer.getItem() == this) { 
        return menuItemContainer; 
       } 
      } 
     } 
     return null; 
    } 

} 

定製MenuItem監聽器類:

public class CstMenuItemListener implements ChangeListener { 

    private CstMenuItem menuItem; 
    private ContextMenu parentPopup; 
    private Region menuItemContainer; 

    public CstMenuItemListener(CstMenuItem menuItem) { 
     this.menuItem = menuItem; 
     menuItem.parentPopupProperty().addListener(this); 
    } 

    @Override 
    public void changed(ObservableValue observable, Object oldValue, Object newValue) { 
     if (observable == menuItem.parentPopupProperty()) { 
      parentPopup = (ContextMenu) newValue; 
      parentPopup.skinProperty().addListener(this); 
     } else if (observable == parentPopup.skinProperty()) { 
      menuItemContainer = menuItem.getAssociatedNode(); 
      menuItemContainer.widthProperty().addListener(this); 
      menuItemContainer.heightProperty().addListener(this); 
     } else if (observable == menuItemContainer.widthProperty()) { 
      System.out.println("width: " + (double) newValue); 
     } else if (observable == menuItemContainer.heightProperty()) { 
      System.out.println("height: " + (double) newValue); 
     } 
    } 
} 
相關問題