如何实现JavaFX节点以便它可以返回计算的layoutBounds?

时间:2018-03-12 19:29:44

标签: javafx

我想实现一个JavaFX布局组件。最终目标是使该组件允许夹层状堆叠多个组件,然后剪切该堆栈并将生成的剪切节点放置到标准布局组件中。

当我实现组件时,一切都很顺利,但有一个例外:我无法正确返回组件的layoutBounds。我花了很多时间在谷歌搜索和研究,但没有找到(谷歌)也没有理解(在JavaFX源代码中)如何报告自制组件的布局界限。

一些细节:Node.getLayoutBounds()是final,因此无法覆盖。 layoutBoundsProperty是只读的,无法修改。在JavaFx Node实现中有许多辅助操作和看似有用的注释,但所有这些操作都引用了包私有功能,无法从外部访问。

所以问题是:如何实现一个组件,以便它可以计算自己的布局边界,并在getLayoutBounds()之类的调用中将其报告给它的上下文?

以下是可执行源代码。不确定它是否有帮助,但它会在正确的位置提出正确的问题。

package stackoverflow.demo;

import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class LayoutBoundsTest extends Application {
    public static void main(String[] args) {
        Application.launch(args);
    }

    /**
     * How to implement this component so that it can return
     * its own computed layout bounds?
     */
    public static class CenterPane extends StackPane
    {
        public CenterPane()
        {
        }

        @Override
        public boolean isResizable()
        {
            return false;
        }

        protected void addLayoutComponent( Node node )
        {
            Bounds bounds = node.getLayoutBounds();
            // How to set 'bounds' on this CenterPane so that
            // they are reported via CenterPane.getLayoutBounds()?
            getChildren().add( node );
        }
    }

    @Override
    public void start(Stage stage) {
        CenterPane centerPane = new CenterPane();
        centerPane.addLayoutComponent( new Circle( 30, Color.YELLOW ) );

        Node circle = new Circle( 30, Color.GREEN );

        VBox root = new VBox();
        root.getChildren().addAll(centerPane, circle);

        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Testing LayoutBounds");
        stage.show();

        // Both calls below should report the same layout bounds.
        System.out.println("b0=" + centerPane.getLayoutBounds());
        System.out.println("b2=" + circle.getLayoutBounds());
    }
}

1 个答案:

答案 0 :(得分:0)

我找到了JDK-8156089 : Provide official API so subclasses can override layout bounds computation

所以我似乎必须等待该功能请求的实现,这在Java 9中是不可用的。 同时解决方法如下:

package stackoverflow.demo;

import java.lang.reflect.Field;

import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class LayoutBoundsTest extends Application {
    public static void main(String[] args) {
        Application.launch(args);
    }

    /**
     * How to implement this component so that it can return
     * its own computed layout bounds?
     */
    public static class CenterPane extends StackPane
    {
        public CenterPane()
        {
        }

        @Override
        public boolean isResizable()
        {
            return false;
        }

        protected void addLayoutComponent( Node node )
        {
            Bounds bounds = node.getLayoutBounds();
            setBounds( bounds );
            getChildren().add( node );
        }

        /**
         * Do it the hard way (and don't try that at home).
         */
        private void setBounds( Bounds bounds )
        {
            try
            {
                // In Java 8 and 9 there's a private field in the
                // Region that is lazy-initialized.  We set this
                // manually.
                Field f = Region.class.getDeclaredField( "boundingBox" );
                f.setAccessible( true );
                f.set( this, bounds );
            }
            catch ( Exception e )
            {
                throw new RuntimeException( "No good.", e );
            }
        }
    }

    @Override
    public void start(Stage stage) {
        CenterPane centerPane = new CenterPane();
        centerPane.addLayoutComponent( new Circle( 30, Color.YELLOW ) );

        Node circle = new Circle( 30, Color.GREEN );

        VBox root = new VBox();
        root.getChildren().addAll(centerPane, circle);

        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Testing LayoutBounds");
        stage.show();

        // Both calls below should report the same layout bounds.
        System.out.println("b0=" + centerPane.getLayoutBounds());
        System.out.println("b2=" + circle.getLayoutBounds());
    }
}