简体   繁体   中英

Where do I have to initialize class member variables JavaFX

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM