JavaFX WhatApp式的ConversationView [英] JavaFX WhatApp-Like ConversationView

查看:68
本文介绍了JavaFX WhatApp式的ConversationView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在JavaFX中制作类似于WhatsApp的对话视图.

I'm trying to make a WhatsApp-Like Conversation-View in JavaFX.

为了使已发送的邮件显示在右侧,而已接收的邮件显示在左侧,则不能使用TextArea.我该怎么做?我尝试了不带TextArea的GridPane,但并没有使事情变得更容易.

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.

这是我的代码:

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();
}

推荐答案

您可以创建一个自定义控件来确定消息的对齐方式和外观,例如气泡外观.作为HBoxVBox的粉丝,我建议将它们与SVGPath结合使用来装饰消息.

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允许您通过提供有关直线,圆弧等的信息来绘制自定义形状.这些并不是Java特有的,因此有一些资源可用于查看一些基本/高级示例.我的建议是在这里阅读: SVGPath 并使用

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:


当布置消息时,VBox就足够了.您可以将可见子项绑定到ObservableList消息,以便以后进行迭代.这样做的另一个好处是,添加到列表中将自动更新用户界面,并且在实现其他功能(如删除,转发等)的情况下,您以后也可以对其进行迭代.


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.

消息/语音气泡:

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);
    }
}

对话窗口:

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));
    }
}

输出:

这篇关于JavaFX WhatApp式的ConversationView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆