简体   繁体   English

ScrollPane 不刷新其内容

[英]ScrollPane is not refreshing its contents

RESUME恢复

Good day StackOverflow community.美好的一天 StackOverflow 社区。

I've been trying for some time to develop a program that enables users to put objects in an area, allowing this area to be moved by the mouse.一段时间以来,我一直在尝试开发一个程序,使用户能够将对象放在一个区域中,从而允许鼠标移动该区域。 For this type of program, I decided to use a ScrollPane, because the user can add various contents in the area which I call the canvas.对于这种类型的程序,我决定使用 ScrollPane,因为用户可以在我称为画布的区域中添加各种内容。 For some reason, something strange is happening in my program.出于某种原因,我的程序中发生了一些奇怪的事情。

EXPLANATION OF PROGRAM程序说明

What I basically did was create a group of objects, and define this group as the ScrollPane content.我基本上做的是创建一组对象,并将该组定义为 ScrollPane 内容。 Within the group, there is a Rectangle object that was added to serve as canvas boundaries.在该组中,添加了一个 Rectangle 对象作为画布边界。 This object has larger dimensions (such as 1500 x 1000, for example), and is used in calculations that prevent nodes from moving beyond its limits.此对象具有更大的尺寸(例如 1500 x 1000),并用于防止节点移动超出其限制的计算。 This is just the logical behind the existing large rectangle in my program, but in reality, there is no Node object with the mouse movement.这只是我程序中现有大矩形背后的逻辑,但实际上,鼠标移动时没有 Node 对象。 What exists is the random distribution of Shape objects by the rectangle area.存在的是矩形区域的 Shape 对象的随机分布。

For ScrollPane has its scrollbars moved, I use the setHvalue setVvalue methods.对于 ScrollPane 移动了其滚动条,我使用setHvalue setVvalue方法。 Unfortunately for my purposes, this method does not change the position of the ScrollPane's viewport with pixel values​​, but values ​​that are in a range between 0f and 1f.不幸的是,对于我的目的,此方法不会使用像素值更改 ScrollPane 视口的位置,而是更改 0f 和 1f 之间范围内的值。 So I can move the viewport with the mouse, I used a equation known as Rule of 3 (here in my Country, as I know), which we equate values ​​and cross multiply.所以我可以用鼠标移动视口,我使用了一个称为 3 法则的方程(在我的国家,据我所知),我们将值相等并交叉乘法。

For example, say I want to move the viewport of the ScrollPane with the mouse horizontally, and that my canvas area has a width of 2000 pixels.例如,假设我想用鼠标水平移动 ScrollPane 的视口,并且我的画布区域的宽度为 2000 像素。 Finding how far (in pixels) the mouse was dragged from one point to another, I need to know how this value represents in a range 0f to 1f.找到鼠标从一个点拖到另一个点的距离(以像素为单位),我需要知道这个值在 0f 到 1f 的范围内如何表示。 Suppose I have dragged the mouse in 3 pixels, I could find the representation of the 0f to 1f with the following comparison:假设我将鼠标拖动了 3 个像素,我可以通过以下比较找到 0f 到 1f 的表示:

2000 px ---- 1f
   3 px ---- xf

Multiplying crossed, I'll get the following result:乘以交叉,我会得到以下结果:

xf = 3 / 2000
xf = 0.0015

Note : I believe you all know that.:相信大家都知道。 I'm not teaching math to anyone, just want to explain the logic of my problem.我不是在教任何人数学,只是想解释我的问题的逻辑。

SOURCE CODE源代码

Here is my program class:这是我的程序类:

import testes.util.TestesUtil;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;

public class ScrollTest4 extends Application
{
// #########################################################################################################
//                                                                                                      MAIN
// #########################################################################################################

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

// #########################################################################################################
//                                                                                                INSTÂNCIAS
// #########################################################################################################

// OUTSIDE

private BorderPane root;
private Button but_moreH;
private Button but_lessH;
private Button but_moreV;
private Button but_lessV;

// LOG

private VBox vbox_south;
private Label lab_hValue;
private Label lab_vValue;
private Label lab_viewport;

// INSIDE

private Rectangle rec_canvas;
private ScrollPane scroll;
private Group grp_objects;

// MOUSE

private double mouse_x = 0;
private double mouse_y = 0;

// MISC

private AnimationTimer timer;

// EDITED - 08/02/2014
private boolean moreH = false;
private boolean moreV = false;  // Purposely unused.
private boolean lessH = false;
private boolean lessV = false;  // Purposely unused.

// #########################################################################################################
//                                                                                                 INÍCIO FX
// #########################################################################################################

@Override public void start(Stage estagio) throws Exception 
{
    this.iniFX();
    this.confFX();
    this.adFX();
    this.evFX();

    Scene cenario = new Scene(this.root , 640 , 480);

    estagio.setScene(cenario);
    estagio.setTitle("Programa JavaFX");
    estagio.show();
}

protected void iniFX()
{
    // OUTSIDE

    this.root = new BorderPane();
    this.but_moreH = new Button();
    this.but_lessH = new Button();
    this.but_moreV = new Button();
    this.but_lessV = new Button();

    // LOG

    this.vbox_south = new VBox();
    this.lab_hValue = new Label();
    this.lab_vValue = new Label();
    this.lab_viewport = new Label();

    // INSIDE

    this.scroll = new ScrollPane();
    this.grp_objects = new Group();
    this.rec_canvas = new Rectangle();

    // MISC

    this.timer = new AnimationTimer()
    {
        @Override public void handle(long now) 
        {
            // EDITED - 08/02/2014
            if(but_moreH.isArmed() || moreH)
            {
                // scroll.hvalueProperty().set(scroll.hvalueProperty().get() + 0.003f);
                scroll.setHvalue(scroll.getHvalue() + 0.003f);
            }
            // EDITED - 08/02/2014
            if(but_lessH.isArmed() || lessH)
            {
                // scroll.hvalueProperty().set(scroll.hvalueProperty().get() - 0.003f);
                scroll.setHvalue(scroll.getHvalue() - 0.003f);
            }
            if(but_moreV.isArmed())
            {
                scroll.setVvalue(scroll.getVvalue() + 0.003f);
            }
            if(but_lessV.isArmed())
            {
                scroll.setVvalue(scroll.getVvalue() - 0.003f);
            }
        }
    };
    this.timer.start();
}

protected void confFX() 
{
    // OUTSIDE

    this.but_moreH.setText("More H");
    this.but_moreH.setMaxHeight(Double.MAX_VALUE);

    this.but_lessH.setText("Less H");
    this.but_lessH.setMaxHeight(Double.MAX_VALUE);

    this.but_moreV.setText("More V");
    this.but_moreV.setMaxWidth(Double.MAX_VALUE);

    this.but_lessV.setText("Less V");
    this.but_lessV.setMaxWidth(Double.MAX_VALUE);

    // LOG

    this.updateHvalue();
    this.updateVvalue();
    this.updateViewport();

    // INSIDE

    this.rec_canvas.setWidth(1200);
    this.rec_canvas.setHeight(1000);
    this.rec_canvas.setFill(Color.INDIANRED);
    this.rec_canvas.setStroke(Color.RED);
    this.rec_canvas.setStrokeType(StrokeType.INSIDE);
    this.rec_canvas.setStrokeWidth(1);
}

protected void adFX() 
{
    // LOG

    this.vbox_south.getChildren().add(this.but_moreV);
    this.vbox_south.getChildren().addAll(this.lab_hValue , this.lab_vValue , this.lab_viewport);

    // OUTSIDE

    this.root.setCenter(this.scroll);
    this.root.setTop(this.but_lessV);
    this.root.setBottom(this.vbox_south);
    this.root.setRight(this.but_moreH);
    this.root.setLeft(this.but_lessH);

    // INSIDE

    this.grp_objects.getChildren().add(this.rec_canvas);
    this.scroll.setContent(this.grp_objects);

    // MISC

    StrokeType[] strokes = {StrokeType.CENTERED , StrokeType.INSIDE , StrokeType.OUTSIDE};

    for(int cont = 0 ; cont < 20 ; cont++)
    {
        Rectangle node = new Rectangle(Math.random() * 100 + 50 , Math.random() * 100 + 50);
        node.setFill(TestesUtil.getCorAleatoria(false));
        node.setStroke(TestesUtil.getCorAleatoria(false));
        node.setStrokeType(strokes[(int) (Math.random() * 2)]);
        node.setStrokeWidth(Math.random() * 9 + 1);
        node.setRotate(Math.random() * 360);
        node.setMouseTransparent(true);

        // EDITED - 08/02/2014
        TestsUtil.putRandomlyIn(
                node , 
                rec_canvas.getBoundsInParent().getMinY() ,
                rec_canvas.getBoundsInParent().getMinY() + rec_canvas.getBoundsInParent().getHeight() , 
                rec_canvas.getBoundsInParent().getMinX() + rec_canvas.getBoundsInParent().getWidth() , 
                rec_canvas.getBoundsInParent().getMinX() );

        this.grp_objects.getChildren().add(node);
    }
}

protected void evFX() 
{
    // ##########################
    //     SCROLL PROPERTIES
    // ##########################

    this.scroll.hvalueProperty().addListener(new ChangeListener<Number>()
    {
        @Override public void changed(ObservableValue<? extends Number> observable,Number oldValue, Number newValue) 
        {
            updateHvalue();
            updateViewport();
        }
    });

    this.scroll.vvalueProperty().addListener(new ChangeListener<Number>()
    {
        @Override public void changed(ObservableValue<? extends Number> observable,Number oldValue, Number newValue) 
        {
            updateVvalue();
            updateViewport();
        }
    });

    this.scroll.setOnKeyPressed(new EventHandler<KeyEvent>()
    {
        @Override public void handle(KeyEvent e) 
        {
            if(e.getCode() == KeyCode.RIGHT)
            {
                moreH = true;
            }
            else if(e.getCode() == KeyCode.LEFT)
            {
                lessH = true;
            }
        }
    });

    this.scroll.setOnKeyReleased(new EventHandler<KeyEvent>()
    {
        @Override public void handle(KeyEvent e) 
        {
            if(e.getCode() == KeyCode.RIGHT)
            {
                moreH = false;
            }
            else if(e.getCode() == KeyCode.LEFT)
            {
                lessH = false;
            }
        }
    });

    // ##########################
    //          CANVAS
    // ##########################

    this.rec_canvas.setOnMousePressed(new EventHandler<MouseEvent>() 
    {
        @Override public void handle(MouseEvent e) 
        {
            // The XY distance from the upper left corner of the canvas.
            mouse_x = e.getX();
            mouse_y = e.getY();
        }
    });

    this.rec_canvas.setOnMouseDragged(new EventHandler<MouseEvent>() 
    {
        @Override public void handle(MouseEvent e) 
        {
            // ##########################
            //           PIXELS 
            // ##########################

            // The distance between mouse movements (drag events).
            double xPixelsMoved = e.getX() - mouse_x;
            // double yPixelsMoved = e.getY() - mouse_y;

            // ##########################
            //           TO 1F
            // ##########################

            double h_of_1f = xPixelsMoved / rec_canvas.getBoundsInParent().getWidth();
            double h_of_1f_inverted = h_of_1f * -1;

            double currentH = scroll.getHvalue();
            scroll.setHvalue(currentH + h_of_1f);

            // scroll.hvalueProperty().set(scroll.getHvalue() + h_de_x);
            // scroll.vvalueProperty().set(scroll.getVvalue() + v_de_y);

            // ##########################
            //           DEBUG
            // ##########################

            System.out.printf("xPixelsMoved: %f , h_of_1f: %f , h_of_1f_inverted: %f %n", 
                    xPixelsMoved , h_of_1f , h_of_1f_inverted);

            // ##########################
            //        UPDATE FROM 
            //       EVENT TO EVENT
            // ##########################

            // Writes last mouse position to update on new motion event.
            mouse_x = e.getX();
            mouse_y = e.getY();
        }
    });
}

// #########################################################################################################
//                                                                                                     MISC.
// #########################################################################################################

protected void updateViewport()
{
    Bounds vport = this.scroll.getViewportBounds();
    this.lab_viewport.setText(String.format("Viewport - [X: %f , Y: %f , W: %f , H: %f]", 
            vport.getMinX() , vport.getMinY() , vport.getWidth() , vport.getHeight() ));
}

protected void updateHvalue()
{
    this.lab_hValue.setText("H value: " + this.scroll.getHvalue() );
}

protected void updateVvalue()
{
    this.lab_vValue.setText("V value: " + this.scroll.getVvalue() );
}

} }

THE PROBLEM问题

Clicking the mouse button on the canvas area and drag it, you can see that the program moves the ScrollPane viewport horizontally.在画布区域单击鼠标按钮并拖动它,您可以看到程序水平移动了 ScrollPane 视口。 The program seems to work perfectly (or not).该程序似乎运行良好(或不运行)。 However, something goes wrong at the time when the mouse is dragged sometimes abruptly (...or not!).但是,有时会突然(...或不会!)拖动鼠标时出现问题。 At certain times the ScrollPane Viewport is not visually updated.在某些时候,ScrollPane 视口不会在视觉上更新。 This is a strange behavior, because even if viewport is not visually updated, the scrollbars are still updated.这是一个奇怪的行为,因为即使视口没有在视觉上更新,滚动条仍然更新。

I put other ways to move the ScrollPane viewport horizontally using the same method, and for some reason, only the approach using the mouse makes it happen.我采用了其他方法来使用相同的方法水平移动 ScrollPane 视口,出于某种原因,只有使用鼠标的方法才能实现。 I thought this could be solved by making a request for layout using requestLayout , also causing a request to a pulse, but it does not work.我认为这可以通过使用requestLayout发出布局请求来解决,也会导致对脉冲的请求,但它不起作用。

THE TEST OUTPUT测试输出

The odd thing is that everything returns to normal when the window of my application is resized.奇怪的是,当我的应用程序窗口调整大小时,一切都恢复正常。 Here's a video that shows what happens to my program:这是一个视频,显示了我的程序会发生什么:

VIDEO & MIRROR 1视频镜子 1

I no longer know what else to do.我不再知道还能做什么。 Can anyone help me with this please?任何人都可以帮我解决这个问题吗?

EDIT (08/02/2014 10:08 AM GMT - 3:00)编辑 (08/02/2014 10:08 AM GMT - 3:00)

The original source code of my application is found written in Portuguese, so you may be seeing something unknown.我的应用程序的原始源代码是用葡萄牙语编写的,所以您可能会看到一些未知的东西。 Basically TestesUtil is a utility class with static methods that define shortcuts to other client classes.基本上 TestesUtil 是一个实用程序类,具有定义其他客户端类的快捷方式的静态方法。 I changed the call from my source code shown here previously and am now putting some methods of my class TestesUtil, translated into English as TestsUtil:我更改了之前此处显示的源代码中的调用,现在将我的类 TestesUtil 的一些方法,翻译成英语作为 TestsUtil:

public static void putRandomlyIn(Node node , double northPoint , double southPoint , double eastPoint , double westPoint)
{
    node.setLayoutX(Math.random() * pontoLeste);
    node.setLayoutY(Math.random() * pontoSul);

    fixEasternBoundary(node , eastPoint);
    fixNorthernBoundary(node , northPoint);
    fixWesternBoundary(node , westPoint);
    fixSouthernBoundary(node , southPoint);
}

There is no mystery here.这里没有什么神秘之处。 This method simply calculates a value from an interval, and defines the LayoutXY properties for the Node argument.此方法只是从一个区间计算一个值,并为 Node 参数定义 LayoutXY 属性。 Methods "fix ..." just check the boundsInParent bounds of the node compared to the point in the argument, and then adjust the layoutXYproperties from the Node object.方法“fix ...”只是检查节点的 boundsInParent 与参数中的点相比的边界,然后从 Node 对象调整 layoutXY 属性。 Even if I remove the random distribution of objects, the problem still happens.即使我删除了对象的随机分布,问题仍然会发生。 So I'm sure this problem is not being caused by this.所以我确定这个问题不是由这个引起的。

The source code of the original post was changed with the addition of the ability to move the scroll bars with the arrow keys.原始帖子的源代码已更改,增加了使用箭头键移动滚动条的功能。 Even if it is already an existing function of ScrollPane, adding that could reproduce the error seen with the mouse (now with arrows).即使它已经是 ScrollPane 的现有功能,添加它也可以重现用鼠标看到的错误(现在带有箭头)。 Some things were also translated into English for better understanding by the community.有些东西也被翻译成英文,以便社区更好地理解。

Please, I ask for help.请,我寻求帮助。 I'm getting dizzy not knowing what to do.我头晕不知道该怎么办。 This type of situation could be happening because of some bug in JavaFX?由于 JavaFX 中的某些错误,可能会发生这种情况? Ahhrr... Please somebody help me in this. Ahhrr...请有人帮助我。 :'( :'(

Thank you for your attention anyway.无论如何,感谢您的关注。

EDIT (09/02/2014 10:50 AM GMT - 3:00)编辑 (09/02/2014 10:50 AM GMT - 3:00)

Forgot to mention... My program was initially written and tested using JDK 8 b123.忘了提...我的程序最初是使用 JDK 8 b123 编写和测试的。 Currently I installed the JDK 8 b128 version and am still getting the same problem.目前我安装了 JDK 8 b128 版本,但仍然遇到同样的问题。 My operating system is Windows 7 64x.我的操作系统是 Windows 7 64x。

I am almost certain that this is a bug.我几乎可以肯定这是一个错误。 Are you guys getting the same result as me?你们和我得到的结果一样吗? Or am I the only one to find this kind of problem?或者我是唯一一个发现这种问题的人? If this is a bug, which procedure should be taken?如果这是一个错误,应该采取哪个程序?

Thank you for your attention.感谢您的关注。

EDIT (10/02/2014 09:45 AM GMT - 3:00)编辑(10/02/2014 09:45 AM GMT - 3:00)

A bounty was started.赏金开始了。

UPDATE更新

This bug has now been fixed for JavaFX 8u20.此错误现已针对 JavaFX 8u20 修复。

Bug description错误描述

This is a bug, that can be easily verified by executing the following code with JavaFx JRE 8:这是一个错误,可以通过使用 JavaFx JRE 8 执行以下代码轻松验证:

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

final ScrollPane sp = new ScrollPane();
final Image[] images = new Image[5];
final ImageView[] pics = new ImageView[5];
final VBox vb = new VBox();
final Label fileName = new Label();
final String [] imageNames = new String [] {"fw1.jpg", "fw2.jpg",
        "fw3.jpg", "fw4.jpg", "fw5.jpg"};

@Override
public void start(Stage stage) {
    VBox box = new VBox();
    Scene scene = new Scene(box, 180, 180);
    stage.setScene(scene);
    stage.setTitle("Scroll Pane");
    box.getChildren().addAll(sp, fileName);
    VBox.setVgrow(sp, Priority.ALWAYS);

    fileName.setLayoutX(30);
    fileName.setLayoutY(160);

    for (int i = 0; i < 5; i++) {
        images[i] = new Image(getClass().getResourceAsStream(imageNames[i]));
        pics[i] = new ImageView(images[i]);
        pics[i].setFitWidth(100);
        pics[i].setPreserveRatio(true);
        vb.getChildren().add(pics[i]);
    }

    sp.setVmax(440);
    sp.setPrefSize(115, 150);
    sp.setContent(vb);
    sp.vvalueProperty().addListener(new ChangeListener<Number>() {
        public void changed(ObservableValue<? extends Number> ov,
            Number old_val, Number new_val) {
                fileName.setText(imageNames[(new_val.intValue() - 1)/100]);
        }
    });

    stage.show();
}

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

This code comes directly from the JavaFX ScrollPane Tutorial .此代码直接来自JavaFX ScrollPane 教程

If one randomly moves the vertical scroll bar with the mouse very rapidly, then at some time the screen will freeze and no longer get updated.如果用鼠标非常快速地随机移动垂直滚动条,那么在某些时候屏幕会冻结并且不再更新。 Although one is still able to move the scroll bar around, the displayed images will stay fixed.尽管仍然可以移动滚动条,但显示的图像将保持固定。 Only if one resizes the frame, the display of the images will be updated and the ScrollPane reverts to its previous state.仅当调整框架大小时,图像的显示才会更新并且 ScrollPane 恢复到其先前的状态。 Note, that this bug will only happen in JRE 8, it is not reproducible in JRE 7.请注意,此错误只会在 JRE 8 中发生,在 JRE 7 中不可重现。

The only workaround for the problem, that I could find, is adding我能找到的问题的唯一解决方法是添加

sp.snapshot(new SnapshotParameters(), new WritableImage(1, 1));

to the listener:给听众:

    sp.vvalueProperty().addListener(new ChangeListener<Number>() {
        public void changed(ObservableValue<? extends Number> ov,
            Number old_val, Number new_val) {
                fileName.setText(imageNames[(new_val.intValue() - 1)/100]);
                sp.snapshot(new SnapshotParameters(), new WritableImage(1, 1));
        }
    });

Calling snapshot on the ScrollPane seems to force the update every time the vvalueProperty changes.每次 vvalueProperty 更改时,在 ScrollPane 上调用快照似乎都会强制更新。 This seems to be a known workaround for several update problems with JavaFX - see here .对于 JavaFX 的几个更新问题,这似乎是一种已知的解决方法 - 请参见此处

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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