简体   繁体   中英

How to get the position of a Label object inside a ScrollPane in JavaFX 8

I have some labels contained inside of a VBox, the VBox is then put into a Scroll Pane. I have the Scroll Pane auto-scrolling by calling setVvalue() on the Scroll Pane. So what I want is as a Label object starts to get close to either the top edge or bottom edge of the Scroll Pane, it should start to fade out (reduce its opacity) as it moves out of view in the Scroll Pane. I am struggling to figure out how to implement this logic, is there a some way to figure out where an object is inside a Scroll Pane? Or is this going to be more complicated. Any help would be appreciated!

I did some research on how this can be achieved and came up with the following:

You can add a ChangeListener to the vvalueProperty of the scrollPane to fire each time the scrollPane scrolls. From here we need to check if any of the Labels are on the top or bottom edges of the view. I came across this article which provided a method to get a child element's visible bounds.

With some trial and error I found that the labels closest to the edge's minY and maxY values were approaching zero as they come closer to the edges. By using this, their opacity can be changed in relation to how close they are from the edge. I set a threshold for when the opacity should be changed, but you can specify constant values as well. Here is an image of what I ended up with:

在此处输入图片说明

And here is all the code:

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

Controller:

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

And 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>

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM