【问题标题】:Change the image in ImageView in javaFX在javaFX中更改ImageView中的图像
【发布时间】:2017-05-08 10:02:53
【问题描述】:

我正在寻找一个示例,当某个变量 x 将值 1 变为 y.png 时,需要更改 动态图像x.png,当这个变量回到 0 时恢复旧图像。如果这是可能的,怎么办?感谢帮助

我正在尝试使用此代码但没有成功...

这是我的控制器类

public class FXMLDocumentController implements Initializable {

@FXML
private ImageView myImage;

@Override
public void initialize(URL url, ResourceBundle rb) {
    Random random = new Random();


    while (true) { // <-- this is the problem how i can do that without while(true)??
        int x = random.nextInt(1);
        if (x == 0) {
            Image image = new Image(getClass().getResource("x.png").toExternalForm());
            myImage.setImage(image);
        }
        if (x == 1) {
            Image image = new Image(getClass().getResource("y.png").toExternalForm());
            myImage.setImage(image);
        }

    }

}

}

【问题讨论】:

  • 所以你想要的行为是图像在 x 和 y 之间闪烁?
  • 是的,是否制作闪烁的图像并不重要

标签: java javafx


【解决方案1】:

一个相当稳健的解决方案是使用IntegerProperty 和ImageView 的imageProperty 之间的绑定:

ImageView im = new ImageView();
IntegerProperty intValue = new SimpleIntegerProperty();
List<Image> images = Arrays.asList(new Image(getClass().getResource("aa.png").toString()),
        new Image(getClass().getResource("bb.png").toString()));

im.imageProperty().bind(Bindings.createObjectBinding(() -> images.get(intValue.getValue()),
    intValue));

图像被放入List(或数组,或任何可索引容器)中,然后绑定返回相应索引上的Image。


要让图片闪烁,可以新建一个Thread:

Task<Void> task = new Task<Void>() {
    @Override
    protected Void call() throws Exception {
        Random random = new Random();
        while (true) {
            Thread.sleep(50);
            intValue.set(random.nextInt(images.size()));
        }
    }
};

Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();

【讨论】:

  • 我在哪里可以在这段代码中插入随机数?
  • 和之前一样:intValue.set(random.nextInt(images.size())).
  • 但是图像不会动态变化,只使用随机的第一选择,我需要它来闪烁生成随机数:1time 1 2time 0 3time 0 4time 1 etc...etc.. . 对不起我的英语
  • 连续不断地改变随机数生成的图像,不会停止,不仅是第一次,谢谢
【解决方案2】:

如果我理解正确,您希望在整数变量的值发生变化时切换图像。 您可以将 x 放入 SimpleIntegerProperty 并为其添加 Listener。

SimpleIntegerProperty xProperty = new SimpleIntegerProperty();
xProperty.addListener((observable, oldValue, newValue) -> {
    if (newValue.intValue() == 0) {
        myImage.setImage(image1);
    }
    if (newValue.intValue() == 1) {
        myImage.setImage(image2);
    }
});
xProperty.set(random.nextInt());

每次您在 xProperty 上调用 set 时,都会触发您的侦听器。

正如对我的回答的评论中指出的那样,您不应该每次都重新加载图像 - 所以我删除了该代码并只使用了变量 image1 和 image2。你可以在构造函数中初始化它们。

【讨论】:

  • 这段代码和作者发布的代码都不必要地破坏了资源加载器。我建议你提取出两个image 变量的常量
  • 我只回答了这个问题,所以我只是复制了代码以显示它必须放在哪里。但我会添加一条评论来指出这一点。
  • 我得到错误newValue == 0 和newValue == 1 编译器说:二元运算符'=='的错误操作数类型第一个提示编号第二个类型int。谢谢
  • 对不起,我的错。 newValue 是 Number 类型的,所以你必须写 newValue.intValue()。我在我的回答中更正了这个......
【解决方案3】:

因此,我认为您的问题不在于 JavaFX 表示逻辑,而在于您尝试使用的源数据的阻塞性质。从这个意义上说,我建议您在他的视图代码中使用@Tobi,并添加以下类:

import javafx.application.Platform;
import java.util.concurrent.Executors;


class XAdapter {

  private static final ExecutorService blockableThread = Executors.newCachedThreadPool();

  public void pushChangesTo(IntegerProperty notifier){
    blockableThread.submit(() -> {

      int newX = random.nextInt(); //insert your long running call here. 

      Platform.runLater(() -> {
        notifier.setValue(newX);
        pushChangesTo(notifier)
      });
    });
  }
}

然后你可以像这样修改你的文档:

public class FXMLDocumentController {

  //...

  IntegerProperty xProperty = new SimpleIntegerProperty();
  private final XAdapter adapter = new XAdapter()

  @Override public void initialize(){
    adapter.pushChangesTo(xProperty)
  }
}

注意:这是java并发,很容易出错。您可能会考虑花一些时间阅读像Java Concurrency In Practice 这样的书,以了解您所做的事情为什么有问题,以及executors 和Platform.runLater 的确切目的是什么。 有很多更好的方法可以做到这一点,我在这里写的解决方案很可能无法为项目负责人所接受。特别是,您可能希望将blockableThread 标记为守护线程。

请注意,我链接到您的书的最后一位作者,以及课程Executors 的作者是同一个人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-16
    • 2017-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多