如何在 JavaFX 8 中获取 ScrollPane 中 Label 对象的位置
How to get the position of a Label object inside a ScrollPane in JavaFX 8
我在 VBox 中包含一些标签,然后将 VBox 放入滚动窗格中。我通过在滚动窗格上调用 setVvalue() 使滚动窗格自动滚动。所以我想要的是当 Label 对象开始靠近滚动窗格的顶部边缘或底部边缘时,它应该开始淡出(降低其不透明度),因为它在滚动窗格中移出视图。我正在努力弄清楚如何实现这个逻辑,是否有某种方法可以弄清楚对象在滚动窗格中的位置?或者这会变得更复杂。如有任何帮助,我们将不胜感激!
我对如何实现这一目标进行了一些研究,并得出以下结论:
您可以将 ChangeListener
添加到 scrollPane
的 vvalueProperty
以在每次 scrollPane
滚动时触发。从这里我们需要检查是否有任何标签位于视图的顶部或底部边缘。我遇到了 this article,它提供了一种获取子元素可见边界的方法。
经过反复试验,我发现最靠近边缘的 minY
和 maxY
值的标签在靠近边缘时接近于零。通过使用它,它们的不透明度可以根据它们与边缘的距离来改变。我为何时更改不透明度设置了一个阈值,但您也可以指定常量值。这是我最终得到的图片:
这里是所有代码:
主要class:
public class Main extends Application{
public static Stage primaryStage;
@Override
public void start(Stage primaryStage) throws Exception {
Main.primaryStage=primaryStage;
AnchorPane page = (AnchorPane) FXMLLoader.load(MainController.class.getResource("mainFXML.fxml"));
Scene scene = new Scene(page);
primaryStage.setTitle("Fade example");
primaryStage.setScene(scene);
// Stage positioning
Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
// stage positioning
primaryStage.setX(primaryScreenBounds.getMaxX()*5/8);
primaryStage.setY(primaryScreenBounds.getMinY() + page.getPrefHeight()*2/4);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
控制器:
public class MainController {
private static final double FADE_THRESHOLD = 80;
@FXML
private ResourceBundle resources;
@FXML
private URL location;
@FXML
private ScrollPane scrollPane;
@FXML
private AnchorPane rootPane;
@FXML
private VBox vBox;
@FXML
void initialize() {
assert vBox != null : "fx:id=\"vBox\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert scrollPane != null : "fx:id=\"scrollPane\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'MainFXML.fxml'.";
ArrayList<Label> labels = new ArrayList<>();
for (int i = 0; i < 300; i++) {
Label l = new Label("Label "+i);
l.setBackground(new Background(new BackgroundFill(Color.AQUA, CornerRadii.EMPTY, Insets.EMPTY)));
labels.add(l);
}
labels.forEach( e-> vBox.getChildren().add(e));
scrollPane.vvalueProperty().addListener(new ChangeListener<Number>()
{
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue)
{
//System.out.println("bounds: "+getVisibleBounds(labels.get(51)));
for(Label l: labels) {
Bounds bounds = getVisibleBounds(l);
double minY = bounds.getMinY();
double maxY = bounds.getMaxY();
if(Math.abs(minY) < FADE_THRESHOLD ) {
l.setOpacity(1-(FADE_THRESHOLD-Math.abs(minY))/FADE_THRESHOLD);
}else if(Math.abs(maxY) < FADE_THRESHOLD) {
l.setOpacity(1-(FADE_THRESHOLD-Math.abs(maxY))/FADE_THRESHOLD);
}else {
l.setOpacity(1);
}
}
}
});
}
public static Bounds getVisibleBounds(Node aNode)
{
// If node not visible, return empty bounds
if(!aNode.isVisible()) return new BoundingBox(0,0,-1,-1);
// If node has clip, return clip bounds in node coords
if(aNode.getClip()!=null) return aNode.getClip().getBoundsInParent();
// If node has parent, get parent visible bounds in node coords
Bounds bounds = aNode.getParent()!=null? getVisibleBounds(aNode.getParent()) : null;
if(bounds!=null && !bounds.isEmpty()) bounds = aNode.parentToLocal(bounds);
return bounds;
}
}
和MainFXML.fxml:
<AnchorPane fx:id="rootPane" prefHeight="408.0" prefWidth="330.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<ScrollPane fx:id="scrollPane" layoutX="28.0" layoutY="14.0" prefHeight="372.0" prefWidth="283.0">
<content>
<VBox fx:id="vBox" prefWidth="281.0" />
</content>
</ScrollPane>
</children>
</AnchorPane>
希望这对您有所帮助。
我在 VBox 中包含一些标签,然后将 VBox 放入滚动窗格中。我通过在滚动窗格上调用 setVvalue() 使滚动窗格自动滚动。所以我想要的是当 Label 对象开始靠近滚动窗格的顶部边缘或底部边缘时,它应该开始淡出(降低其不透明度),因为它在滚动窗格中移出视图。我正在努力弄清楚如何实现这个逻辑,是否有某种方法可以弄清楚对象在滚动窗格中的位置?或者这会变得更复杂。如有任何帮助,我们将不胜感激!
我对如何实现这一目标进行了一些研究,并得出以下结论:
您可以将 ChangeListener
添加到 scrollPane
的 vvalueProperty
以在每次 scrollPane
滚动时触发。从这里我们需要检查是否有任何标签位于视图的顶部或底部边缘。我遇到了 this article,它提供了一种获取子元素可见边界的方法。
经过反复试验,我发现最靠近边缘的 minY
和 maxY
值的标签在靠近边缘时接近于零。通过使用它,它们的不透明度可以根据它们与边缘的距离来改变。我为何时更改不透明度设置了一个阈值,但您也可以指定常量值。这是我最终得到的图片:
这里是所有代码:
主要class:
public class Main extends Application{
public static Stage primaryStage;
@Override
public void start(Stage primaryStage) throws Exception {
Main.primaryStage=primaryStage;
AnchorPane page = (AnchorPane) FXMLLoader.load(MainController.class.getResource("mainFXML.fxml"));
Scene scene = new Scene(page);
primaryStage.setTitle("Fade example");
primaryStage.setScene(scene);
// Stage positioning
Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
// stage positioning
primaryStage.setX(primaryScreenBounds.getMaxX()*5/8);
primaryStage.setY(primaryScreenBounds.getMinY() + page.getPrefHeight()*2/4);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
控制器:
public class MainController {
private static final double FADE_THRESHOLD = 80;
@FXML
private ResourceBundle resources;
@FXML
private URL location;
@FXML
private ScrollPane scrollPane;
@FXML
private AnchorPane rootPane;
@FXML
private VBox vBox;
@FXML
void initialize() {
assert vBox != null : "fx:id=\"vBox\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert scrollPane != null : "fx:id=\"scrollPane\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'MainFXML.fxml'.";
ArrayList<Label> labels = new ArrayList<>();
for (int i = 0; i < 300; i++) {
Label l = new Label("Label "+i);
l.setBackground(new Background(new BackgroundFill(Color.AQUA, CornerRadii.EMPTY, Insets.EMPTY)));
labels.add(l);
}
labels.forEach( e-> vBox.getChildren().add(e));
scrollPane.vvalueProperty().addListener(new ChangeListener<Number>()
{
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue)
{
//System.out.println("bounds: "+getVisibleBounds(labels.get(51)));
for(Label l: labels) {
Bounds bounds = getVisibleBounds(l);
double minY = bounds.getMinY();
double maxY = bounds.getMaxY();
if(Math.abs(minY) < FADE_THRESHOLD ) {
l.setOpacity(1-(FADE_THRESHOLD-Math.abs(minY))/FADE_THRESHOLD);
}else if(Math.abs(maxY) < FADE_THRESHOLD) {
l.setOpacity(1-(FADE_THRESHOLD-Math.abs(maxY))/FADE_THRESHOLD);
}else {
l.setOpacity(1);
}
}
}
});
}
public static Bounds getVisibleBounds(Node aNode)
{
// If node not visible, return empty bounds
if(!aNode.isVisible()) return new BoundingBox(0,0,-1,-1);
// If node has clip, return clip bounds in node coords
if(aNode.getClip()!=null) return aNode.getClip().getBoundsInParent();
// If node has parent, get parent visible bounds in node coords
Bounds bounds = aNode.getParent()!=null? getVisibleBounds(aNode.getParent()) : null;
if(bounds!=null && !bounds.isEmpty()) bounds = aNode.parentToLocal(bounds);
return bounds;
}
}
和MainFXML.fxml:
<AnchorPane fx:id="rootPane" prefHeight="408.0" prefWidth="330.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<ScrollPane fx:id="scrollPane" layoutX="28.0" layoutY="14.0" prefHeight="372.0" prefWidth="283.0">
<content>
<VBox fx:id="vBox" prefWidth="281.0" />
</content>
</ScrollPane>
</children>
</AnchorPane>
希望这对您有所帮助。