I'm making an app using JavaFX and JFoenix through the scene builder in Java but I have some trouble with the initialization of my class member variables. Here is my code
public class MessageApp extends Application {
private AnchorPane mainLayout;
private Stage primaryStage;
private int i = 5;
public void init() {
i = 0;
}
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(PeerApp.class.getResource("PeerApp.fxml"));
mainLayout = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Test");
JFXDecorator decorator = new JFXDecorator(primaryStage, mainLayout);
decorator.setCustomMaximize(true);
Scene scene = new Scene(decorator, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
@FXML
private void clickSendMessage() {
System.out.println("i = " + i);
}
public static void main(String[] args) {
launch(args);
}
}
The problem here is that even if I reset I to 0 in the init
function or the start
function when I click on my send button that triggers the clickSendMessage
function, my println
prints "i = 5". The only way to reset it is to put i = 0 in the clickSendMessage function itself.
So, in this case, it is not a big problem, but if I don't initialize a class member variable during its declaration, even if I initialize it in the init
or start
function, my app crashes when I click my send button because at this stage it is still considered as null but I don't know why.
Any clue of why ? Where do I have to initialize my class member variables?
EDIT:
Ok so I tried to create a dedicated controller for my view and my first problem seems to be fixed, everything is initialised as expected but now my ListView
is not updated each time that I add a Contact
to my list but it was the case before. I don't know what I broke by creating a separated controller, any idea ?
Here's the full code
PeerApp.java
public class PeerApp extends Application {
private AnchorPane mainLayout;
private Stage primaryStage;
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(PeerApp.class.getResource("PeerApp.fxml"));
mainLayout = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Peer Messenger");
JFXDecorator decorator = new JFXDecorator(primaryStage, mainLayout);
decorator.setCustomMaximize(true);
Scene scene = new Scene(decorator, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
PeerAppController.java
public class PeerAppController {
private PeerApplication peer = new PeerApplication("1234");
private ObservableList<Contact> contacts;
private ChangeListener<Contact> listener;
private int i = 5;
@FXML
private JFXListView<Contact> contactList;
@FXML
private JFXButton addContactButton;
@FXML
private JFXTextArea messageTextArea;
@FXML
private GridPane chatGridPane;
@FXML
private JFXButton sendButton;
@FXML
public void initialize() {
initPeer();
initRootLayout();
contacts = FXCollections.observableArrayList(Contact.extractor());
contactList.setItems(contacts);
listener = new ChangeListener<Contact>() {
@Override
public void changed(ObservableValue<? extends Contact> observable, Contact oldValue, Contact newValue) {
chatGridPane.getChildren().clear();
List<Message> conversation = contacts.get(contactList.getSelectionModel().getSelectedIndex()).getConversation();
int i;
i = 0;
while (conversation.size() >= i + 1) {
Label messageLabel = new Label(conversation.get(i).getContent());
chatGridPane.addRow(i, messageLabel);
i++;
}
}
};
contactList.getSelectionModel().selectedItemProperty().addListener(listener);
i = 0;
}
private void initPeer() {
peer.init(EncryptionType.NONE, CompressionType.NONE);
peer.getPeerDaemonPlug().setComListener(new ICommunicationListener() {
@Override
public void onDataReceived(String s) {
Label labelMessage = new Label(s);
GridPane.setHalignment(labelMessage, HPos.RIGHT);
chatGridPane.addRow(getRowCount(chatGridPane) + 1, labelMessage);
contacts.get(contactList.getSelectionModel().getSelectedIndex()).getConversation().add(new Message(s, false));
}
@Override
public void onStreamPacketCompleted(OutputStream outputStream) {
}
});
}
private void initRootLayout() {
// Init the layout
ColumnConstraints c1 = new ColumnConstraints();
c1.setPercentWidth(100);
sendButton = new JFXButton();
contactList = new JFXListView<Contact>();
chatGridPane = new GridPane();
chatGridPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
chatGridPane.getColumnConstraints().add(c1);
}
@FXML
private void clickAddContact() {
TextInputDialog dialog = new TextInputDialog("");
dialog.setTitle("New Contact");
dialog.setHeaderText("Add a new contact");
dialog.setContentText("Enter the ID of your contact:");
Optional<String> result = dialog.showAndWait();
result.ifPresent(id -> {
Contact c = new Contact(id);
contacts.add(c);
System.out.println(contactList.getItems().size());
});
}
@FXML
private void clickSendMessage() {
// Not to be there
String message = messageTextArea.getText();
Label labelMessage = new Label(message);
try {
peer.sendRequest(sendDataToNode, new SendDataToNodeParam(contactList.getSelectionModel().getSelectedItem().toString(), message));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
i = 0;
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
i = 1;
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
i = 2;
e.printStackTrace();
} catch (InvalidKeyException e) {
i = 3;
e.printStackTrace();
} catch (BadPaddingException e) {
i = 4;
e.printStackTrace();
} catch (IOException e) {
i = 5;
e.printStackTrace();
}
messageTextArea.setText("");
GridPane.setHalignment(labelMessage, HPos.LEFT);
chatGridPane.addRow(getRowCount(chatGridPane) + 1, labelMessage);
contacts.get(contactList.getSelectionModel().getSelectedIndex()).getConversation().add(new Message(message, true));
System.out.println("i = " + i);
}
private int getRowCount(GridPane pane) {
int numRows = pane.getRowConstraints().size();
for (int i = 0; i < pane.getChildren().size(); i++) {
Node child = pane.getChildren().get(i);
if (child.isManaged()) {
Integer rowIndex = GridPane.getRowIndex(child);
if(rowIndex != null){
numRows = Math.max(numRows,rowIndex+1);
}
}
}
return numRows;
}
}
Contact.java
public class Contact {
private StringProperty id;
private List<Message> conversation;
public Contact(String id) {
this.id = new SimpleStringProperty(id);
this.conversation = FXCollections.observableArrayList();
}
public static Callback<Contact, Observable[]> extractor() {
return new Callback<Contact, Observable[]>() {
@Override
public Observable[] call(Contact param) {
return new Observable[]{param.id};
}
};
}
@Override
public String toString() {
return String.format("%s", id.get());
}
public StringProperty getId() {
return this.id;
}
public void setId(StringProperty id) {
this.id = id;
}
public List<Message> getConversation() {
return this.conversation;
}
}
PeerApp.fxml
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="PeerAppController">
<children>
<SplitPane dividerPositions="0.29797979797979796" layoutX="200.0" layoutY="120.0" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<JFXListView fx:id="contactList" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<JFXButton fx:id="addContactButton" buttonType="RAISED" layoutX="133.0" layoutY="357.0" onAction="#clickAddContact" ripplerFill="#10ae07" text="+" textAlignment="CENTER" AnchorPane.bottomAnchor="14.0" AnchorPane.rightAnchor="14.0" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<JFXTextArea fx:id="messageTextArea" focusColor="#40a85c" layoutX="15.0" layoutY="237.0" maxHeight="50.0" minHeight="10.0" prefHeight="50.0" prefWidth="339.0" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="72.0" />
<ScrollPane layoutX="12.0" layoutY="12.0" prefHeight="276.0" prefWidth="394.0" AnchorPane.bottomAnchor="65.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="5.0">
<content>
<GridPane fx:id="chatGridPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
</content></ScrollPane>
<JFXButton fx:id="sendButton" layoutX="350.0" layoutY="338.0" onAction="#clickSendMessage" prefHeight="50.0" prefWidth="64.0" ripplerFill="#18cd00" text="SEND" textAlignment="CENTER" textFill="#0dae04" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="344.0" AnchorPane.rightAnchor="5.0" />
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</AnchorPane>
In my opinion it is bad practice to mix the class which responsability is to start your application and load your .fxml
file(s) with the class which is your Controller
, that cares about controlling your UI, for example in your case you have the clickSendMessage()
method who have to be in a controller class.
So i suggest that you write a separate class that is your Controller , you attach your controller to the .fxml
file and then you can handle much easier all interactions between UI and the logic part. I can show you a simple example:
package stackoverflow;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class TestController implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
// here are all stuffs initialized
}
}
then the .fxml
file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="500.0" prefWidth="700.0" xmlns="http://javafx.com/javafx/8.0.91" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="stackoverflow.TestController">
// here you can fill it with content
</AnchorPane>
So now you have separate a controller and an .fxml
so you can load this .fxml
as you did in the main class then you can add all stuffs in your controller that you want to control your application including that method. This is a so simple and clear way to build a javafx application. I think this is understandable but if i missed something feel free to ask me.
my friend, do you set your PeerApp.fxml's controller to MessageApp too?
if not, when you click send button, it won't trigger the clickSendMessage().
but if you set your PeerApp.fxml's controller to MessageApp, javafx automatically create another instance of MessageApp, so if you click send button, it's actually trigger another instance of MessageApp's clickSendMessage(), and it prints its i variable.
or can you post your PeerApp.fxml code to facilitate us to check?
init() method is override from Application class, it will run automatically when we start our javafx application.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.