如何在JavaFX中获取Menu或MenuItem宽度?

时间:2015-02-24 15:02:09

标签: javafx-8 menuitem

我正在扩展默认的MenuMenuItem类,以便为其添加一些动画效果。

问题在于我需要知道MenuMenuItem我正在处理的宽度和高度。这些课程不会延伸NodeRegion,因此没有公开的方法可以获得它们的大小。大小由MenuItem内的文本大小加上相应​​的默认填充组成,我可以计算文本占用的空间,但我无法得到MenuItem的填充量。

有一个名为impl_styleableGetNode()的方法返回Node,但它总是为我返回null。

无论如何要获得尺寸? MenuBar似乎也没有为此提供任何有用的方法。

修改

以下是我的课程,我试图在Menu课程中实施this素材设计按钮。基本上我使用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);
    }
}

1 个答案:

答案 0 :(得分:1)

首先,您必须收听parentPopupProperty的{​​{1}}更改。当您获得父弹出窗口的实例而不是MenuItem skinProperty(parentPopup)的寄存器监听器时。获得皮肤后,您可以获得ContextMenu等同于MenuItemContainer的{​​{1}},并且您可以收听NodeMenuItem widthProperty次更改}。

注意:heightProperty更改在屏幕上显示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;
    }

}