i want to add and edit directly an element to a listview :
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javafx_test;
import java.util.Observable;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
/**
*
* #author karim
*/
public class Javafx_test extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<String> items = FXCollections.observableArrayList("test1", "test2");
ListView<String> list = new ListView<>(items);
list.setEditable(true);
list.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> param) {
return new TextFieldListCell<>(new StringConverter<String>() {
#Override
public String toString(String object) {
return object;
}
#Override
public String fromString(String string) {
return string;
}
});
}
});
Button btn = new Button();
btn.setText("Add String");
btn.setOnAction((ActionEvent event) -> {
String c = new String("test");
list.getItems().add(list.getItems().size(), c);
list.scrollTo(c);
list.edit(list.getItems().size() - 1);
});
VBox root = new VBox(list, btn);
Scene scene = new Scene(root);
primaryStage.setTitle("test!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Everything seems correct but that not working, it like its try to modify the first item not the newly added item in the last index, i don't know why
That's a bug.
There seems to be some truly horrible interplay between focus and editing. The basic problem seems to be that when a list cell loses focus, it cancels any editing. I think that by clicking on the button, you cause the focus to shift to that button, and then on the next rendering pulse the list cell sees it has lost focus and cancels editing. I can't quite explain why the first item in the list appears to go to an editing state, but I suspect it is due to some further interaction with the list's focusModel, which manages focus of individual items.
For a truly ugly hack, use an AnimationTimer to delay the call to ListView.edit(...) by an additional rendering frame. (In case you're not familiar with it, an AnimationTimer defines a handle(...) method that is invoked once on each rendering pulse; here I just count one frame and then call edit, and stop the timer.)
btn.setOnAction((ActionEvent event) -> {
String c = "test"+(list.getItems().size()+1);
list.getItems().add(list.getItems().size(), c);
list.scrollTo(list.getItems().size() - 1);
// list.edit(list.getItems().size() - 1);
new AnimationTimer() {
int frameCount = 0 ;
#Override
public void handle(long now) {
frameCount++ ;
if (frameCount > 1) {
list.edit(list.getItems().size() - 1);
stop();
}
}
}.start();
});
Calling scrollTo(...) with an index instead of an item seems more robust too (especially as you have items in there that are equal to each other :).)
Maybe someone else can come up with something a bit cleaner that this...
The issue seems to be the Cells not being updated before calling edit. Since updating the cells is done during layout, calling layout before starting the edit should fix the issue:
Example:
#Override
public void start(Stage primaryStage) {
ListView<String> listView = new ListView<>();
listView.setEditable(true);
listView.setCellFactory(TextFieldListCell.forListView());
Button editButton = new Button("Add & Edit");
editButton.setOnAction((ActionEvent event) -> {
listView.getItems().add("");
listView.scrollTo(listView.getItems().size() - 1);
listView.layout();
listView.edit(listView.getItems().size() - 1);
});
Scene scene = new Scene(new VBox(listView, editButton));
primaryStage.setScene(scene);
primaryStage.show();
}
What James_D already mentioned: It seems that the index for the edit method is calculated wrong. In the following example it should not grab the correct index, but it does.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ListEdit extends Application {
int i = 3;
#Override
public void start(Stage primaryStage) {
ObservableList<String> items = FXCollections.observableArrayList("test1", "test2");
ListView<String> list = new ListView<>(items);
list.setCellFactory(TextFieldListCell.forListView());
list.setEditable(true);
Button btn = new Button();
btn.setText("Add String");
btn.setOnAction((ActionEvent event) -> {
list.getItems().add(i - 1, "test" + i);
list.edit(i - 2);
i++;
});
VBox root = new VBox(list, btn);
Scene scene = new Scene(root);
primaryStage.setTitle("test!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
I want to ask if it is possible to make a chip in JFXChipView editable once it has been set.
You can create your own JFXChip and implement a behavior to enable editing. First, you need to have an editable label. I looked up online and I found this post: JavaFX custom control - editable label. Then, you can extend JFXChip to use that EditableLabel:
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXChip;
import com.jfoenix.controls.JFXChipView;
import com.jfoenix.svg.SVGGlyph;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.scene.layout.HBox;
public class EditableChip<T> extends JFXChip<Property<T>> {
protected final HBox root;
public EditableChip(JFXChipView<Property<T>> view, Property<T> item) {
super(view, item);
JFXButton closeButton = new JFXButton(null, new SVGGlyph());
closeButton.getStyleClass().add("close-button");
closeButton.setOnAction(event -> {
view.getChips().remove(item);
event.consume();
});
// Create the label with an initial value from the item
String initialValue = view.getConverter().toString(item);
EditableLabel label = new EditableLabel(initialValue);
label.setMaxWidth(100);
// Bind the item to the text in the label
item.bind(Bindings.createObjectBinding(() -> view.getConverter().fromString(label.getText()).getValue(), label.textProperty()));
root = new HBox(label, closeButton);
getChildren().setAll(root);
}
}
Note: I am using Property<T> instead of using the desired class T because JFXChipView stores the item the first time you add it. And in that case, you're going to get the values as you entered them the first time when calling JFXChipView#getChips().
Sample application:
import com.jfoenix.controls.JFXChipView;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class EditableChipViewApp extends Application {
#Override
public void start(Stage primaryStage) {
JFXChipView<Property<String>> chipView = new JFXChipView<>();
chipView.setChipFactory(EditableChip::new);
chipView.setConverter(new StringConverter<Property<String>>() {
#Override
public String toString(Property<String> object) {
return object == null ? null : object.getValue();
}
#Override
public Property<String> fromString(String string) {
return new SimpleStringProperty(string);
}
});
VBox container = new VBox(chipView);
Scene scene = new Scene(container, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Result:
This is how you get the actual values of the chips:
List<String> chipsValues = chipView.getChips().stream().map(Property::getValue).collect(Collectors.toList());
I am trying to add GUIs, created from individual files and add them into my main code.
While it seems to be working, kind of, however, it is missing some elements. For example, in my GridPane, there are a label and a text, both of which are missing. Likewise, for my treeview, there is a treeitem within, however, that is missing as well.
What I am trying to attempt is to reduce the amount of code in the main field and as well as to call relevant events between the Guis, eg. if I select something in the TreeView, that selected TreeItem information will be populated in the GridPane.
Client.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Client extends Application
{
private treeviewGui tvGui;
private gridpaneGui inputFieldsGui;
public void init()
{
tvGui = new treeviewGui();
inputFieldsGui = new gridpaneGui();
}
#Override
public void start(Stage topView)
{
topView.setTitle("Test Application");
HBox mainLayout = new HBox(10);
mainLayout.getChildren().addAll(tvGui, inputFieldsGui);
Scene scene = new Scene(mainLayout);
topView.centerOnScreen();
topView.setScene(scene);
topView.show();
}
public static void main(String[] argv)
{
launch(argv);
}
}
treeviewGui.java
import javafx.scene.control.*;
public class treeviewGui extends TreeView
{
private TreeView treeview;
public treeviewGui()
{
treeview = new TreeView();
preload();
}
private void preload()
{
TreeItem<String> newTI = new TreeItem<>("blah");
treeview.setRoot(newTI);
}
}
gridPane.java
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.geometry.Pos;
import javafx.scene.text.Text;
public class gridpaneGui extends GridPane
{
private GridPane gridPane;
public Text fnameTxt;
public gridpaneGui()
{
gridPane = new GridPane();
gridPane.setAlignment(Pos.CENTER);
gridPane.setHgap(5);
gridPane.setVgap(5);
// First Name
Label fnameLbl = new Label("First Name");
fnameTxt = new Text("-");
gridPane.addRow(0, fnameLbl, fnameTxt);
}
public void setFname(String nameStr)
{
fnameTxt.setText(nameStr);
}
}
i'm working on a project and i'd like to find a way to change the background color of some elements in a listView. i've find a way to add css style class to the listView in general but not to specific elements .
Also , i've heard about cell factory but I dont know if cell factory can adapt during the programme or just set up things at the begging
(i have a listView of an object that I call player , and I want that , when the player in the listView get enough points , his name becomes red)
is there a way to do something like this ?
ListView<Players> listview = ...;
for(Player p : listView){
p.addListener(//change color to red)
}
Thanks
I would use ObservableList and addListener to the given List.
Sample code:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
private StackPane main;
private ListView<Player> players;
#Override
public void start(Stage stage) {
main = new StackPane();
var scene = new Scene(main, 640, 480);
players = new ListView<Player>();
ObservableList<Player> playerObjs = FXCollections.observableArrayList (
new Player("A", 50),
new Player("B", 30),
new Player("C", 60),
new Player("D", 5),
new Player("E", 0)
);
players.setItems(playerObjs);
playerObjs.addListener(new ListChangeListener<Player>() {
#Override
public void onChanged(Change<? extends Player> change) {
updateView();
}
});
main.getChildren().add(players);
stage.setScene(scene);
stage.show();
}
public void updateView() {
for(int i = 0; i < players.getItems().size(); i++) {
if(players.getItems().get(i).getHp() < 10) {
players.getItems().get(i).setBackground(...);
}
}
}
public static void main(String[] args) {
launch();
}
}
Now, everytime the list changes, it calls updateView(), which if some condition holds, will set the given item Background to some value.
Let me know if that helped.
I'm using javaFX to create an application.
I have a hyper-link somewhere and I've set an (onAction) for it as shown below
Hyperlink studentList = ...; // It's given proper object
studentList.setOnAction(...);
now somewhere else i used this method to simluate a click on this hyperlink
studentList.fire();
now my problem is that how can i distinguish real click/keyPress from fire() method ?
Here's one way to do it. Just add an EventHandler to the setOnMousePressed property. Be sure to add it to setOnMousePressed and not e.g. setOnMouseClicked, since setOnMousePressed is invoked before the fire() is invoked while setOnMouseClicked is invoked after.
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MCVE extends Application {
#Override
public void start(Stage stage) {
VBox content = new VBox(5);
content.setPadding(new Insets(10));
Hyperlink link = new Hyperlink("Hyperlink");
Button fireButton = new Button("Fire hyperlink");
fireButton.setOnAction(e -> link.fire());
BooleanProperty mouseClicked = new SimpleBooleanProperty(false);
link.setOnMousePressed(e -> {
System.out.println("Mouse click");
mouseClicked.set(true);
});
link.setOnAction(e -> {
if (!mouseClicked.get()) {
System.out.println("No mouse click");
}
mouseClicked.set(false);
});
content.getChildren().addAll(link, fireButton);
stage.setScene(new Scene(content));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I've recently started playing around with JavaFX :) I'm working on a little pet project and one of the issues I'm currently having is animating the resize of a VBox when a child is added or removed to/from the VBox.
I've got "the kids" fading out and then being removed from the VBox already. Once that animation completes I need the VBox height to resize preferably as an animation and not like the instant change it currently does.
The other threads that I've found are similar but I think they aren't quite exactly what I'm looking for.
Animation upon layout changes
Adding Node's animated to a VBox
Main Class:
package application;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCombination;
import javafx.scene.paint.Color;
import application.notifier.*;
public class Main extends Application
{
#Override
public void start(Stage stage)
{
try
{
stage.setTitle("Test");
Group root = new Group();
Scene scene = new Scene(root, (Screen.getPrimary().getBounds().getWidth()-100), (Screen.getPrimary().getBounds().getHeight()-100), Color.WHITE);
Notifier notice = new Notifier();
Notifier.addNotice("Testing Add Notice");
Notifier.addNotice("Testing Add Notice again!");
root.getChildren().add(Notifier.container);
stage.setFullScreen(true);
stage.setScene(scene);
stage.setFullScreenExitHint("");
//stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
stage.show();
Button test = new Button("Remove");
test.setLayoutX(500.0);
test.setLayoutY(500.0);
test.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
Notifier.removeNotice();
}
});
root.getChildren().add(test);
}
catch(Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
launch(args);
}
}
Notifier Class:
import java.util.ArrayList;
import javafx.animation.FadeTransition;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.util.Duration;
public class Notifier
{
private static int maxVisibleNotices = 5;
private static int currentVisible = 0;
private static ArrayList<String> msgOverflow;
public static VBox container;
public Notifier()
{
BackgroundFill bkgrndFill = new BackgroundFill(Color.rgb(0, 0, 0, .65), new CornerRadii(10.0), new Insets(0));
Background bkgrnd = new Background(bkgrndFill);
Notifier.container = new VBox();
Notifier.container.backgroundProperty().set(bkgrnd);
Notifier.container.setAlignment(Pos.TOP_CENTER);
Notifier.container.setMinWidth(Screen.getPrimary().getBounds().getWidth() - 50);
Notifier.container.setMaxWidth(Screen.getPrimary().getBounds().getWidth() - 50);
Notifier.container.setLayoutX((Screen.getPrimary().getBounds().getWidth() - (Screen.getPrimary().getBounds().getWidth() - 50))/2);
Notifier.container.setLayoutY(5.0);
Notifier.container.setSpacing(5.0);
Notifier.container.setPadding(new Insets(5.0));
Notifier.msgOverflow = new ArrayList<String>();
}
public static void addNotice(String msg)
{
if(Notifier.currentVisible < Notifier.maxVisibleNotices)
{
Text txt = new Text(msg);
txt.setFill(Color.rgb(255,255,255));
Notifier.container.getChildren().add(txt);
Notifier.currentVisible++;
}
else
{
Notifier.msgOverflow.add(msg);
}
}
public static void removeNotice()
{
if(Notifier.currentVisible > 0)
{
FadeTransition ft = new FadeTransition(Duration.millis(1000), Notifier.container.getChildren().get(0));
ft.setFromValue(1.0);
ft.setToValue(0.0);
ft.setCycleCount(0);
ft.setAutoReverse(false);
ft.play();
ft.setOnFinished(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
Notifier.container.getChildren().remove(0);
Notifier.currentVisible--;
}
});
}
}
}
I hope this is clear enough.
And thanks in advance for help or suggestions.
Probably not very helpful any more. I am currently working on the same thing.
You just need:
1. Add the same animation(if they all supposed to be same) to each of the remaining children in the VBox.
2. Use ParallelAnimation to make them run at the same time.
3. Use again a SequentialAnimation for you "kids" fading out animation and the parallel animation. This ensures they will not happen concurrently since multiple threads could run simultaneously.
4. To reach still the same view as what the instant update does, use animation/timeline.setonFinished(EventHandler()) to updates the screen