代码之家  ›  专栏  ›  技术社区  ›  Wolfgang Fahl

JavaFX相对于父级调整子级大小

  •  1
  • Wolfgang Fahl  · 技术社区  · 7 年前

    在显示imageView的可调整大小窗格中,我要选择多个矩形。

    为此,我创建了一个类SelectableImageViewPane。两个班 ImageViewPane RubberBandSelection 来自com.bitplan.javafx项目,我是其中的提交者。

    SelectableImageViewPaneDemo 以及下面的屏幕截图示例。不幸的是,选择矩形没有调整大小/重新定位。

    enter image description here enter image description here

    如何确保子矩形相对于周围的窗格调整大小/重新定位,并与ImageViewPane/ImageView同步?

    package com.bitplan.javafx;
    
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    import javafx.geometry.Bounds;
    import javafx.geometry.Pos;
    import javafx.scene.Node;
    import javafx.scene.image.ImageView;
    import javafx.scene.layout.AnchorPane;
    import javafx.scene.layout.Pane;
    import javafx.scene.layout.StackPane;
    
    /**
     * an ImageViewPane with a RubberBandSelection that properly resizes the rectangles
     * @author wf
     *
     */
    public class SelectableImageViewPane extends StackPane {
      protected static Logger LOGGER = Logger.getLogger("com.bitplan.javafx");
      public static boolean debug=true;
      private RubberBandSelection selection;
      private ImageViewPane imageViewPane;
      private Pane glassPane;
    
      /**
       * get my selection
       * @return
       */
      public RubberBandSelection getSelection() {
        return selection;
      }
    
      /**
       * show the bound of the given node with the given title
       * @param title
       * @param n
       */
      public void showBounds(String title,Node n) {
        if (debug) {
          Bounds b = n.getLayoutBounds();
          LOGGER.log(Level.INFO,String.format("%s: min %.0f,%.0f max %.0f,%.0f",title,b.getMinX(),b.getMinY(),b.getMaxX(),b.getMaxY()));        
        }
      }
    
      @Override
      protected void layoutChildren() {
        super.layoutChildren();
        int index=1;
        if (debug) {
          showBounds("pane",this);
          showBounds("imageViewPane",imageViewPane);
          showBounds("glassPane",glassPane);
          showBounds("imageView",imageViewPane.imageViewProperty().get());
        }
        for (Node n:selection.selected) {
         showBounds(""+(index++),n);
        }
      }
    
      /**
       * create me
       * @param imageView
       */
      public SelectableImageViewPane(ImageView imageView) {
        imageViewPane=new ImageViewPane(imageView);
        getChildren().add(imageViewPane);
        StackPane.setAlignment(imageViewPane, Pos.CENTER);
        glassPane = new AnchorPane();
        glassPane.setStyle("-fx-background-color: rgba(0, 0, 0, 0.1);");
        getChildren().add(glassPane);
        //glassPane.prefWidthProperty().bind(imageViewPane.widthProperty());
        //glassPane.prefHeightProperty().bind(imageViewPane.heightProperty());
        selection = new RubberBandSelection(glassPane);
        selection.setSelectButton(true);
      }
    
    }
    

    更新 同时 I modified RubberBandSelection

    Bounds rB = s.relativeBounds;
                    double w = getWidth();
                    double h=getHeight();
                    double minX=rB.getMinX()*w;
                    double minY=rB.getMinY()*h;
                    double width=rB.getWidth()*w;
                    double height=rB.getHeight()*h;
                    layoutInArea(s.node, minX,minY,width,height, 0, HPos.LEFT,
                              VPos.TOP);
    

    该解决方案工作得更好,但仍有一些问题可能涉及父级中的x/y偏移。

    已更新SelectableImageViewPane

    package com.bitplan.javafx;
    
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    import com.bitplan.javafx.RubberBandSelection.Selection;
    
    import javafx.geometry.Bounds;
    import javafx.geometry.HPos;
    import javafx.geometry.Pos;
    import javafx.geometry.VPos;
    import javafx.scene.Node;
    import javafx.scene.image.ImageView;
    import javafx.scene.layout.AnchorPane;
    import javafx.scene.layout.Pane;
    import javafx.scene.layout.StackPane;
    
    /**
     * an ImageViewPane with a RubberBandSelection that properly resizes the
     * rectangles
     * 
     * @author wf
     *
     */
    public class SelectableImageViewPane extends StackPane {
        protected static Logger LOGGER = Logger.getLogger("com.bitplan.javafx");
        public static boolean debug = true;
        private RubberBandSelection selection;
        private ImageViewPane imageViewPane;
        private Pane glassPane;
    
        /**
         * get my selection
         * 
         * @return
         */
        public RubberBandSelection getSelection() {
            return selection;
        }
    
        /**
         * show the bound of the given node with the given title
         * 
         * @param title
         * @param n
         */
        public void showBounds(String title, Node n) {
            if (debug) {
                Bounds b = n.getLayoutBounds();
                LOGGER.log(Level.INFO, String.format("%s: min %.0f,%.0f max %.0f,%.0f", title, b.getMinX(), b.getMinY(),
                        b.getMaxX(), b.getMaxY()));
            }
        }
    
        @Override
        protected void layoutChildren() {
            super.layoutChildren();
            int index = 1;
            if (debug) {
                showBounds("pane", this);
                showBounds("imageViewPane", imageViewPane);
                showBounds("glassPane", glassPane);
                showBounds("imageView", imageViewPane.imageViewProperty().get());
            }
            for (Selection s : selection.selected.values()) {
    
                if (debug) {
                    showBounds("" + (index++), s.node);
                    LOGGER.log(Level.INFO, s.asPercent());
                }
                Bounds rB = s.relativeBounds;
                double w = getWidth();
                double h=getHeight();
                double minX=rB.getMinX()*w;
                double minY=rB.getMinY()*h;
                double width=rB.getWidth()*w;
                double height=rB.getHeight()*h;
                layoutInArea(s.node, minX,minY,width,height, 0, HPos.LEFT,
                          VPos.TOP);
            }
        }
    
        /**
         * create me
         * 
         * @param imageView
         */
        public SelectableImageViewPane(ImageView imageView) {
            imageViewPane = new ImageViewPane(imageView);
            getChildren().add(imageViewPane);
            StackPane.setAlignment(imageViewPane, Pos.CENTER);
            glassPane = new AnchorPane();
            glassPane.setStyle("-fx-background-color: rgba(0, 0, 0, 0.1);");
            getChildren().add(glassPane);
            // glassPane.prefWidthProperty().bind(imageViewPane.widthProperty());
            // glassPane.prefHeightProperty().bind(imageViewPane.heightProperty());
            selection = new RubberBandSelection(glassPane);
            selection.setSelectButton(true);
        }
    
    }
    
    0 回复  |  直到 7 年前
        1
  •  0
  •   Wolfgang Fahl    7 年前

    有两个主要因素可以使其发挥作用:

    1. 跟踪图像大小-我修改了 ImageViewPane
    2. 确保图像中选定的矩形的大小与图像的更改同步。下面的RelativePane是一个助手类,其中矩形(实际上是按钮)可以相对定位。

    Selection Resized

    请注意,chriscamacho对原始代码有两处更改:

    相对论烷

    package com.bitplan.javafx;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    import javafx.beans.Observable;
    import javafx.geometry.BoundingBox;
    import javafx.geometry.Bounds;
    import javafx.scene.Parent;
    import javafx.scene.control.Control;
    import javafx.scene.layout.Pane;
    
    /**
     * We want a Pane that will automatically resize its components The components
     * width height and position are all expressed as a relative of the width and
     * height of the Pane from 0.0 to 1.0 As the Pane resizes the position and sizes
     * will all remain relative to the Pane's new width and height. Aspect ratio is
     * not respected
     * 
     * see https://gist.github.com/chriscamacho/4f8b2e3e8f8340278b7c
     * 
     * @author chriscamacho
     * @author wf - modified to relative instead of percent
     *
     */
    public class RelativePane extends Pane implements RelativeSizer {
      protected static Logger LOGGER = Logger.getLogger("com.bitplan.javafx");
      public static boolean debug=false;
    
      /**
       * helper class to keep track of relative position
       */
      public class ControlBundle {
        public double rx, ry, rw, rh;
        public Control control;
    
        /**
         * construct me for the given control c and the given relative positions
         * 
         * @param c
         * @param rX
         * @param rY
         * @param rW
         * @param rH
         */
        ControlBundle(Control c, double rX, double rY, double rW, double rH) {
          control = c;
          rx = rX;
          ry = rY;
          rw = rW;
          rh = rH;
        }
      }
    
      Map<Control, ControlBundle> controls = new HashMap<Control, ControlBundle>();
    
      /**
       * create a relative Pane
       */
      public RelativePane() {
        super();
        widthProperty().addListener(o -> sizeListener(o));
        heightProperty().addListener(o -> sizeListener(o));
      }
    
      /**
       * show the bounds of the given node with the given title
       * 
       * @param title
       * @param b
       */
      public static void showBoundsPercent(String title, Bounds b) {
        LOGGER.log(Level.INFO,
            String.format("%s: min %.0f%%,%.0f%% max %.0f%%,%.0f%%", title,
                b.getMinX() * 100.0, b.getMinY() * 100.0, b.getMaxX() * 100.0,
                b.getMaxY() * 100.0));
      }
    
      /**
       * show the given bounds with the given title
       * 
       * @param title
       * @param b
       */
      public static void showBounds(String title, Bounds b) {
        LOGGER.log(Level.INFO, String.format("%s: min %.0f,%.0f max %.0f,%.0f",
            title, b.getMinX(), b.getMinY(), b.getMaxX(), b.getMaxY()));
      }
    
    
      /**
       * listen to a change of the given observable
       * @param o
       */
      void sizeListener(Observable o) {
        int index=0;
        for (ControlBundle cb : controls.values()) {
          index++;
          double w = getWidth() * cb.rw;
          double h = getHeight() * cb.rh;
          double x = getWidth() * cb.rx;
          double y = getHeight() * cb.ry;
          if (debug) {
            showBoundsPercent("r"+index,new BoundingBox(cb.rx,cb.ry,cb.rw,cb.rh));
            showBounds("a"+index,new BoundingBox(x,y,w,h));
          }
    
          cb.control.setPrefWidth(w);
          cb.control.setMinWidth(w);
          cb.control.setMaxWidth(w);
    
          cb.control.setPrefHeight(h);
          cb.control.setMinHeight(h);
          cb.control.setMaxHeight(h);
    
          cb.control.setLayoutX(x);
          cb.control.setLayoutY(y);
    
        }
    
      }
    
      /**
       * add a control with the given percentages
       * 
       * @param ctrl
       * @param x
       * @param y
       * @param w
       * @param h
       */
      public void addControl(Control ctrl, double x, double y, double w, double h) {
        this.getChildren().add(ctrl);
        controls.put(ctrl, new ControlBundle(ctrl, x, y, w, h));
      }
    
      public void removeControl(Control ctrl) {
        this.getChildren().remove(ctrl);
        controls.remove(ctrl);
      }
    
      @Override
      public Parent getSizer() {
        return this;
      }
    
    }