Items replacing but not asked - java

I'm working on a little game. So I create the game interface (countains the gun, the canvas which I use as gamespace and an interface for the user to control is gun). I place the different elements in the window and there is my problem. When I execute my code, all is well placed but once I use one of the buttons (buttons in both codes) or the slider (second code), the slider and the fire button replace themselves. And I don't understand why because I never asked this rellocation in my code. Also, when the items rellocate, I can't use any other items excepted the fire button and the slider.
Here are screenshots of what I have before using a button (first screenshot) (it's also how I want the interface to be) and the second screenshot shows the rellocation I have.
How it looks when I use nothing.
How it looks when I use a button or the slider.
Main.java :
package application;
import bureaux.Bureau;
import canons.Canon;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.ToolBar;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application{
private StackPane root, rootBureau;
private Scene scene;
private Stage stage;
private Text joueur;
private Button menu, musique, ajoutJoueur;
private FlowPane rootJeu;
private Bureau bureauJoueur;
private ToolBar toolBar;
private Canon canonJoueur;
#Override
public void start(Stage primaryStage) throws IOException {
getRoot();
getScene();
stage = primaryStage;
creerInterface();
stage.setTitle("Mad Java Guns");
stage.setResizable(false);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
public void creerInterface(String mode) { // creating the gamespace and the objects that the user need to play
getToolBar().getItems().addAll(getMusique(), getAjoutJoueur());
getRootBureau().getChildren().add(getBureauJoueur());
getRootJeu().getChildren().add(getCanonJoueur());
getRoot().getChildren().addAll(getToolBar(), getJoueur(), getRootBureau(), getRootJeu());
}
// Getters
public StackPane getRoot() {
if(root == null) {
root = new StackPane();
}
return root;
}
public Scene getScene() {
if(scene == null) {
scene = new Scene(root,1000,800);
}
return scene;
}
public Text getJoueur() { // gamespace
if(joueur == null) {
joueur = new Text("Espace de jeu");
joueur.setFont(Font.font("Arial", 20));
joueur.setTranslateY(120);
}
return joueur;
}
public ToolBar getToolBar() {
if(toolBar == null) {
toolBar = new ToolBar();
toolBar.setTranslateY(122);
toolBar.setTranslateX(3);
toolBar.setStyle("-fx-background-color: transparent");
}
return toolBar;
}
public Button getMusique() { // button too change the music in game
if (musique == null) {
musique = new Button("Musique");
musique.setOnMouseClicked(e -> {
System.out.println("musique"); // not coded yet
});
musique.setFocusTraversable(false);
}
return musique;
}
public Button getAjoutJoueur() { // add players in the game
if(ajoutJoueur == null) {
ajoutJoueur = new Button("Ajouter un joueur");
ajoutJoueur.setOnMouseClicked(e -> {
System.out.println("ajoutJoueur"); //not coded yet
});
ajoutJoueur.setFocusTraversable(false);
}
return ajoutJoueur;
}
public StackPane getRootBureau() { // pane where the user's interface will be placed
if(rootBureau == null) {
rootBureau = new StackPane();
rootBureau.setStyle("-fx-background-color: lightgrey");
rootBureau.setMaxSize(990, 250);
rootBureau.setTranslateY(270);
}
return rootBureau;
}
public Bureau getBureauJoueur() { // user's interface
if(bureauJoueur == null) {
bureauJoueur = new Bureau("Billy", getCanonJoueur());
}
return bureauJoueur;
}
}
Class Bureau.java :
package bureaux;
import canons.Canon;
import javafx.geometry.Orientation;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Slider;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
public class Bureau extends Parent {
private Slider sliderCanon;
private HBox boxPrincipale;
private VBox boxControlesCanon;
private Button feu;
public Bureau(String nom, Canon canon) {
getBoxControlesCanon().getChildren().addAll(getSliderCanon(), getFeu());
getBoxPrincipale().getChildren().add(getBoxControlesCanon());
this.setTranslateX(-480); // placing the boxes
this.setTranslateY(-95);
this.getChildren().add(getBoxPrincipale());
}
//Getteurs
public HBox getBoxPrincipale() {
if(boxPrincipale == null) { // return a HBox which countains the VBox (next function)
// and other elements which aren't created yet.
boxPrincipale = new HBox();
}
return boxPrincipale;
}
public VBox getBoxControlesCanon() { // return a VBox which countains the controls of the gun
//(gun not showed in the code, doesn't concern the problem)
if(boxControlesCanon == null) {
boxControlesCanon = new VBox();
boxControlesCanon.setSpacing(20);
}
return boxControlesCanon;
}
public Slider getSliderCanon() { //slider to orient the gun (gun not showed in the code, doesn't concern the problem)
if(sliderCanon == null) {
sliderCanon = new Slider(0, 360, 0);
sliderCanon.setOrientation(Orientation.VERTICAL);
sliderCanon.valueProperty().addListener(e -> {
System.out.println(sliderCanon.getValue());
});
sliderCanon.setShowTickMarks(true);
sliderCanon.setShowTickLabels(true);
sliderCanon.setMajorTickUnit(90f);
}
return sliderCanon;
}
public Button getFeu() { // fire button
if(feu == null) {
feu = new Button("Feu");
feu.setOnMouseClicked(e -> {
System.out.println("Feu");
});
feu.setFocusTraversable(false);
}
return feu;
}
}
Please ask for more informations if necessary. Thanks for your help.
EDIT : sorry for the unpoliteness on top of this text, I used to edit it and add "Hello" but it just don't want to show it :/

You are using Bureau extends Parent. You will have to be more specific and used the nodes that will produce the outcome you need.
Try something like Bureau extends HBox. Then
getBoxControlesCanon().getChildren().add(new VBox(getSliderCanon(), getFeu()));
To get the left alignment, you may need to do something like
getBoxControlesCanon().getChildren().addAll(new VBox(getSliderCanon(), getFeu()), someOtherNode);
HBox.setHGrow(someOtherNode, Priority.ALWAYS);
If you look at Parent compared to VBox, you will see that Parent does not describe how children nodes will be laid out. A lot of nodes that are a subclass of Parent do describe how their children nodes will be laid out.

Related

Is it possible to change the white file icon that appears when dragging a node with setOnDragDetected? (JavaFX) [duplicate]

This question already has answers here:
Using JavaFX with drag and drop, is it possible to have a ghost of the dragged object follow the cursor?
(2 answers)
JavaFX drag & drop with custom node beside mouse icon
(1 answer)
Closed 11 months ago.
GOAL:
I wish to change the white file icon that appears when dragging a node that has implemented setOnDragDetected. More specifically, I'm recreating chess in JavaFX, and want to have a chess piece image appear when dragging, instead of the current white file image, like as shown below.
CODE: I've created a minimal example:
package application;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
public class StackOverflowSample extends Application {
#Override
public void start(Stage primaryStage) {
try {
primaryStage.setTitle("Chess Sample");
Group root = new Group();
Scene scene = new Scene(root, 400, 200);
/* 1. Setting up Chess squares (aka stackpanes)*/
StackPane source1 = new StackPane(new Rectangle(100,100,Color.BLUE));
StackPane target1 = new StackPane(new Rectangle(100,100,Color.LIGHTBLUE));
source1.setLayoutX(50);
source1.setLayoutY(50);
target1.setLayoutX(250);
target1.setLayoutY(50);
/*2. Setting up the chess piece to move*/
Image whiteKnightImg = new Image("https://www.kindpng.com/picc/m/22-223299_white-knight-chess-png-transparent-png.png", 90,90,true,true);
ImageView whiteKnightView = new ImageView(whiteKnightImg);
source1.getChildren().add(whiteKnightView);
/* 3. Adding Drag and Drop Functionality*/
source1.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
Dragboard db = source1.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.putString("hasPiece");
db.setContent(content);
event.consume();
}
});
target1.setOnDragOver(new EventHandler <DragEvent>() {
#Override
public void handle(DragEvent event) {
if (event.getGestureSource() != target1 && event.getDragboard().hasString()) {
event.acceptTransferModes(TransferMode.MOVE);
}
event.consume();
}
});
target1.setOnDragDropped(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
target1.getChildren().add(whiteKnightView);
success = true;
}
event.setDropCompleted(success);
event.consume();
}
});
source1.setOnDragDone(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
if (event.getTransferMode() == TransferMode.MOVE) {
System.out.println("success");
}
event.consume();
}
});
root.getChildren().addAll(source1,target1);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
POSSIBLE SOLUTIONS:
Java Source Code. Maybe there's a way to inherit the Node class that implement the dragging related methods. I took a look at the source code for JavaFX.scene.Node and found the section related to setOnDragDetected, but didn't look into it any further.
public final void setOnDragDetected(
EventHandler<? super MouseEvent> value) {
onDragDetectedProperty().set(value);
}
public final EventHandler<? super MouseEvent> getOnDragDetected() {
return (eventHandlerProperties == null)
? null : eventHandlerProperties.getOnDragDetected();
}
/**
* Defines a function to be called when drag gesture has been
* detected. This is the right place to start drag and drop operation.
* #return the event handler that is called when drag gesture has been
* detected
*/
public final ObjectProperty<EventHandler<? super MouseEvent>>
onDragDetectedProperty() {
return getEventHandlerProperties().onDragDetectedProperty();
}
It's not possible. I've considered that it's not something that can be changed. believe it's possible that it's impossible to change this white icon, so I might just change my approach entirely. Instead of using StackPanes as squares that also hold images, I'm considering this tutorial.

Scrollpane in javafx is a burden on the RAM when displaying a big collection of items and views

I am developing a library application using javafx, where the user can import epub files, and he gets a library with shelves, where each shelf contains at most 6 books. I am using a scrollpane inside it a VBox containing additional VBoxes(each one resembles a shelf) each one contains a image(which is the shelf),and above it an HBox containing images of book covers. I tried with the listview but it doesn't work since a listview lists a list of items that you click on one of them, and in my case the item will be the entire shelf which contains several books(I want to handle the click on each book individually). Sorry for the long description.
This is the image represention of my work
There are many options for this; but likely the best is using a ListView as the comments suggest.
The sample application below will demonstrate one way to do so. I have not done any work on styling the ListView, however. Mostly because I am not very proficient in CSS myself (I welcome edits and suggestions), but also because that is out of the scope of this fairly vague question.
Combining a ListView with a custom CellFactory, you are able to build a layout for each "shelf" in your library; the ListView will display each row using that layout.
There are additional comments in the code below.
Library Example MCVE:
LibraryExample.java:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Separator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import java.util.List;
public class LibraryExample extends Application {
// Our list of shelves that will be displayed in the ListView
private final ObservableList<Shelf> shelves = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Build a list of 100 sample books. This list could come from a database or other outside source, of course
List<Book> books = FXCollections.observableArrayList();
for (int i = 0; i < 100; i++) {
books.add(new Book("Book #" + i, new ImageView("sample/generic-cover.png")));
}
// We will now create our shelves for the books. We will limit the number of books to 6 per shelf. This uses
// the subList method of our List to grab every 6 books until we run out.
int index = 0;
while (index < books.size()) {
// Make sure there are at least 6 books remaining, otherwise, we need to get the subList up to the size of
// the original list.
final int numToAdd = (index + 6 <= books.size() ? index + 6 : books.size());
shelves.addAll(new Shelf(books.subList(index, numToAdd)));
index += 6;
}
// Now, let's create our ListView that will hold our shelves.
ListView<Shelf> listView = new ListView<>();
VBox.setVgrow(listView, Priority.ALWAYS);
// Now for the magic. We will override the CellFactory for the ListView so we can provide our own layout
// for each row
listView.setCellFactory(new Callback<ListView<Shelf>, ListCell<Shelf>>() {
#Override
public ListCell<Shelf> call(ListView<Shelf> param) {
return new ShelfListCell();
}
});
listView.setItems(shelves);
root.getChildren().add(listView);
// Show the Stage
primaryStage.setWidth(700);
primaryStage.setHeight(600);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
class ShelfListCell extends ListCell<Shelf> {
#Override
protected void updateItem(Shelf shelf, boolean empty) {
super.updateItem(shelf, empty);
if (shelf == null || empty) {
setGraphic(null);
} else {
// Here, we will build our layout for each shelf
VBox root = new VBox(5);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(5));
HBox hBox = new HBox(20);
hBox.setAlignment(Pos.CENTER);
hBox.setPadding(new Insets(5));
// Add image for each each book on this shelf to the layout
for (Book book : shelf.getBooks()) {
// Get the image of the book and add a simple click listener
ImageView cover = book.getCoverImage();
cover.setPreserveRatio(true);
cover.setFitHeight(100);
cover.setOnMouseClicked(event -> System.out.println("Clicked " + book.getTitle()));
hBox.getChildren().add(book.getCoverImage());
}
root.getChildren().addAll(hBox, new Separator(Orientation.HORIZONTAL));
// Set the cell to display our layout
setGraphic(root);
}
}
}
Book.java:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.image.ImageView;
public class Book {
private final StringProperty title = new SimpleStringProperty();
private final ObjectProperty<ImageView> coverImage = new SimpleObjectProperty<>();
public Book(String title, ImageView coverImage) {
this.title.set(title);
this.coverImage.set(coverImage);
}
public String getTitle() {
return title.get();
}
public StringProperty titleProperty() {
return title;
}
public void setTitle(String title) {
this.title.set(title);
}
public ImageView getCoverImage() {
return coverImage.get();
}
public ObjectProperty<ImageView> coverImageProperty() {
return coverImage;
}
public void setCoverImage(ImageView coverImage) {
this.coverImage.set(coverImage);
}
}
Shelf.java:
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.List;
public class Shelf {
// Set max number of books per shelf
private final static int MAX_BOOKS = 6;
// Our observable list of books
private final ListProperty<Book> books = new SimpleListProperty<>(FXCollections.observableArrayList());
public Shelf(List<Book> books) {
this.books.addAll(books);
}
public void addBooks(Book... books) {
this.books.addAll(books);
}
public static int getMaxBooks() {
return MAX_BOOKS;
}
public ObservableList<Book> getBooks() {
return books.get();
}
public ListProperty<Book> booksProperty() {
return books;
}
public void setBooks(ObservableList<Book> books) {
this.books.set(books);
}
}
The Result:

Moving focus using keyboard trouble

Please help.
Situation: we have VBox with a lot of buttons (and i can scroll focus up/down via arrow keys on my keyboard), but they doesn't fit on the small form so i need vertical scrolling. I apply ScrollPane - and eventually everything became good except moving focus. When i press up/down arrow keys focus stands still but affects scrolling.
I need to disable scroll by keys with ScrollPane (use only mouse!) and enable move focus by up/down arrow keys. Could You please help me, how could i do it?
Many thanks!
Here my code
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestJavaFXScrollPane extends Application {
#Override
public void start(Stage primaryStage) {
Button btn0 = new Button("Btn0");
btn0.setOnKeyPressed((event) -> {
System.out.println("btn.KeyPressed; Code: " + event.getCode());
});
Button btn1 = new Button("Btn1");
Button btn2 = new Button("Btn2");
Button btn3 = new Button("Btn3");
VBox root = new VBox();
root.getChildren().addAll(btn0, btn1, btn2, btn3);
ScrollPane scrollPane = new ScrollPane(root);
scrollPane.setOnKeyPressed(event -> {
//if(event.getCode() == KeyCode.DOWN || event.getCode() == KeyCode.UP)
//event.consume(); // Tried to fix issue, but without result ((
});
Scene scene = new Scene(scrollPane, 100, 80);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Found solution by myself (is that correct?)
class MyEventHandler implements EventHandler<KeyEvent> {
#Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.UP) {
Object t0 = event.getSource();
if (!(t0 instanceof Button))
return;
((BehaviorSkinBase) ((Button)t0).getSkin()).getBehavior().traversePrevious();
} else if(event.getCode() == KeyCode.DOWN) {
Object t0 = event.getSource();
if (!(t0 instanceof Button))
return;
((BehaviorSkinBase) ((Button)t0).getSkin()).getBehavior().traverseNext();
}
}
public static void ProcessBurrons(Button... btns) {
if(btns == null && btns.length == 0) return;
for(Button el : btns) {
el.setOnKeyPressed(new MyEventHandler());
}
}
}

What is right way to work with tableview?

For some time i have been trying to get my tableview work as kind of spreadsheet that is updated by background thread and when cell get updated, it for few seconds higlights ( changes style ) and then goes back to original style.
I already know, that i can't store and set styles directly in table cell and i need some kind of backing class, that will hold this data. But tableview with its "reusing" of cells (using same cells for different data) acts really weird. When all cells fits on screen it works flawlessly for me, but once i place around 100 cells and it becomes scrollable it starts being buggy, sometimes styles ( or setted graphic) disappears and after scrolling appears, if i disable some top cells of view, some other cells after scrolling get disabled as well and so on. Is there any right way to do this?
What i need basically is
Background data thread ---updates--> tableview
Another thread --after few seconds removes style--> tableview
As i have it now, i have model class that holds data, style and reference to table cell where it should be ( i disabled ordering, so it should be ok ) and background thread updates data in model class, and that model class changes style on referenced cell and register itself in "style remover" thread, that after while removes style.
I think posting my actual code won't be useful, because once i've discovered that cells are being reused my code has become too complicated and a little bit unreadable so i want to completely redo it right way.
Peformance is not that important for me, there wont be more than 100 cells, but this highlighting and having buttons in tableview must work flawlessly.
This is how my app looks like now - for idea of what i need.
EDIT: here is link to my another question related to this.
The collaborators:
on the data side, a (view) model which has a recentlyChanged property, that's updated whenever the value is changed
on the view side, a custom cell that listens to that recentlyChanged property and updates its style as appropriate
The tricky part is to clean up cell state when re-used or not-used: the method that's always (hopefully!) called is cell.updateIndex(int newIndex), so that's the place to un-/register the listener.
Below a runnable (though crude ;) example
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import de.swingempire.fx.util.FXUtils;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TableCoreRecentlyChanged extends Application {
public static class RecentChanged extends TableCell<Dummy, String> {
private ChangeListener<Boolean> recentListener = (src, ov, nv) -> updateRecentStyle(nv);
private Dummy lastDummy;
/*
* Just to see any effect.
*/
protected void updateRecentStyle(boolean highlight) {
if (highlight) {
setStyle("-fx-background-color: #99ff99");
} else {
setStyle("-fx-background-color: #009900");
}
}
#Override
public void updateIndex(int index) {
if (lastDummy != null) {
lastDummy.recentlyChangedProperty().removeListener(recentListener);
lastDummy = null;
}
updateRecentStyle(false);
super.updateIndex(index);
if (getTableRow() != null && getTableRow().getItem() != null) {
lastDummy = getTableRow().getItem();
updateRecentStyle(lastDummy.recentlyChangedProperty().get());
lastDummy.recentlyChangedProperty().addListener(recentListener);
}
}
#Override
protected void updateItem(String item, boolean empty) {
if (item == getItem()) return;
super.updateItem(item, empty);
if (item == null) {
super.setText(null);
super.setGraphic(null);
} else {
super.setText(item);
super.setGraphic(null);
}
}
}
private Parent getContent() {
TableView<Dummy> table = new TableView<>(createData(50));
table.setEditable(true);
TableColumn<Dummy, String> column = new TableColumn<>("Value");
column.setCellValueFactory(c -> c.getValue().valueProperty());
column.setCellFactory(e -> new RecentChanged());
column.setMinWidth(200);
table.getColumns().addAll(column);
int editIndex = 20;
Button changeValue = new Button("Edit");
changeValue.setOnAction(e -> {
Dummy dummy = table.getItems().get(editIndex);
dummy.setValue(dummy.getValue()+"x");
});
HBox buttons = new HBox(10, changeValue);
BorderPane content = new BorderPane(table);
content.setBottom(buttons);
return content;
}
private ObservableList<Dummy> createData(int size) {
return FXCollections.observableArrayList(
Stream.generate(Dummy::new)
.limit(size)
.collect(Collectors.toList()));
}
private static class Dummy {
private static int count;
ReadOnlyBooleanWrapper recentlyChanged = new ReadOnlyBooleanWrapper() {
Timeline recentTimer;
#Override
protected void invalidated() {
if (get()) {
if (recentTimer == null) {
recentTimer = new Timeline(new KeyFrame(
Duration.millis(2500),
ae -> set(false)));
}
recentTimer.playFromStart();
} else {
if (recentTimer != null) recentTimer.stop();
}
}
};
StringProperty value = new SimpleStringProperty(this, "value", "initial " + count++) {
#Override
protected void invalidated() {
recentlyChanged.set(true);
}
};
public StringProperty valueProperty() {return value;}
public String getValue() {return valueProperty().get(); }
public void setValue(String text) {valueProperty().set(text); }
public ReadOnlyBooleanProperty recentlyChangedProperty() { return recentlyChanged.getReadOnlyProperty(); }
public String toString() {return "[dummy: " + getValue() + "]";}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
// primaryStage.setTitle(FXUtils.version());
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TableCoreRecentlyChanged.class.getName());
}

How to make a ListView selectable but not editable

so I'm writing a javafx app and I need to be able to select the cells from the list view (for copy paste purposes) but I don't want to make it editable, I mean, the content cannot be changed unless I want to (allowing it through a button, for example).
So I have the following code:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
List<String> contacts = new ArrayList<>(Arrays.asList("968787522","3424234234","2343234324"));
ListView<String> contactsList = new ListView();
contactsList.setItems(FXCollections.observableArrayList(contacts));
//this gives me the ability to edit the row as text field but I want this text field to not be editable
contactsList.setCellFactory(TextFieldListCell.forListView());
StackPane pane = new StackPane();
pane.getChildren().add(contactsList);
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show(); }
public static void main(String[] args) {
launch(args);
}
}
and if I set 'contactsList' as not editable, I'm not able to edit, neither select.
As you can see (image bellow),I'm editing the cell, but I want to be able to select the text(not the item), but I don't want to be able to delete characters (text selectable but not editable).
so after breaking my head off, lots of research and API reading, I came up with a solution. This does EXACTLY what I wanted to do. Here is the demo if someone needs it ;)
So the idea is, each time we want to select the content of a row we need to select the row, get the textField and set the editing to true or false, (every time).
So in the demo that I made, I placed a button so you can toggle the editing to true or false to be sure that's is working, and how is working.
Cheers.
I commented some of the code for better understanding, if you have any questions about this just let me know.
package sample;
import com.sun.javafx.scene.control.skin.VirtualFlow;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main extends Application {
private boolean editable = false;
public static IndexedCell getCell(final Control control, final int index) {
return getVirtualFlow(control).getCell(index);
}
public static VirtualFlow<?> getVirtualFlow(Control control) {
Group group = new Group();
Scene scene = new Scene(group);
Stage stage = new Stage();
if(control.getScene() == null) {
group.getChildren().setAll(control);
stage.setScene(scene);
stage.show();
}
VirtualFlow<?>flow = (VirtualFlow<?>) control.lookup("#virtual-flow");
return flow;
}
public void setEditable(ListView contactsList){
//this needs to be done since we need to run our code after the text field was rendered
//so we need to invoke our code after this happens, if not it will throw a null pointer...
Platform.runLater(() -> {
//this is one of the most important guys because javafx api says that
//TextFieldListCell.forListView() allows editing of the cell content when the cell is double-clicked,
// or when {#link ListView#edit(int)} is called.
int rowIndex = contactsList.getSelectionModel().getSelectedIndex();
contactsList.edit(rowIndex);
ListCell rootCell = (ListCell) getCell(contactsList, rowIndex);
TextField textField = (TextField) rootCell.getGraphic();
textField.setEditable(editable);
});
}
#Override
public void start(Stage primaryStage) throws Exception{
List<String> contacts = new ArrayList<>(Arrays.asList("968787522","3424234234","2343234324"));
ListView<String> contactsList = new ListView();
contactsList.setItems(FXCollections.observableArrayList(contacts));
contactsList.setEditable(true);
//this gives me the ability to edit the row as text field but I want this text field to not be editable
contactsList.setCellFactory(TextFieldListCell.forListView());
contactsList.setOnEditStart(e -> {
setEditable(contactsList);
});
StackPane pane = new StackPane();
Button editBtn = new Button("Toggle edit");
editBtn.setOnAction(event -> {
editable = !editable;
editBtn.setText("Editing = " + editable);
//to cancel any editing that might be occuring
contactsList.getSelectionModel().clearSelection();
});
pane.getChildren().addAll(contactsList,editBtn);
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If I understand you correctly, it is not necessary to set the listview to 'not editable', as the default behaviour should suffice for your purpose. Take a look at this code, for example:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
ListView listView = new ListView();
listView.getItems().addAll("one","two","three","four");
listView.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println(listView.getSelectionModel().getSelectedItem());
}
});
StackPane root = new StackPane();
root.getChildren().add(listView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("ListView Example");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I changed nothing about the editable-property of the ListView, but I can select every item, without being able to edit it (in the sense of changing its value). You can easily add an EventHandler to the ListView to perform whatever operation you want to perform. You could also add an EventHandler to every cell of the ListView by manipulating the CellFactory, as shown in this answer: How to handle ListView item clicked action?
Here's what works for me:
TableView<DataBean> table = new TableView<>();
table.setItems(...); // list of some DataBean objects with dataBeanField proprty
table.setEditable(true);
TableColumn<DataBean, String> column = new TableColumn<>("SomeData");
column.setCellValueFactory(new PropertyValueFactory<DataBean, String>("dataBeanField"));
column.setCellFactory(new Callback<TableColumn<DataBean, String>, TableCell<DataBean, String>>() {
#Override
public TableCell<DataBean, String> call(TableColumn<DataBean, String> param) {
return new TextFieldTableCell<>(new DefaultStringConverter() {
private String defaultValue = "";
#Override
public String fromString(String newValue) {
return super.fromString(defaultValue);
}
#Override
public String toString(String value) {
return defaultValue = super.toString(value);
}
});
}
});

Categories

Resources