JavaFX изменяет размер дочерних элементов относительно родительского - PullRequest
1 голос
/ 09 марта 2019

В пределах панели с изменяемым размером, которая показывает 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);
  }

}

Обновление Тем временем Я изменил 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);
    }

}

1 Ответ

0 голосов
/ 01 апреля 2019

Есть два основных ингредиента, чтобы это работало:

  1. Отслеживание размера изображения - я изменил ImageViewPane соответственно.
  2. , убедившись, что выбранразмеры прямоугольников в изображении изменяются синхронно с изменениями изображения.RelativePane ниже - это вспомогательный класс, в котором прямоугольники (фактически кнопки) могут быть расположены относительно.

Selection Resized

Обратите внимание, что в chriscamacho есть два изменения исходного кода:

RelativePane

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;
  }

}
...