JavaFx Progress cannot be to solve a variable - java

I'm trying to put the progress to 100% after meet condition OK. But it not finds the variable "progressBar", if instead of set the progress, I set the visibility, works.
columSituacao.setCellValueFactory(new Callback<CellDataFeatures<Tabela, HBox>, ObservableValue<HBox>>() {
public ObservableValue<HBox> call(CellDataFeatures<Tabela, HBox> p) {
final Tabela tabela = p.getValue();
final ProgressBar progressBar = new ProgressBar(0.0);
progressBar.setPrefWidth(columSituacao.getWidth());
progressBar.progressProperty().bind(tabela.progressProperty());
final HBox box = new HBox();
box.setPrefHeight(Progress.PREF_HEIGHT);
final Text text = new Text();
text.textProperty().bind(tabela.etapaProperty());
final BorderPane border = new BorderPane();
border.setTop(text);
border.setBottom(progressBar);
BorderPane.setAlignment(text, Pos.CENTER);
tabela.etapaProperty().addListener(new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue.equals(ConstantesEtapa.ETAPA_OK)) {
progressBar.setProgress(1.0);//Here is the exception
}
}
});
box.getChildren().add(border);
return new SimpleObjectProperty<HBox>(box);
}
});
Tank's

I think your issue is that you cannot set a unidirectionally bound value.
Unbind the value before you try to set it.
For example:
if (newValue.equals(ConstantesEtapa.ETAPA_OK)) {
progressBar.progressProperty().unbind();
progressBar.setProgress(1.0);
}

Related

How to access values from automatically generated Labels?

Here I receive an ObservableList of Products. Then for each Product in the List I create a Label for the name, price and quantity. I also create two Buttons, to add and remove quantity, altering the value in the label. My problem is in the ´escolherProdutos()´ method, where I want to access the value of each Label respective to de quantity so I can know what quantities of each product are being requested, and I cant seem to access these values from outside the populateFlowPane() method. This is probably not the best solution to my problem, and I am a beginner, so if you can help me solve my problem or even have a better way of doing this I would be really grateful.
public class EscolherProdutosController
{
#FXML private VBox nomesVBox;
#FXML private VBox precoVBox;
#FXML private VBox qtdsVBox;
#FXML private Button escolherBtn;
private static ArrayList<Label> quantidades = new ArrayList<>();
#FXML
public void initialize()
{
populateFlowPane();
}
public void populateFlowPane()
{
ObservableList<Produto> produtos = Logic.getProdutos();
produtos.forEach(prod -> {
HBox hbox = new HBox(5);
Label nome = new Label(prod.getNome());
Label preco = new Label(String.valueOf(prod.getPreco()));
Button minus = new Button("-");
minus.setMinSize(20, 20);
Label qtd = new Label("0");
Button plus = new Button("+");
nomesVBox.getChildren().add(nome);
precoVBox.getChildren().add(preco);
hbox.getChildren().addAll(minus, qtd, plus);
qtdsVBox.getChildren().add(hbox);
//remover unidades do produto
minus.setOnAction((ActionEvent e) -> {
int quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 1)
{
quantidade--;
qtd.setText(String.valueOf(quantidade));
}
});
//adicionar unidades do produto
plus.setOnAction((ActionEvent e) -> {
int quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 0)
{
quantidade++;
qtd.setText(String.valueOf(quantidade));
}
});
quantidades.add(qtd);
});
}
public void escolherProdutos()
{
ObservableList<Produto> produtos = Logic.getProdutos();
produtos.forEach(prod -> {
quantidades.forEach(qtd -> {
Logic.escolherProdutos(prod.getIdProduto(),
Integer.parseInt(qtd.getText()));
});
});
}
}
I would probably add them to a list and access them accordingly im not sure what you need from them because you are being vague but take a look at the code sample below
public class MySceneController {
#FXML private ListView listView;
private ArrayList<Label> labelList = new ArrayList<>();
#FXML
public void initialize()
{
populateListView();
}
public void populateListView()
{
ObservableList<Products> products = Logic.getProducts();
products.forEach(prod -> {
Label label = new Label(prod.getName());
//Add them to a list here
labelList.add(label);
listView.getItems().addAll(results);
});
}
public void doSomething()
{
for (Label label : labelList) {//Maybe iterate through the list depending on what you need
//do something
}
//Here is where I need to access the label values
}
}
I solved the problem. Maybe it's not the best solution, but it works for me. What I did was create a HashMap where the product ID is the key and the quantity is the value.
Then for the add and remove buttons, I simply replace the value for the corresponding key in the HashMap. Doing this, I can then use it as input for the escolherProdutos() method from the Logic class, to select the products.
public class EscolherProdutosController
{
#FXML private VBox nomesVBox;
#FXML private VBox precoVBox;
#FXML private VBox qtdsVBox;
private HashMap<BigDecimal, Integer> quantidades = new HashMap<>();
#FXML
public void initialize()
{
populateFlowPane();
}
public void populateFlowPane()
{
ObservableList<Produto> produtos = Logic.getProdutos();
produtos.forEach(prod -> {
HBox hbox = new HBox(5);
Label nome = new Label(prod.getNome());
Label preco = new Label(String.valueOf(prod.getPreco()));
Button minus = new Button("-");
minus.setMinSize(20, 20);
Label qtd = new Label("0");
Button plus = new Button("+");
nomesVBox.getChildren().add(nome);
precoVBox.getChildren().add(preco);
hbox.getChildren().addAll(minus, qtd, plus);
qtdsVBox.getChildren().add(hbox);
//remover unidades do produto
minus.setOnAction((ActionEvent e) -> {
Integer quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 1)
{
quantidade--;
qtd.setText(String.valueOf(quantidade));
if(quantidades.containsKey(prod.getIdProduto()))
quantidades.replace(prod.getIdProduto(), quantidade);
else
quantidades.put(prod.getIdProduto(), quantidade);
}
});
//adicionar unidades do produto
plus.setOnAction((ActionEvent e) -> {
Integer quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 0)
{
quantidade++;
qtd.setText(String.valueOf(quantidade));
if(quantidades.containsKey(prod.getIdProduto()))
quantidades.replace(prod.getIdProduto(), quantidade);
else
quantidades.put(prod.getIdProduto(), quantidade);
}
});
});
}
public void escolherProdutos()
{
Logic.escolherProdutos(quantidades);
}

Javafx listen for Control changes in a Panel

I want to add a change listener for every control in a panel. I want it to be robust, so whenever I change fxml file, I wouldn't have to alter the code that listens for changes in controls.
I have figured a way to add a listener for a specific type of control.
panel.getChildren()
.stream()
.filter(node -> node instanceof TextField).forEach(node ->
((TextField) node).textProperty()
.addListener((observable, oldValue, newValue) -> {
//execute some code
}));
However I would need to a add similar code to every type of control I intend to use in a panel for this to work.
panel.getChildren()
.stream()
.filter(node -> node instanceof TextField).forEach(node ->
((TextField) node).textProperty()
.addListener((observable, oldValue, newValue) -> {
//execute some code
}));
panel.getChildren()
.stream()
.filter(node -> node instanceof TextArea).forEach(node ->
((TextArea) node).textProperty()
.addListener((observable, oldValue, newValue) -> {
//execute some code
}));
//and so on...
panel.getChildren()
.stream()
.filter(node -> node instanceof ComboBox).forEach(node ->
((ComboBox<?>) node).valueProperty()
.addListener((observable, oldValue, newValue) -> {
//execute some code
}));
What I want to do is.
I've a document editor that has a panel with controls, so whenever user changes one of that controls preset value, panel with save and cancel buttons would be enabled. Also if user tried to exit a program, without canceling or saving a document, a warning would pop up asking him if he wants to discard changes and exit or cancel.
However I intend to do a lot of changes to document structure, so I will need to add and remove controls from panel constantly. So I need the best way to add this type of listener for every control in a panel in one go.
you should write (extend) your own controls and use them in your application. In them you can implement all specific tracking logic as #James_D mentioned. Such an extended TextField should look like this:
public class TrackableTextField extends javafx.scene.control.TextField {
private StringProperty originalText = new ReadOnlyStringWrapper(this, "originalText");
public final String getOriginalText() { return originalText.get(); }
public final void setOriginalText(String value) {
originalText.set(value);
setText(value);
}
public final StringProperty originalTextProperty() { return originalText; }
private final ReadOnlyBooleanWrapper dirty = new ReadOnlyBooleanWrapper(this, "dirty", false);
public final boolean isDirty() { return dirty.get(); }
public final ReadOnlyBooleanProperty dirtyProperty() { return dirty.getReadOnlyProperty(); }
public TrackableTextField() {
init();
}
public TrackableTextField(String text) {
init();
setOriginalText(text);
}
private void init() {
textProperty().addListener( e -> {
dirty.set(!Objects.equals(getOriginalText(), getText()));
} );
}
public void rollback() {
setText(getOriginalText());
}
public void commit() {
setOriginalText(getText());
}
}
and a usage example may be like
public class Test extends Application {
private TrackableTextField tf_name = new TrackableTextField();
private TrackableTextField tf_sname = new TrackableTextField();
private Button save = new Button("Save");
private Button discard = new Button("Discard");
#Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
root.add(new Label("Name: "), 0, 0);
root.add(tf_name, 1, 0);
root.add(new Label("Surname: "), 0, 1);
root.add(tf_sname, 1, 1);
root.add(save, 0, 2);
root.add(discard, 1, 2);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
initialize();
}
private void initialize() {
save.setDisable(true);
discard.setDisable(true);
save.disableProperty().bind(tf_name.dirtyProperty().or(tf_sname.dirtyProperty()).not());
discard.disableProperty().bind(tf_name.dirtyProperty().or(tf_sname.dirtyProperty()).not());
tf_name.setOriginalText("guleryuz");
tf_sname.setOriginalText("guleryuz");
save.setOnAction( e -> {
tf_name.commit();
tf_sname.commit();
} );
discard.setOnAction( e -> {
tf_name.rollback();
tf_sname.rollback();
} );
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX bindContentBidirectional of different types of Lists

If you have two different Lists.
ObservableList<File>
ObservableList<Node>
or just
ObservableList<A>
ObservableList<B>
How do you bind it together? Is there any transform or something like that?
Thank you.
Referal - https://stackoverflow.com/a/29320895/1347177
You can listen to the change in the ObservableList of Files and for each new addition add a corresponding ImageView to the VBox.
I have create an example in which I have an ObservableList of String, which is a list of URL of images. On the button click I add a new URL(the address being the same) to the list. I have a ListChangeListener which listens to the addition of Strings and adds an ImageView to the VBox for it.
I am listening to Addition. You can add for Removal as well.
public class Main extends Application {
private final String IMAGE_PATH = "http://i.imgur.com/fcn1bFx.jpg";
private final ObservableList<String> listOfImages = FXCollections.observableArrayList();
#Override
public void start(Stage primaryStage) throws Exception {
ScrollPane pane = new ScrollPane();
VBox box = new VBox();
box.setSpacing(10);
box.setAlignment(Pos.CENTER);
Button button = new Button("Add");
button.setOnAction( e -> {
listOfImages.add(IMAGE_PATH);
});
listOfImages.addListener((ListChangeListener<String>) c -> {
c.next();
if (c.wasAdded()) {
box.getChildren().add(0, new ImageView(c.getAddedSubList().get(0)));
}
});
box.heightProperty().addListener((ov, oldValue, newValue) -> {
pane.setVvalue(pane.getVmax());
});
box.getChildren().add(button);
pane.setContent(box);
Scene scene = new Scene(pane, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
}
This is not a bi-directional binding but will fill in your needs.
Moreover, you can also add Listeners to the VBox and do corresponding changes to the ObservableList.
box.getChildren().addListener(new ListChangeListener<Node>() {
#Override
public void onChanged(Change<? extends Node> c) {
c.next();
if(c.wasRemoved()){
listOfImages.remove(c.getList().get(0));
}
}
});

Show always the last line in a TextArea

I have a JavaFx TextArea where i update the text from another thread.
/**
* Start a new thread to update the text in the textarea. The progressmanager is called in a loop to deleiver the updated text
*/
private void startTextAreaUpdate() {
final Task<Void> updateTextAreaTask = new Task<Void>() {
#Override
protected Void call() throws Exception {
// Start updating the progresstext
while (!prog.getIsDone()) {
updateMessage(prog.getProgressText());
Thread.sleep(100);
}
return null;
}
};
//Here the TextArea text is bound to the task
taProgressText.textProperty()
.bind(updateTextAreaTask.messageProperty());
// Start the action in a new thread
Thread th = new Thread(updateTextAreaTask);
th.setDaemon(true);
th.start();
}
Because the text doesn't fit in the textarea i always want to show the last line of the text.
In the main thread i add a ChangeListener.
taProgressText.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
int lastPos = taProgressText.getText().length();
taProgressText.positionCaret(lastPos);
}
});
However the position doesn't change. What is going wrong here?
I think the position of the carat changes, but it doesn't actually scroll.
Try this (ugly hack...) instead of the binding and the current listener you have:
updateTextAreaTask.messageProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> obs, String oldMessage, String newMessage) {
taProgressText.setText(newMessage);
ScrollBar scrollBar = (ScrollBar) taProgressText.lookup(".scroll-bar:vertical");
if (scrollBar != null) {
scrollBar.setValue(scrollBar.getMax());
}
}
});

JavaFX custom component get action from button in custom component

I want to get the event of two buttons in my custom component.
the component is a imageview with two buttons to move between images, but I need to get the position of the image that is currently displayed, Im storing the key of the image, but I need to know when a button have been pressed outside the custom component, so I can change a Label outside the custom component.
public class TransitionSlider extends AnchorPane {
#FXML
private AnchorPane transitionSliderPane;
#FXML
private ImageView transitionSliderImageView;
#FXML
private Button prevButton;
#FXML
private Button nextButton;
private Map<Integer,Image> imageMap;
private Image currentImage;
private DropShadow imageViewDropShadow;
private int currentKey = 1;
private Image[] images;
public TransitionSlider() {
FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setController(this);
loader.setLocation(this.getClass().getResource("TransitionSlider.fxml"));
loader.setClassLoader(this.getClass().getClassLoader());
try {
loader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
prevButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= 1){
currentKey = currentKey + 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
nextButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= imageMap.size()){
currentKey = currentKey - 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
}
// more code here...
}
I want a way to capture the event and get variables inside the component and change a label outside the custom component...
for example:
public class Gallery extends Application {
#FXML
TransitionSlider ts;
Label label;
#Override
public void start(Stage stage) throws Exception {
label = new Label();
TransitionSlider ts = new TransitionSlider();
ts.captureButtonEvent(){ // need a way to capture this
label.setText(ts.getCurrentKey());
}
// more code here....
}
If I understood your question correctly, you want a binding.. Follow these steps:
1) Put bindable field and its getter/setter into TransitionSlider:
private IntegerProperty currentKey = new SimpleIntegerProperty(1);
public int getCurrentKey() {
return currentKey.get();
}
public void setCurrentKey(int val) {
return currentKey.set(val);
}
public IntegerProperty currentKeyProperty() {
return currentKey;
}
2) Bind this property to label's text in Gallery:
label = new Label();
TransitionSlider ts = new TransitionSlider();
label.textProperty.bind(ts.currentKeyProperty().asString());
Alternatively, if you want to do stuff more than just changing label's text, you can add a change listener to currentKeyProperty:
ts.currentKeyProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
label.setText(newValue);
// do other stuff according to "oldValue" and "newValue".
}
});

Categories