简体   繁体   English

JavaFX 进度条不更新

[英]JavaFX Progress Bar doesn't update

I've searched through all the other issues with this, but with no luck.我已经搜索了所有其他问题,但没有运气。 I have been trying different things for over four hours and have gotten absolutely no progress (or errors, which is even more frustrating) so I figured I would finally ask here.四个多小时以来,我一直在尝试不同的东西,但绝对没有任何进展(或错误,这更令人沮丧),所以我想我最终会在这里问。

I am working on a small survival game as a side project to teach myself more about Java UI elements.我正在开发一个小型生存游戏作为辅助项目,以自学更多有关 Java UI 元素的知识。 The player scavenges for food and such, and it is a race against starvation and thirst.玩家寻找食物等,这是一场与饥饿和口渴的赛跑。 The situation I have is such:我的情况是这样的:

The game opens into a controller (MainController) that has several bars indicating the character's stats;游戏打开一个控制器(MainController),它有几个指示角色统计数据的条; for this example, we'll say Hunger and Thirst.对于这个例子,我们会说饥饿和口渴。

The player can click a button to open the character's inventory, which includes their food.玩家可以点击一个按钮来打开角色的物品栏,其中包括他们的食物。 This opens a new window on a new controller (InventoryController) that lists buttons corresponding to the character's inventory items.这会在新控制器 (InventoryController) 上打开一个新窗口,其中列出了与角色库存项目对应的按钮。

When the player clicks a food item, the character's Hunger drops, and the associated ProgressBar should drop as well.当玩家点击食物时,角色的饥饿值会下降,并且相关的 ProgressBar 也应该会下降。

Issue is: The character's internal hunger stat drops, and the ProgressBar's progress drops, but the bar never updates visually, even though it did internally.问题是:角色的内部饥饿统计数据下降,ProgressBar 的进度下降,进度条永远不会在视觉上更新,即使它在内部发生了变化。

As a brief example:举个简单的例子:

public class MainController implements Initializable {

    @FXML private ProgressBar hungerBar;

    public void initialize(URL url, ResourceBundle rb) {
        updateBars();
    }    

    public void updateBars(){
        hungerBar.setProgress(CC.pc.getHunger()/100);
    }
}

This works at first.首先起作用 It successfully get's the character's hunger, divides it by 100, and then sets the bar (in this case, to .40).它成功获取角色的饥饿值,将其除以 100,然后设置条形(在本例中为 0.40)。 No issues.没有问题。 The bar appears and is 40% full.栏出现并且已满 40%。 However, when I open and use the second Controller, nothing happens.但是,当我打开并使用第二个 Controller 时,没有任何反应。

public class InventoryController implements Initializable {


    @Override
    public void initialize(URL url, ResourceBundle rb) {
    }    

    @FXML private void feedVenison(ActionEvent event) throws Exception{

        FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
        loader.load();
        PlayController c = loader.<PlayController>getController();

        //Stuff that does the math and changes the character's stats here;
        //cut short for clarity.

        c.updateBars();
    }
}

I can add in a few Print commands, such as a System.out.println(hungerBar.getProgress()), and see that everything functions perfectly fine.我可以添加一些打印命令,例如 System.out.println(hungerBar.getProgress()),然后看到一切都运行良好。 The Character's hunger decreases, and in turn, the bar's progress is updated and is, functionally, lower.角色的饥饿感会降低,反过来,进度条的进度也会更新,并且在功能上会降低。 However, the bar never changes, no matter where between .01 and 1 it is set.但是,无论设置在 0.01 和 1 之间的哪个位置,条形图都不会改变。

I've tried and tried and I'm at my wits end!我已经尝试了又尝试,但我已经无能为力了! I'm obviously doing something wrong, and I'm sure it is obvious, but I have no idea what.我显然做错了什么,我确定这是显而易见的,但我不知道是什么。 There's no errors, it just doesn't work.没有错误,它只是不起作用。 Am I just using the wrong tool for this job?我是否只是为这项工作使用了错误的工具? What gives?什么给?

Update 1:更新 1:

More digging brought me to this question , which is very similar to what I am trying to do.更多的挖掘让我想到了这个问题,这与我想要做的非常相似。 Unfortunately, it has no answers either.不幸的是,它也没有答案。 It has a comment that leads to this Oracle doc, which I attempted to implement.它有一条评论指向这个 Oracle 文档,我试图实现它。

public void updateBars(){

    DoubleProperty hunger = new SimpleDoubleProperty(CC.pc.getHunger());
    hungerBar.progressProperty().bind(hunger);

}

But once again, I get the exact same issue.但再一次,我遇到了完全相同的问题。 The bar updates internally, yet the UI never changes and always displays a static amount that is incorrect.条形在内部更新,但 UI 永远不会改变并且总是显示不正确的静态数量。 What do I do?我该怎么办?

Update 2:更新 2:

I keep digging, but every time I think I have found the answer it just does the exact same thing over and over again.我一直在挖掘,但每次我认为我找到了答案时,它只是一遍又一遍地做同样的事情。 Tried out this, but same result.尝试了这个,但结果相同。

public void updateBars(){

    maybeWillWork();

}

public void maybeWillWork()
 {
    Platform.runLater(new Runnable() {
        @Override public void run() {
           hungerBar.setProgress(CC.pc.getHunger()/100);
           System.out.println("It ran!");
        }
    });
}

Exact same result.完全一样的结果。

Please.请。 What am I doing wrong?我做错了什么? I refuse to believe that the entirety of StackOverflow is stumped by a simple ProgressBar like I am;我拒绝相信像我这样的简单的 ProgressBar 难倒了整个 StackOverflow; someone has to know what stupid mistake I am making.有人必须知道我犯了什么愚蠢的错误。

Update 3:更新 3:

Well that was frustrating, but I got it to work.嗯,这令人沮丧,但我让它工作了。 I have posted the answer below.我已经在下面发布了答案。

What you're doing wrong in your approach你的方法做错了什么

You're updating the value when the controller is initialized, but you're only making a "snapshot" of the value.您在初始化控制器时更新该值,但您只是制作该值的“快照”。

Even if you're using the property ( Update 1 ) you're creating a independent property that is initially filled with the current value, but the value stored in the property is never updated.即使您正在使用该属性 ( Update 1 ),您也正在创建一个最初用当前值填充的独立属性,但存储在该属性中的值永远不会更新。

But you're calling updateBars() again, right?但是您又在调用updateBars() ,对吗? You do this but you create a new scene with a new controller instance for this purpose, so you do not adjust the scene that is displayed but some other scene that is never displayed.您这样做,但为此目的使用新的控制器实例创建了一个新场景,因此您无需调整显示的场景,而是调整一些从未显示的其他场景。

For this reason the updates never reach the scene currently shown.因此,更新永远不会到达当前显示的场景。

Of course you could store the existing controller in a convenient location and access it, but this way you increase the cohesion of your program.当然,您可以将现有控制器存储在方便的位置并访问它,但这样可以增加程序的内聚力。

A better approach would be to create a model class and add observable properties.更好的方法是创建一个模型类并添加可观察的属性。 You only create a single instance of this class and pass it everywhere where it's accessed.您只需创建此类的单个实例并将其传递到访问它的任何地方。 By observing the properties parts of your program "interested" in the value can update themselfs:通过观察程序中对值“感兴趣”的属性部分可以更新自己:

public class Model {

    private final DoubleProperty hunger = new SimpleDoubleProperty();

    public DoubleProperty hungerProperty() {
        return hunger;
    }

    public void setHunger(double value) {
        hunger.set(value);
    }

    public double getHunger() {
        return hunger.get();
    }

}

The model instance can be passed to the controllers by one of the approaches mentioned in this question: Passing Parameters JavaFX FXML模型实例可以通过这个问题中提到的方法之一传递给控制器​​: 传递参数 JavaFX FXML

Eg例如

public class MainController {

    @FXML private ProgressBar hungerBar;

    public void setModel(Model model){
        hungerBar.progressProperty().bind(model.hungerProperty().divide(100d));
    }
}
public class InventoryController {

    @FXML private void feedVenison(ActionEvent event) throws Exception {
        model.setHunger(someNewValue);
    }

    private Model model;

    public void setModel(Model model){
        this.model = model;
    }
}

Make sure you're passing the same instance of Model to both controllers using the approach in your own feedVenison method but only when you load the a scene that is also displayed.确保您使用自己的feedVenison方法中的方法将相同的Model实例传递给两个控制器,但仅当您加载也显示的场景时。 A factory that loads the views and using a custom interface containtning the setModel method could help reduce duplicate code...加载视图并使用包含setModel方法的自定义接口的工厂可以帮助减少重复代码......

Note: You could also achieve this by using a property in the class used for CC.pc , but IMHO using static members to pass information should be avoided.注意:您也可以通过使用用于CC.pc的类中的属性来实现这CC.pc ,但恕我直言,应避免使用静态成员传递信息。

After nearly eight hours of trying, I finally figured out the issue.经过近八个小时的尝试,我终于找到了问题所在。 Now, I freely admit that I don't entirely understand how this works, or why, but it does function exactly as I hoped it would.现在,我坦率地承认我并不完全理解这如何工作的,或者为什么,但它确实像我希望的那样发挥作用。 It took dozens of searches over far too much time, but I finally stumbled across this page.花了太多时间进行了数十次搜索,但我终于偶然发现了这个页面。 Instead of updating the label directly, he was using something called a "binding".他没有直接更新标签,而是使用了一种叫做“绑定”的东西。 Well, that's different... Of course, I had no idea what this was, but I dug and dug and lo and behold...嗯,那不一样……当然,我不知道这是什么,但我挖了又挖,瞧……

The bar needs to be bound to a DoubleProperty, and then that gets .set() to something, which updates the bar.酒吧需要绑定到一个DoubleProperty,然后将该得到.SET()的东西,它更新了吧。

I had toyed with the DoubleProperty in Update 1, but I neglected to attempt using the .set() function.我曾在更新 1 中使用过 DoubleProperty,但我忽略了尝试使用 .set() 函数。 Sure enough...果然...

This is what finally worked for me.这就是最终对我有用的方法。

@FXML ProgressBar hungerBar;
static DoubleProperty hungerUpdater = new SimpleDoubleProperty(.0);
public void initialize(URL url, ResourceBundle rb) {
        hungerBar.progressProperty().bind(hungerUpdater);
    }    

We create a new DoubleProperty , then bind the bar we wish to update to it.我们创建一个新的DoubleProperty ,然后将我们希望更新的栏绑定到它。 Later, on the same controller, we have:后来,在同一个控制器上,我们有:

public void updateBars(){
    hungerUpdater.set(CC.pc.getHunger()/100);
}

We grab the character's Hunger (which is between 0 and 100) and divide it by 100, so that we get a decimal between 0 and 1. This is then used to set the hungerUpdater DoubleProperty.我们获取角色的饥饿值(介于 0 和 100 之间)并将其除以 100,从而得到介于 0 和 1 之间的小数。然后将其用于设置 HungerUpdater DoubleProperty。

On the other controller, we have:在另一个控制器上,我们有:

public void initialize(URL url, ResourceBundle rb) {
}    

@FXML private void feedVenison(ActionEvent event) throws Exception{

    FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
    loader.load();
    MainController c = loader.<MainController>getController();

    //Stuff that does the math and changes the character's stats here;
    //cut short for clarity.

    c.updateBars();
}
}

From what I can gather, this essentially forces JavaFX to update the bar immediately, because the bar is actively listening to this Double for an update.据我所知,这实质上是强制JavaFX 立即更新栏,因为栏正在积极侦听此 Double 以进行更新。 As soon as an update is seen, the program updates it.一旦看到更新,程序就会更新它。

This was an extremely frustrating evening, but I finally got it.这是一个非常令人沮丧的夜晚,但我终于明白了。 I'm going to go drink heavily and pretend I didn't struggle this hard on something so simple and, to many, probably quite obvious.我要去大喝,假装我没有在这么简单的事情上这么努力,对许多人来说,可能很明显。

The problem : Progress bar take parameter between 0 to 1, which means a decimal value.问题:进度条采用 0 到 1 之间的参数,这意味着一个十进制值。 However simple division in Java returns an integer which can not hold decimals.然而,Java 中的简单除法返回一个不能保留小数的整数。

Solution : Cast the division to double type解决方案:将除法转换为双精度型

progressBar.setProgress((double)counter / 1000);

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

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