恢复
祝您有个美好的StackOverflow社区。
我一直在尝试开发一个程序,该程序使用户可以将对象放在一个区域中,并允许通过鼠标移动该区域。对于此类程序,我决定使用ScrollPane,因为用户可以在我称为画布的区域中添加各种内容。由于某种原因,我的程序中发生了一些奇怪的事情。
程序说明
我基本上所做的是创建一组对象,并将该组定义为ScrollPane内容。在该组内,添加了一个Rectangle对象以用作画布边界。该对象具有较大的尺寸(例如1500 x 1000),并用于防止节点超出其限制的计算中。这只是我的程序中现有大矩形后面的逻辑,但实际上,鼠标移动没有Node对象。存在的是Shape对象按矩形区域的随机分布。
因为ScrollPane的滚动条已移动,所以我使用setHvalue setVvalue方法。不幸的是,出于我的目的,此方法不会使用像素值更改ScrollPane视口的位置,而是使用介于0f和1f之间的值。因此我可以用鼠标移动视口,我使用了一个称为3规则的方程式(据我所知,在这里是我的国家),我们将值等价并进行交叉乘法。
例如,假设我想用鼠标水平移动ScrollPane的视口,并且我的画布区域的宽度为2000像素。找到鼠标从一个点拖到另一点的距离(以像素为单位),我需要知道该值在0f到1f范围内的表示方式。假设我将鼠标拖动了3个像素,可以通过以下比较找到0f到1f的表示形式:
2000 px ---- 1f
3 px ---- xf
相乘相乘,我将得到以下结果:
xf = 3 / 2000
xf = 0.0015
注意:我相信大家都知道。我不是在教数学
只想解释我的问题的逻辑。
源代码
这是我的程序类:
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() );
}
}
问题
单击画布区域上的鼠标按钮并将其拖动,可以看到该程序水平移动了ScrollPane视口。该程序似乎可以正常运行(或不能正常运行)。但是,有时会突然拖动鼠标(...或没有!)时出了点问题。在某些时候,ScrollPane视口不会以视觉方式进行更新。这是一个奇怪的行为,因为即使未在视觉上更新视口,滚动条仍会更新。
我使用其他方法使用相同的方法水平移动ScrollPane视口,由于某种原因,只有使用鼠标的方法才能实现它。我认为可以通过使用requestLayout发出布局请求来解决此问题,这也会导致发出脉冲请求,但这是行不通的。
测试输出
奇怪的是,当我调整应用程序窗口的大小时,一切恢复正常。这是一个视频,显示我的程序发生了什么:
VIDEO和MIRROR 1
我不再知道该怎么办。有人可以帮我吗?
编辑(2014年2月8日上午10:08 GMT-3:00)
我的应用程序的原始源代码是用葡萄牙语编写的,因此您可能会看到未知的内容。基本上,TestesUtil是带有静态方法的实用程序类,这些方法定义了其他客户端类的快捷方式。我从前面显示的源代码中更改了该调用,现在放入类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);
}
这里没有谜。此方法仅根据一个间隔计算一个值,然后为Node参数定义LayoutXY属性。方法“修复...”仅检查节点的boundsInParent边界与参数中的点相比,然后从Node对象调整layoutXYproperties。即使删除对象的随机分布,问题仍然会发生。因此,我确定此问题不是由此引起的。
原始帖子的源代码已更改,并增加了使用箭头键移动滚动条的功能。即使它已经是ScrollPane的现有功能,添加它也可以重现鼠标看到的错误(现在带有箭头)。一些东西也被翻译成英文,以使社区更好地理解。
请,我寻求帮助。我不知道该怎么办而感到头晕。由于JavaFX中的某些错误,可能会发生这种情况? Ahhrr ...请有人帮助我。 :'(
无论如何,谢谢您的关注。
编辑(2014年2月9日格林尼治标准时间上午10:50-3:00)
忘了提及...我的程序最初是使用JDK 8 b123编写和测试的。目前,我安装了JDK 8 b128版本,仍然遇到相同的问题。我的操作系统是Windows 7 64x。
我几乎可以肯定这是一个错误。你们得到的结果和我一样吗?还是我是唯一发现这种问题的人?如果这是一个错误,应该采取哪个步骤?
感谢您的关注。
编辑(2014年10月2日09:45 GMT-3:00)
赏金开始了。
最佳答案
更新
JavaFX 8u20的此错误现已修复。
错误说明
这是一个错误,可以通过使用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);
}
}
此代码直接来自JavaFX ScrollPane Tutorial。
如果使用鼠标非常快速地随机移动垂直滚动条,则屏幕有时会冻结并且不再更新。尽管仍然可以左右移动滚动条,但是显示的图像将保持不变。仅当调整框架的大小时,图像的显示才会更新,并且ScrollPane会恢复为其先前的状态。请注意,此错误只会在JRE 8中发生,而在JRE 7中是不可复制的。
我可以找到的唯一解决该问题的方法是添加
sp.snapshot(new SnapshotParameters(), new WritableImage(1, 1));
给听众:
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));
}
});
每次vvalueProperty更改时,在ScrollPane上调用快照似乎都会强制进行更新。对于JavaFX-see here的多个更新问题,这似乎是已知的解决方法。
关于java - ScrollPane不刷新其内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21632693/