JavaFX WhatApp-Like ConversationView - javafx

I'm trying to make a WhatsApp-Like Conversation-View in JavaFX.
In order to make the sent messages appear on the right and the received messages appear on the left then I cannot use TextArea. How can I do it? I tried GridPane without TextArea but it didn't make things easier.
Moreover, is it a good practice to make controls static?
Extra: if you can also help me do the chat bubble behind the text, it would be great.
Here is my code:
public class ConversationView implements WhatAppView {
private static Label nameLabel, statusLabel;
private static TextField messageTextField;
static TextArea messagesTextArea;
private static GridPane conversationSection;
private static Label changeViewLink;
private static Button sendMsgButton;
// private static int rowIndex = 1;
public void showView() {
AppMain.stage.setResizable(false);
AppMain.stage.setWidth(350);
AppMain.stage.setHeight(550);
BorderPane rootPane = new BorderPane();
rootPane.setPadding(new Insets(5, 5, 5, 5));
final int sectionHeight = 55;
StackPane contactSection = new StackPane();
nameLabel = new Label("RW");
statusLabel = new Label("Online");
changeViewLink = new Label("Go Back");
changeViewLink.setStyle("-fx-text-fill: blue;");
changeViewLink.styleProperty().bind(
Bindings.when(changeViewLink.hoverProperty())
.then(new SimpleStringProperty("-fx-underline: true; -fx-text-fill: blue;"))
.otherwise(new SimpleStringProperty("-fx-underline: false; -fx-text-fill: blue;")));
changeViewLink.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
AppMain.changeView(new ChatsView());
}
});
contactSection.getChildren().addAll(nameLabel, statusLabel, changeViewLink);
StackPane.setAlignment(changeViewLink, Pos.TOP_RIGHT);
StackPane.setAlignment(statusLabel, Pos.BOTTOM_CENTER);
contactSection.setPrefHeight(sectionHeight);
conversationSection = new GridPane();
conversationSection.setStyle("-fx-background-image: url('whatsapp-wallpaper.jpg')");
messagesTextArea = new TextArea();
messagesTextArea.setEditable(false);
// conversationSection.getColumnConstraints().addAll(new
// ColumnConstraints(AppMain.stage.getWidth()/2 - 10), new
// ColumnConstraints(AppMain.stage.getWidth()/2 - 10));
conversationSection.add(messagesTextArea, 0, 0);
conversationSection.setPrefSize(AppMain.stage.getWidth(), AppMain.stage.getHeight());
// conversationSection.getStylesheets().add("conversation.css");
ScrollPane scroll = new ScrollPane();
scroll.setPrefSize(conversationSection.getWidth(), conversationSection.getHeight());
scroll.setContent(conversationSection);
FlowPane messageSection = new FlowPane();
sendMsgButton = new Button("_Send");
sendMsgButton.setDisable(true);
sendMsgButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
sendMsg();
}
});
sendMsgButton.setPrefHeight(sectionHeight);
Tooltip sendMsgToolTip = new Tooltip("Send Message");
Tooltip.install(sendMsgButton, sendMsgToolTip);
FlowPane.setMargin(sendMsgButton, new Insets(0, 0, 0, 5));
messageTextField = new TextField();
messageTextField.setPromptText("Type your message here...");
Platform.runLater(new Runnable() { // 100% focus
public void run() {
messageTextField.requestFocus();
}
});
messageTextField.setPrefWidth(AppMain.stage.getWidth() - AppMain.stage.getWidth() / 5);
messageTextField.setPrefHeight(sectionHeight);
messageTextField.setAlignment(Pos.TOP_LEFT);
messageTextField.setOnKeyTyped(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (messageTextField.getText() != null && !messageTextField.getText().isEmpty()) {
sendMsgButton.setDisable(false);
} else {
sendMsgButton.setDisable(true);
}
}
});
messageTextField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (event.getCode().equals(KeyCode.ENTER) && messageTextField.getText() != null
&& !messageTextField.getText().isEmpty()) {
sendMsg();
}
}
});
messageSection.getChildren().add(messageTextField);
messageSection.getChildren().add(sendMsgButton);
messageSection.setPrefHeight(sectionHeight);
rootPane.setTop(contactSection);
rootPane.setCenter(conversationSection);
rootPane.setBottom(messageSection);
Scene scene = new Scene(rootPane);
AppMain.stage.setScene(scene);
AppMain.stage.setTitle("WhatsApp");
}
}
public class AppMain extends Application {
static Stage stage;
#Override
public void start(Stage primaryStage) throws Exception {
stage = primaryStage;
AppMain.stage.show();
changeView(new ConversationView());
}
public static void changeView(WhatAppView view) {
view.showView();
}
}
public interface WhatAppView {
public void showView();
}

You can create a custom control to determine message alignment and aesthetics such as the bubble like appearance. As a fan of HBox and VBox, I would recommend their usage in combination with an SVGPath to decorate the message.
SVGPath's let you draw custom shapes by providing information on the lines, arcs etc. These aren't unique to java so there are a few resources available to see some basic/advanced examples. My recommendation would be to read here: SVGPath and use the TryitEditor to experiment
Here are two quick examples:
When it comes to laying out the messages a VBox would suffice. You can bind the viewable children to an ObservableList of messages you would be able to iterate later. The added benefit of this is that adding to the list will update the UI automatically, and you'll also be able to iterate these later in the event you implement additional features such as delete, forward etc
I'd recommend reading up on the Bindings api, particularly bindContentBidirectional for more information on this
Using my above recommendations i've written a small example below you can reference. It's not visually impressive, but hopefully you can get some ideas from it, particularly this:
Extra: if you can also help me do the chat bubble behind the text, it
would be great.
The messages/speech bubbles:
enum SpeechDirection{
LEFT, RIGHT
}
public class SpeechBox extends HBox{
private Color DEFAULT_SENDER_COLOR = Color.GOLD;
private Color DEFAULT_RECEIVER_COLOR = Color.LIMEGREEN;
private Background DEFAULT_SENDER_BACKGROUND, DEFAULT_RECEIVER_BACKGROUND;
private String message;
private SpeechDirection direction;
private Label displayedText;
private SVGPath directionIndicator;
public SpeechBox(String message, SpeechDirection direction){
this.message = message;
this.direction = direction;
initialiseDefaults();
setupElements();
}
private void initialiseDefaults(){
DEFAULT_SENDER_BACKGROUND = new Background(
new BackgroundFill(DEFAULT_SENDER_COLOR, new CornerRadii(5,0,5,5,false), Insets.EMPTY));
DEFAULT_RECEIVER_BACKGROUND = new Background(
new BackgroundFill(DEFAULT_RECEIVER_COLOR, new CornerRadii(0,5,5,5,false), Insets.EMPTY));
}
private void setupElements(){
displayedText = new Label(message);
displayedText.setPadding(new Insets(5));
displayedText.setWrapText(true);
directionIndicator = new SVGPath();
if(direction == SpeechDirection.LEFT){
configureForReceiver();
}
else{
configureForSender();
}
}
private void configureForSender(){
displayedText.setBackground(DEFAULT_SENDER_BACKGROUND);
displayedText.setAlignment(Pos.CENTER_RIGHT);
directionIndicator.setContent("M10 0 L0 10 L0 0 Z");
directionIndicator.setFill(DEFAULT_SENDER_COLOR);
HBox container = new HBox(displayedText, directionIndicator);
//Use at most 75% of the width provided to the SpeechBox for displaying the message
container.maxWidthProperty().bind(widthProperty().multiply(0.75));
getChildren().setAll(container);
setAlignment(Pos.CENTER_RIGHT);
}
private void configureForReceiver(){
displayedText.setBackground(DEFAULT_RECEIVER_BACKGROUND);
displayedText.setAlignment(Pos.CENTER_LEFT);
directionIndicator.setContent("M0 0 L10 0 L10 10 Z");
directionIndicator.setFill(DEFAULT_RECEIVER_COLOR);
HBox container = new HBox(directionIndicator, displayedText);
//Use at most 75% of the width provided to the SpeechBox for displaying the message
container.maxWidthProperty().bind(widthProperty().multiply(0.75));
getChildren().setAll(container);
setAlignment(Pos.CENTER_LEFT);
}
}
Conversation window:
public class ConversationView extends VBox{
private String conversationPartner;
private ObservableList<Node> speechBubbles = FXCollections.observableArrayList();
private Label contactHeader;
private ScrollPane messageScroller;
private VBox messageContainer;
private HBox inputContainer;
public ConversationView(String conversationPartner){
super(5);
this.conversationPartner = conversationPartner;
setupElements();
}
private void setupElements(){
setupContactHeader();
setupMessageDisplay();
setupInputDisplay();
getChildren().setAll(contactHeader, messageScroller, inputContainer);
setPadding(new Insets(5));
}
private void setupContactHeader(){
contactHeader = new Label(conversationPartner);
contactHeader.setAlignment(Pos.CENTER);
contactHeader.setFont(Font.font("Comic Sans MS", 14));
}
private void setupMessageDisplay(){
messageContainer = new VBox(5);
Bindings.bindContentBidirectional(speechBubbles, messageContainer.getChildren());
messageScroller = new ScrollPane(messageContainer);
messageScroller.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
messageScroller.setHbarPolicy(ScrollBarPolicy.NEVER);
messageScroller.setPrefHeight(300);
messageScroller.prefWidthProperty().bind(messageContainer.prefWidthProperty().subtract(5));
messageScroller.setFitToWidth(true);
//Make the scroller scroll to the bottom when a new message is added
speechBubbles.addListener((ListChangeListener<Node>) change -> {
while (change.next()) {
if(change.wasAdded()){
messageScroller.setVvalue(messageScroller.getVmax());
}
}
});
}
private void setupInputDisplay(){
inputContainer = new HBox(5);
TextField userInput = new TextField();
userInput.setPromptText("Enter message");
Button sendMessageButton = new Button("Send");
sendMessageButton.disableProperty().bind(userInput.lengthProperty().isEqualTo(0));
sendMessageButton.setOnAction(event-> {
sendMessage(userInput.getText());
userInput.setText("");
});
//For testing purposes
Button receiveMessageButton = new Button("Receive");
receiveMessageButton.disableProperty().bind(userInput.lengthProperty().isEqualTo(0));
receiveMessageButton.setOnAction(event-> {
receiveMessage(userInput.getText());
userInput.setText("");
});
inputContainer.getChildren().setAll(userInput, sendMessageButton, receiveMessageButton);
}
public void sendMessage(String message){
speechBubbles.add(new SpeechBox(message, SpeechDirection.RIGHT));
}
public void receiveMessage(String message){
speechBubbles.add(new SpeechBox(message, SpeechDirection.LEFT));
}
}
Output:

Related

JavaFX button unselect

I'm trying to make a sprite editor using JavaFX using buttons as the pixels. I am able to change the colour of each button on press, but I'm trying to get it so if I click and drag I can paint multiple pixels.
The problem I'm finding is that after clicking and holding onto a button, I am unable to select the new button when I move the mouse over new button to select that one too. If I click and drag re-entering that button I can get the "Paint Dragged Pixel" debug message, but not if I enter a new pixel with the mouse down, which is what I want. Also I can get the pixel button to print "Entered Pixel" when the mouse enters any button, but not for when I click and drag to a new pixel.
I think the problem is that when I click on one pixel I am locked in, and unable to select a new pixel by hovering over a new one. Is there a way to unbind this selection, or is the problem a different one.
Main Application:
public class Main extends Application {
boolean mousePressed = false;
public boolean isMousePressed() {
return mousePressed;
}
#Override
public void start(Stage primaryStage) throws Exception{
BorderPane borderPane = new BorderPane();
primaryStage.setTitle("SpriteSheet");
Group root = new Group();
Scene scene = new Scene(borderPane, 500,200);
scene.setFill(Color.BLACK);
primaryStage.setScene(scene);
GridPane gridPane = new GridPane();
borderPane.setCenter(root);
for(int x = 0; x < 10; x++)
{
for(int y = 0; y < 10; y++) {
PixelButton button = new PixelButton();
button.setParentMain(this);
button.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
mousePressed = true;
System.out.println("mouseDown");
}
});
button.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
mousePressed = false;
System.out.println("mouseUp");
}
});
gridPane.add(button, x, y);
}
}
root.getChildren().add(gridPane);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The class for the PixelButton.
public class PixelButton extends Button {
Main parentMain;
public void setParentMain(Main parent) {
parentMain = parent;
}
public PixelButton() {
this.setMinSize(10, 10);
this.setPrefSize(10, 10);
this.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
}
});
this.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("Entered Pixel");
if(parentMain.isMousePressed()){
System.out.println("Paint Dragged Pixel");
}
}
});
}
}
Thank you in advance.
Ok so I have been thinking about this one for a bit and have finally come up with a solution I simplified my solution a bit and used rectangles instead of buttons but you can transfer most of the code to the buttons as well so to start with this is not the exact functionality you were looking for but as close as I could get basically I fire an event on mouse press that releases the mouse click and as long as that event is not coming from the rectangle then dont flip the painting boolean paint and so you basically click to enter a "Paint Mode" and click again to get out of coloring tiles
public class Main extends Application {
private boolean mousePressed;
#Override
public void start(Stage primaryStage) throws Exception{
BorderPane borderPane = new BorderPane();
primaryStage.setTitle("SpriteSheet");
Group root = new Group();
Scene scene = new Scene(borderPane, 500,200);
// scene.setFill(Color.BLACK);
primaryStage.setScene(scene);
GridPane gridPane = new GridPane();
borderPane.setCenter(root);
for(int x = 0; x < 10; x++) {
for(int y = 0; y < 10; y++) {
Rectangle rectangle = new Rectangle(10, 10);
rectangle.setOnMousePressed(event -> {
mousePressed = true;
System.out.println("mouseDown");
rectangle.fireEvent(new MouseEvent(MouseEvent.MOUSE_RELEASED,
rectangle.getLayoutX(), rectangle.getLayoutY(), rectangle.getLayoutX(), rectangle.getLayoutY(),
MouseButton.PRIMARY, 1,
false, false, false, false,
false, false, false, false,
false, false, null));
});
rectangle.setOnMouseReleased(event -> {
System.out.println(event.getSource());
if(!event.getSource().toString().equals("Rectangle[x=0.0, y=0.0, width=10.0, height=10.0, fill=0x000000ff]")) {
mousePressed = false;
System.out.println("mouseUp");
}
});
rectangle.setOnMouseMoved(event -> {
if(mousePressed) {
rectangle.setFill(Color.BLUE);
}
});
gridPane.add(rectangle, x, y);
}
}
root.getChildren().add(gridPane);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}

I'm trying to implemenet GUI on some kind of Doubly Linked list

I've so far done this, it's adding labels on the stage but removing thing is working but not as intended...
How can I store data of labels in an array or something so I can compare the remove.getText(); with that and delete that typed value if previous added as a label on stage.
public class Main extends Application {
Group root;
Label label1,label;
int count=1,count1=1;
public static void main(String[] args) {launch(args);}
public void start(Stage stage) throws Exception {
stage.setTitle("LinkedList GUI");
stage.setResizable(false);
root = new Group();
LinkedList<Integer> Linked =new LinkedList<Integer>();
Button Addfirst = new Button("AddFirst");
Addfirst.setTranslateX(40);
Addfirst.setTranslateY(350);
TextField first=new TextField();
first.setPrefWidth(60);
first.setTranslateX(120);
first.setTranslateY(350);
Addfirst.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
int a = Integer.parseInt(first.getText());
Linked.addFirst(a);
label1=new Label(" "+first.getText());
label1.setFont(Font.font("Buxton Sketch",FontWeight.BOLD,FontPosture.ITALIC,12));
label1.setPrefWidth(25);
label1.setPrefHeight(25);
label1.setTranslateY(60);
label1.setTextFill(Color.GREEN );
label1.setStyle("-fx-border-color: Blue;");
root.getChildren().addAll(label1);
label1.setTranslateX(250-(20*count++));
count++;
}
});
Button Addlast = new Button("AddLast");
Addlast.setTranslateX(200);
Addlast.setTranslateY(350);
TextField last=new TextField();
last.setPrefWidth(60);
last.setTranslateX(270);
last.setTranslateY(350);
Addlast.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
int b= Integer.parseInt(last.getText());
Linked.addLast(b);
label=new Label(" "+last.getText());
label.setFont(Font.font("Buxton Sketch",FontWeight.BOLD,FontPosture.ITALIC,12));
label.setPrefWidth(25);
label.setPrefHeight(25);
label.setTranslateY(60);
label.setTextFill(Color.GREEN );
label.setStyle("-fx-border-color: Blue;");
root.getChildren().add(label);
label.setTranslateX(250+(20*count1++));
count1++;
}
});
Button delete = new Button("Delete");
delete.setTranslateX(350);
delete.setTranslateY(350);
TextField remove=new TextField();
remove.setPrefWidth(60);
remove.setTranslateX(420);
remove.setTranslateY(350);
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
if(remove.getText().equals(first.getText())) {
Linked.remove(first.getText());
root.getChildren().remove(label1);
}
else if(remove.getText().equals(last.getText())) {
Linked.remove(last.getText());
root.getChildren().remove(label);
}
else {
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Error");
alert.setContentText("Not in List");
alert.showAndWait();
}
}
});
Text text =new Text("Doubly LinkedList GUI");
text.setStyle("-fx-border-color: Blak;");
text.setFont(Font.font("Buxton Sketch",FontWeight.BOLD,FontPosture.ITALIC,16));
text.setTranslateX(150);
text.setTranslateY(30);
root.getChildren().addAll(text,Addfirst,first,Addlast,last,delete,remove);
Scene scene = new Scene(root,500,400);
stage.setScene(scene);
stage.show();
}
}
I recommend using a layout that positions the children for you instead of positioning them yourself. E.g. using a HBox as parent for the labels allows you to simply add/remove the child at the same index as the one added/removed in the list:
#Override
public void start(Stage stage) throws Exception {
HBox container = new HBox(20);
container.setPrefHeight(40);
Button addFirst = new Button("add head");
Button addLast = new Button("add tail");
Button remove = new Button("remove");
TextField textField = new TextField();
HBox buttonContainer = new HBox(10, textField, addFirst, addLast, remove);
final LinkedList<Integer> list = new LinkedList<>();
addFirst.setOnAction(evt -> {
String text = textField.getText();
Integer value = Integer.parseInt(text);
list.addFirst(value);
container.getChildren().add(0, new Label(text));
});
addLast.setOnAction(evt -> {
String text = textField.getText();
Integer value = Integer.parseInt(text);
list.addLast(value);
container.getChildren().add(new Label(text));
});
remove.setOnAction(evt -> {
String text = textField.getText();
int value = Integer.parseInt(text);
ListIterator<Integer> iterator = list.listIterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
if (element == value) {
container.getChildren().remove(iterator.nextIndex() - 1);
iterator.remove();
break;
}
}
});
VBox root = new VBox(container, buttonContainer);
Scene scene = new Scene(root, 500, 400);
stage.setScene(scene);
stage.show();
}

How make scrollbar appear in Window with Panel with undefined size?

In the example bellow - window with Panel grows till it reaches browser window limits.
After that Panel content becomes scrollable but no scrollbar appear.
If I set Panel to fixed size scrollbar appear as per documentation.
Can someone suggest Vaadin pattern to implement scrollbars when Window or Panel size reaches browser window limit?
public class DemoUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout layout = new VerticalLayout();
layout.setSizeFull();
Button demo1 = new Button("Question");
demo1.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
Window window = new Window();
window.setClosable(true);
window.center();
window.setResizable(true);
window.setModal(false);
window.setContent(new UndefWindow(window));
addWindow(window);
}
});
layout.addComponent(demo1);
setContent(layout);
}
}
public class UndefWindow extends VerticalLayout {
private Window window;
public UndefWindow(Window window){
this.window = window;
Panel panel = new Panel();
VerticalLayout layout = new VerticalLayout();
Button add10 =new Button("Add 10");
add10.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
for (int i=0; i <10; i++){
layout.addComponent(new Label("Label: "+i));
}
window.center();
}
});
layout.addComponent(add10);
panel.setContent(layout);
addComponent(panel);
setExpandRatio(panel, 1.0f);
}
}
EDIT: I was able to achieve this with SizeReporter addon - code bellow
SizeReporter
public class UndefWindow extends VerticalLayout {
private Window window;
VerticalLayout root;
int rootHeight;
public UndefWindow(Window window, VerticalLayout root){
this.window = window;
this.root = root;
Panel panel = new Panel();
// panel.setHeight("400px");
SizeReporter sizeReporterRoot = new SizeReporter(root);
sizeReporterRoot.addResizeListener(new ComponentResizeListener() {
#Override
public void sizeChanged(ComponentResizeEvent event) {
System.out.println("Root size: " + event.getWidth() + " x " + event.getHeight());
rootHeight = event.getHeight();
}
});
SizeReporter sizeReporter = new SizeReporter(panel);
sizeReporter.addResizeListener(new ComponentResizeListener() {
#Override
public void sizeChanged(ComponentResizeEvent event) {
System.out.println("Panel size: " + event.getWidth() + " x " + event.getHeight());
if(event.getHeight()>rootHeight){
window.setHeight(rootHeight-60, Unit.PIXELS);
//window.center();
panel.setHeight(rootHeight-60, Unit.PIXELS);
}else
window.center();
}
});
VerticalLayout layout = new VerticalLayout();
Button add10 =new Button("Add 10");
add10.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
for (int i=0; i <10; i++){
layout.addComponent(new Label("Label: "+i));
}
}
});
addComponent(add10);
panel.setContent(layout);
addComponent(panel);
}
}
You have a bit overly complicated thinking in your Window content. In Vaadin, Window itself extends Panel, so you do not need additional Panel there at all. So I stripped it away. Also in order to layouts to work properly, you want to have your VerticalLayout height undefined, i.e. to grow until browser window size is met. In order to Panel scrolling to work in that scenario, there needs to be some content of defined height, in this case it is the Label, so I set defined height to Label. Then adding labels, will add size of the VerticalLayout, and once browser height is exceeded, Panel gets scroll bars.
public class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout layout = new VerticalLayout();
layout.setSizeFull();
Button demo1 = new Button("Question");
demo1.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
Window window = new Window("Window");
window.setClosable(true);
window.center();
window.setResizable(true);
window.setModal(false);
window.setContent(new UndefWindow(window));
addWindow(window);
}
});
layout.addComponent(demo1);
setContent(layout);
}
public class UndefWindow extends VerticalLayout {
private Window window;
public UndefWindow(Window window){
this.window = window;
Button add10 =new Button("Add 10");
add10.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
for (int i=0; i <10; i++){
Label label = new Label("Label: "+i);
label.setHeight("38px");
addComponent(label);
}
window.center();
}
});
addComponent(add10);
}
}
}

JavaFx choicebox event handler method doesnt change variable?

I'm new at javafx but I'm writing an app and I want my "to" value to change depending on which option is chosen in the choicebox but my current code always keeps it at 0..help? I want to be able to change the to depending on state
public void start(Stage primaryStage) {
double to=0;
primaryStage.setTitle("ShCal");
GridPane pane = new GridPane();
` pane.setAlignment(Pos.CENTER);
pane.setHgap(10);
pane.setVgap(10);
pane.setPadding(new Insets(25, 25, 25, 25));
Scene scene = new Scene(pane, 300, 275);
//button
Button button=new Button("to");
pane.add(button, 0, 3);
//Pick state
Label State=new Label("State");
pane.add(State,0,0);
//choicebox
ChoiceBox<String> choicesBox=new ChoiceBox<>();
choicesBox.getItems().addAll("NJ","NY");
pane.add(choicesBox,1,0);
//set default
choicesBox.setValue(null);
button.setOnAction(e->getChoice(choicesBox,to));
primaryStage.setScene(scene);
primaryStage.show();
}
private double getChoice(ChoiceBox<String> choicesBox, double tx) {
String state=choicesBox.getValue();
System.out.print(tx);
if(state=="NJ")
{
tx=10/100;
}
System.out.print(state);
return tx;
}
public static void main(String[] args) {
launch(args);
}
}
That is because your to value is of the primitive type double, defined in the scope of your start method. The method getChoice returns the new value, but you are not updating it.
Here are two approaches that you can try:
Define to as member:
private double to = 0;
private double getChoice(ChoiceBox<String> choicesBox) {
String state=choicesBox.getValue();
if(state=="NJ") {
tx=10/100;
}
}
However I personally would prefer a solution that is more inline with JavaFX: Define the to variable as member property:
private DoubleProperty to = new SimpleDoubleProperty(0);
private double getChoice(ChoiceBox<String> choicesBox) {
String state=choicesBox.getValue();
if(state=="NJ") {
tx.setValue(10/100);
}
}
Doing it this way you can then for example have a label displaying the value without the hassle of requiring to update it on each change:
Label lbl = new Label();
lbl.textProperty().bind(to.asString());

Change between two FXML files in JavaFX2.2 on change Listener

i am new in JavaFX programming. I have an Application, with a simple login page as described in the example here, and i add a StringProperty to the actiontarget element. So when the text changes inside the actiontarget i want a new FXML file with a webview inside, to load from the FXMLLoader and be dipslayed on the screen. Below is the exception i get. I can load any other fxml file, without a webview inside it, without a problem. Thanks in advance.Code samples below
The exception :
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-3
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:237)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:397)
at com.sun.webpane.sg.prism.InvokerImpl.checkEventThread(InvokerImpl.java:33)
at com.sun.webpane.platform.WebPage.<init>(WebPage.java:189)
at com.sun.webpane.sg.ImplementationManager.createPage(ImplementationManager.java:57)
at com.sun.webpane.sg.ImplementationManager.createPage(ImplementationManager.java:51)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:704)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:691)
at javafx.scene.web.WebView.<init>(WebView.java:245)
at student.WebBrowser.<init>(WebBrowser.java:31)
at Login.Login.replaceSceneContent(Login.java:171)
at Login.Login.access$000(Login.java:66)
at Login.Login$2.changed(Login.java:143)
at Login.Login$2.changed(Login.java:137)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:121)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:128)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
at javafx.scene.text.Text.setText(Text.java:188)
at Login.Client.run(Client.java:66)
First my listener:
// Add change listener
sp.addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String t1) {
if(t1.equalsIgnoreCase("user authenticated successfully")){
try {
replaceSceneContent(cb.getSelectionModel().getSelectedItem().toString()+".fxml",primaryStage);
System.out.println("everything ok");
} catch (Exception ex) {
System.out.println("something went wrong");
ex.printStackTrace();
}
}
}
});
Second my method : replaceSceneContent(String fxml, Stage stage)
private Parent replaceSceneContent(String fxml, Stage stage) throws Exception {
Parent page = (Parent) FXMLLoader.load(getClass().getResource("/FXML_Files/"+fxml), null, new JavaFXBuilderFactory());
Scene scene = stage.getScene();
if (scene == null) {
scene = new Scene(page, 700, 450);
stage.setScene(scene);
} else {
stage.getScene().setRoot(page);
}
if(fxml.equalsIgnoreCase("Student.fxml")){
Pane spane = (Pane) page.lookup("#pane");
WebBrowser wb = new WebBrowser();
spane.getChildren().add(wb);
}
return page;
}
And my WebBrowser class similar to the example in NetBeans7.2:
public class WebBrowser extends Pane {
public WebBrowser() {
WebView view;
final WebEngine eng;
view = new WebView();
view.setMinSize(10, 10);
view.setPrefSize(500, 400);
eng = view.getEngine();
eng.load("http://www.oracle.com/us/index.html");
VBox.setVgrow(this, Priority.ALWAYS);
setMaxWidth(Double.MAX_VALUE);
setMaxHeight(Double.MAX_VALUE);
final TextField locationField = new TextField("http://www.oracle.com/us/index.html");
locationField.setMaxHeight(Double.MAX_VALUE);
Button goButton = new Button("Go");
goButton.setDefaultButton(true);
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
eng.load(locationField.getText().startsWith("http://") ? locationField.getText() :
"http://" + locationField.getText());
}
};
goButton.setOnAction(goAction);
locationField.setOnAction(goAction);
eng.locationProperty().addListener(new ChangeListener<String>() {
#Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
locationField.setText(newValue);
}
});
GridPane grid = new GridPane();
ButtonsEvents be = new ButtonsEvents();
TilePane tp = be;
tp.setAlignment(Pos.CENTER);
grid.setVgap(5);
grid.setHgap(5);
GridPane.setConstraints(locationField, 0, 0, 1, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.SOMETIMES);
GridPane.setConstraints(goButton,1,0);
GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS);
GridPane.setConstraints(tp, 0, 2, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.NEVER);
grid.getColumnConstraints().addAll(
new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true),
new ColumnConstraints(40, 40, 40, Priority.ALWAYS, HPos.CENTER, true)
);
grid.getChildren().addAll(locationField, goButton,view, tp);
getChildren().add(grid);
}
#Override
protected void layoutChildren() {
List<Node> managed = getManagedChildren();
double width = getWidth();
double height = getHeight();
double top = getInsets().getTop();
double right = getInsets().getRight();
double left = getInsets().getLeft();
double bottom = getInsets().getBottom();
for (int i = 0; i < managed.size(); i++) {
Node child = managed.get(i);
layoutInArea(child, left, top,
width - left - right, height - top - bottom,
0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER);
}
}
}
As stated in the exception, changes in the JavaFX scene graph can only be made in the JavaFX application thread. To make sure your code is run in this thread, try the following:
Platform.runLater(new Runnable() {
#Override
public void run() {
// This method is invoked on JavaFX thread
replaceSceneContent(cb.getSelectionModel().getSelectedItem().toString()+".fxml",primaryStage);
}
});
The javafx manager will run this code at some point in the future on the correct thread.
More information:
http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm

Resources