简体   繁体   中英

How can I set another value in property listener?

I want to speed-up my ScrollPane scrolling. I need something like:

scrollPane.vvalueProperty().addListener((observable, oldValue, newValue) -> {
            scrollPane.setVvalue(oldValue.doubleValue() + (newValue.doubleValue() - oldValue.doubleValue()) * 2);
        });

but without stackowerflow exections and working..

May be there is a way to consume this like an event?

PS BTW, why does setOnScroll() fire only when scrolling reaches max (top) or min (bot) position?

I don't really recommend modifying a property while it is already changing, but if you want to do it you need to set a flag to suppress recursive calls. Here's an example:

import java.util.Random;

import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;

public class ModifyScrollSpeed extends Application {

    @Override
    public void start(Stage primaryStage) {
        ScrollPane scrollPane = new ScrollPane(createContent());
        DoublingListener.register(scrollPane.vvalueProperty());

        Scene scene = new Scene(scrollPane, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    private static class DoublingListener implements ChangeListener<Number> {

        private boolean doubling ;

        private Property<Number> target ;

        private DoublingListener(Property<Number> target) {
            this.target = target ;
        }

        public static void register(Property<Number> target) {
            target.addListener(new DoublingListener(target));
        }

        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            if (! doubling) {
                doubling = true ;
                target.setValue(oldValue.doubleValue() + 2 * (newValue.doubleValue() - oldValue.doubleValue()));
            }
            doubling = false ;
        }

    }

    private Node createContent() {
        TilePane tilePane = new TilePane();
        Random rng = new Random();
        for (int i = 0; i < 200; i++) {
            Region region = new Region();
            region.setMinSize(40,  40);
            region.setPrefSize(40, 40);
            region.setMaxSize(40, 40);
            region.setStyle(String.format("-fx-background-color: #%02x%02x%02x;", 
                    rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)));
            tilePane.getChildren().add(region);
        }
        return tilePane ;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

I don't actually see any other way to change the increment amounts for a scroll pane. Note that ScrollBar has API for changing the increment amounts via its unitIncrement and blockIncrement properties, however ScrollPane does not have equivalent properties.

There is a comment in the source code for ScrollPane which says

/*
 * TODO The unit increment and block increment variables have been
 * removed from the public API. These are intended to be mapped to
 * the corresponding variables of the scrollbars. However, the problem
 * is that they are specified in terms of the logical corrdinate space
 * of the ScrollPane (that is, [hmin..hmax] by [vmin..vmax]. This is
 * incorrect. Scrolling is a user action and should properly be based
 * on how much of the content is visible, not on some abstract
 * coordinate space. At some later date we may add a finer-grained
 * API to allow applications to control this. Meanwhile, the skin should
 * set unit and block increments for the scroll bars to do something
 * reasonable based on the viewport size, e.g. the block increment
 * should scroll 90% of the pixel size of the viewport, and the unit
 * increment should scroll 10% of the pixel size of the viewport.
 */

The current skin for the scroll pane hard codes the unit and block increments for its scroll bars (in the updateHorizontalSB and updateVerticalSB methods) in the manner described in this comment (ie 10% and 90% of the visible amount), so I see no real way to get at these. In Java 9 the skin class will become a public class, and so at a minimum you could subclass it and modify this behavior.

You can try something like this -

private boolean scrolllChanging = false; 

private void myScroll(ObservableDoubleValue observable, Double oldValue, Double newValue) {
    if (!scrollChanging) {
        try {
            scrollChanging = true; 
            // Insert logic here. Any subsequent changes won't reach here until `scrollChanging` is set to false again. 
        } finally {
            scrollChanging = false; 
        }
    }
}

scrollPane.vvalueProperty().addListener(this::myScroll);

Forgive any minor type errors, I have not compiled this.

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