问题描述
在一个可调整大小的窗格中,该窗格显示一个imageView,我想选择多个矩形.
Within a resizable Pane that shows an imageView i'd like to select multiple rectangles.
为此,我创建了一个类SelectableImageViewPane.这两个类 ImageViewPane 和 RubberBandSelection 来自我是提交者的com.bitplan.javafx项目.
To do so I created a class SelectableImageViewPane. The two classes ImageViewPane and RubberBandSelection are from the com.bitplan.javafx Project where I am a committer.
调整窗口大小时,相关窗格的大小也进行了调整,如您从例如的生成的调试输出中看到的那样. SelectableImageViewPaneDemo 和以下示例屏幕截图.不幸的是,选择矩形未调整大小/位置.
When the window is resized the relevant panes are resized nicely as you can see from the debug output produced by e.g. SelectableImageViewPaneDemo and the following example screenshots. Unfortunately the selection rectangles are not resized / repositioned.
我如何确保子矩形-相对于周围的玻璃窗格调整大小/位置并与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 以记住矩形的相对边界在父对象中(无论如何,以后在我的应用程序中使用矩形时都需要使用它).如果父级的尺寸发生更改,此信息可用于在父级中正确布局矩形.
UpdateIn the meantime I modified RubberBandSelection to remember the relative bounds of the rectangles within the parent (which I'll need later anyway when using the rectangles in my application). This information can be used to properly layout the rectangles in the parent should the parent's dimension change.
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偏移量的问题.
The solution works better but there is still some issue probably involving the x/y offset within the parent.
更新了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);
}
}
推荐答案
有两种主要的方法可以使这项工作:
There are two main ingredients to make this working:
- 跟踪图像的大小-我修改了 ImageViewPane 相应.
- 确保与图像的更改同步地调整图像中所选矩形的大小.下面的RelativePane是一个帮助器类,其中的矩形(实际上是按钮)可以相对放置.
- Tracking the size of the image - I modified ImageViewPane accordingly.
- making sure the selected rectangles within the image are resized in sync with changes of the Image. The RelativePane below is a helper class in which the rectangles (buttons in fact) can be positioned relatively.
请注意,chriscamacho对原始代码进行了两项更改:
Please note that there are two changes to the original code by chriscamacho:
- 相对位置是从0.0到1.0,而不是使用百分比
- 代替使用translationX/Y layoutX/layoutY,另请参见
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;
}
}
这篇关于JavaFX调整子级相对于父级的大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!