Create the ChatClient GUI

Posted on 2021-11-28 21:08  Capterlliar  阅读(160)  评论(0编辑  收藏  举报

写一个(自己和自己)聊天的界面。

效果图:

 

1.导入JavaFX包

  具体可以看这个教程

2.基础界面

  JavaFX的基本结构大致如下:

public class ChatClient extends Application {  //继承javafx.application.Application
    public void start(Stage primaryStage){//重载start,主窗体(舞台)
        //进行一些编绘
        primaryStage.show();//展示
    }
    public static void main(String[] args){
        Application.launch(args);
    }//main可以不写
}

  因为这个界面要求有左边栏,底部输入部分,所以选择BorderPane,大概位置长这样,不用的部分会自动补齐:

 

  各个部分里选择V(ertical)Box,也就是各个组件竖直排列。在左侧边栏加上两个按钮,VBox的插入方法是getChilden:

 Button[] bt={new Button("Send"),new Button("Quit")};
for(Button i:bt){
           VBox.setMargin(i,new Insets(0,0,20,0));//留出一点间隙,顺序为上右下左
           vBox.getChildren().add(i);
       }

  给这个VBox加点绿(有点像css?):

Box.setStyle("-fx-padding: 10;" +
                "-fx-border-style: solid inside;" +
                "-fx-border-width: 1;" +
                "-fx-border-color: green;");

  在下面加一个文本输入框:

private TextArea t=new TextArea();
t.setWrapText(true);//这样输入较多时可以自动换行
t.setPrefHeight(20);//初始高度20

  右边加一个对话框,希望它可以使文字自动换行,并且能滚动,不会把下面输入框挤没,选用ScrollPane。

ScrollPane sp=new ScrollPane();
sp.setContent(vBox2=getVBox2());//ScrollPane似乎不能添加多条内容,因此在里面加一个VBox

  把它们放到场景里,再放到舞台上:

        BorderPane pane=new BorderPane();

        sp.setContent(vBox2=getVBox2());
        pane.setCenter(sp);
        pane.setRight(vBox=getVBox());//一些VBox的初始设置
        t.setWrapText(true);
        t.setPrefHeight(20);
        pane.setBottom(t);

        Scene scene =new Scene(pane,200,250);
        primaryStage.setTitle("ChatRoom");//起个名字
        primaryStage.setScene(scene);
        primaryStage.setWidth(500);//一打开时的宽度
        primaryStage.setMinHeight(200);//最小高度,不要把输入框挤跑
        primaryStage.show();    

  现在就有一个界面了,接下来为它绑定函数。

  首先绑定退出,这个好说,exit(0)嘛qwq。用lambda表达式,就不用写函数了,起名困难症的福音。

bt[1].setOnAction((actionEvent -> {System.exit(0);}));

  至于send按钮,我希望点击它和按回车都能把消息发出去,因此要绑定两个动作,但函数是一个,可以拎出来单独写,就不用写两遍了。

    bt[0].setOnAction(actionEvent -> sendMessage());
       t.setOnKeyPressed(Keyevent -> {
           if(Keyevent.getCode()== KeyCode.ENTER) //按回车才能发出去,有张名称和按键对照表的
          sendMessage(); });

  最后写sendMessage函数。自动换行用TextFlow设定宽度实现。把String套进Text,把Text套进TextFlow,再把TextFlow套进VBox。(仔细想想还有VBox套进Pane,Pane套进Scene,Scene套进Stage。套娃之王FX,厉害,厉害(掏出单片眼镜.jpg))

private void sendMessage(){
        String message=t.getText();//从文本框里获得信息
        Text temp=new Text(message);//开始套娃
        temp.setFont(Font.font(15));//设置一个字体大小
        TextFlow l=new TextFlow(temp);
        double width=sp.getWidth();
        l.setPrefWidth(width);//获取当前VBox宽度,这样大小变化后还能接着用
        vBox2.getChildren().add(l);
        sp.vvalueProperty().bind(vBox2.heightProperty());//保证每次输完信息进度条都在最下面,不用手动翻。
        t.clear();//清空输入框
    }

  关于每次进度条在最下面,可以看这个贴子

  到这里就能正常用了。后面还要加点东西。先把这些打了省得明天忘了。

 

--TBC--             

    -----------------------------------------------分割线:一些做的时候折腾的奇怪东西-------------------------------------------

  一开始是没有想到用ScrollPane的,折腾了一大通。最后去楼下和软院队友打cf的时候问怎么搞,zwq跟我讲,有个进度条那种的啊。

  我:好的。

  先整了个双端队列,消息条数超过目前长度时队首出队,也就是说每次都要清空重来。那么问题来了,什么时候超出长度呢。给字体设了个大小,15,然后每次除以15。一调发现高度是300多,照这个大小下去能放20行,显然翻车。想想300多单位应该是px,那15是什么。搜了一下发现是外国人用的磅,对照表在这里。

  我是对照表

  好那再除21吧!问题解决了。然而长度还会超,做个聊天框怎么这么复杂微信我不该每天辱骂你的。又是一通乱搜后发现用TextFlow很舒服,但是每两条消息之间有空行。我不想算距离了,我算不来,我最不会算数了每次看1-10中间有几个数都要掰手指头qaq。

  但还是收获了一些奇怪的东西:

  •   清除VBox内容:vBox.getChildren.clear();
  •   获取一个字符串所占像素,虽然这是Swing但混在里面也能跑就是了:
        JLabel label=new JLabel("2333333");
        FontMetrics metrics = label.getFontMetrics(label.getFont());
        int textH = metrics.getHeight();
        int textW = metrics.stringWidth(label.getText());    

-------------------------------------------------结束分割线------------------------------------------------------------------------------

  进行一些更新。

  今天新加功能:

    • 选择背景颜色
    • 选择昵称
    • 一些没什么用的菜单

  先从选择昵称开始。

  在按钮下加一个选择框ChoiceBox:

       ChoiceBox<String> choiceBox=getChoiceBox();
       VBox.setMargin(choiceBox,new Insets(0,0,20,0));
       vBox.getChildren().add(choiceBox);

  现在来设置选择框里的信息:

private ChoiceBox<String> getChoiceBox(){
        ChoiceBox<String> cb = new ChoiceBox<>();
        ObservableList<String> nameList=FXCollections.observableArrayList();
        nameList.add("Lapp");
        nameList.add("Licia");
        cb.setItems(nameList);//这个函数要求FXCollections,没别的。
        //官方文档说法:suitable for methods that require ObservableList on input
        cb.setTooltip(new Tooltip("your nickname"));//这样鼠标停在上面会有信息,如下图
        return cb;
    }

小黑框挺好看的

 

 

  开一个全局变量(可能正经程序会传参吧但我懒)记录当前说话的人,所以要给这个选择框绑定一个程序返回当前选择内容:

cb.getSelectionModel().selectedIndexProperty().addListener(((observableValue, number, t1) -> {
            name= nameList.get(t1.intValue());
        }));

  这里依然使用lambda表达式,observableValue很多地方简写为ov,不知道是干嘛的反正先写着,number值根据调试结果我猜是选择了几次,从-1开始累加也不知道有什么用,最后一个表示列表中选择元素下标,从0开始计数。也是下面要用的东西。

  获得名字后对message进行修改,加上说话人的昵称;如果没选择,可以弹出一个警告:

        if(Objects.equals(name, "")){
            Alert alert=new Alert(Alert.AlertType.WARNING);
            alert.setHeaderText("Nameeeee!");//顶部信息
            alert.setContentText("Please select a nick name!");//具体内容
            alert.showAndWait();//等着点确定
            return;
        }
        Label n=new Label(name);
        n.setFont(Font.font(10));//字体调小一点
        fp.getChildren().add(n);    

  这一部分搞完了,接下来弄菜单。

pane.setTop(menuBar=getMenuBar());

  又开始套娃,MenuBar套Menu,Menu套CheckMenuItem,Menu也可以套Menu。

private MenuBar getMenuBar(){
        MenuBar menuBar=new MenuBar();

        Menu menuFile = new Menu("File");
        CheckMenuItem quit=new CheckMenuItem("Quit");
        quit.setOnAction(actionEvent -> {System.exit(0);});//绑个退出
        menuFile.getItems().add(quit);//单独一个套进去

        Menu color=new Menu("Color");
        CheckMenuItem white=new CheckMenuItem("Beautiful white");
        CheckMenuItem green=new CheckMenuItem("Also beautiful green");
        color.getItems().addAll(green,white);//多个一起套进去

        menuFile.getItems().add(color);

        Menu menuHelp = new Menu("Help");
        CheckMenuItem about=new CheckMenuItem("About");
        about.setOnAction(actionEvent -> {
            Alert alert=new Alert(Alert.AlertType.INFORMATION);
            alert.setHeight(500);
            alert.setTitle("About the chatroom");
            alert.setHeaderText("Look here!");
            alert.setContentText("About the chatroom?That's not important at all.Just enjoy time here!");
            alert.showAndWait();
        });//一些奇怪的about
        menuHelp.getItems().add(about);

        menuBar.getMenus().addAll(menuFile, menuHelp);
        return menuBar;
    }

  现在菜单也有了。哦MenuBar记得选javafx的,选到awt就完蛋了。

  最后改改颜色。决定给它整套可以换的皮肤出来(妈,我要把这玩意染成绿的.jpg)。然后被整了一上午。

  ScrollPane不知道为什么css对它没用,听说可以设成透明的但试了试不行。而且BorderPane,top和bottom是左右撑满,left和right左右贴紧,中间centre靠着左右,但上下不紧贴,最后背景一坨一坨的分外诡异。

   先说ScrollPane背景。可以在里面塞一个FlowPane然后设置它的背景色,原来塞的VBox,其实两个都行,但在尝试的过程中换成FlowPane了。

   然后设置FlowPane初始大小。和外层sp绑定一下。外层sp要和BorderPane绑,害。

        sp.prefHeightProperty().bind(pane.heightProperty());
        sp.prefWidthProperty().bind(pane.widthProperty());    
        flowPane.prefHeightProperty().bind(sp.heightProperty());
        flowPane.prefWidthProperty().bind(sp.widthProperty());

  加一些css:

    String style1="-fx-padding: 10;" +
            "-fx-border-style: solid inside;" +
            "-fx-border-width: 1;" +
            "-fx-border-color: green;";
    String style2="-fx-padding: 10;" +
            "-fx-border-style: solid inside;" +
            "-fx-border-width: 1;" +
            "-fx-border-color: green;" +
            "-fx-background-color:rgb(119, 192, 75, .9);";
    green.setOnAction(actionEvent -> {
            vBox.setStyle(style2);
            fp.setStyle("-fx-padding:10;"+
                    "-fx-background-color:rgb(119, 192, 75, .9);");
        });
    white.setOnAction(actionEvent -> {
            vBox.setStyle(style1);
            fp.setStyle("-fx-padding:10;"+
                    "-fx-background-color:white;");
        });

  这就行了。

 

  完整代码(应该不会被查水表吧):

  1 import javafx.application.Application;
  2 import javafx.collections.FXCollections;
  3 import javafx.collections.ObservableList;
  4 import javafx.geometry.Insets;
  5 import javafx.scene.Scene;
  6 import javafx.scene.control.*;
  7 import javafx.scene.input.KeyCode;
  8 import javafx.scene.layout.BorderPane;
  9 import javafx.scene.layout.FlowPane;
 10 import javafx.scene.layout.VBox;
 11 import javafx.scene.text.Font;
 12 import javafx.scene.text.Text;
 13 import javafx.scene.text.TextFlow;
 14 import javafx.stage.Stage;
 15 
 16 import java.util.Objects;
 17 
 18 
 19 public class ChatClient extends Application {
 20     Scene scene;
 21     BorderPane pane;
 22     TextArea t=new TextArea();
 23     VBox vBox;
 24     FlowPane fp;
 25     ScrollPane sp=new ScrollPane();
 26     javafx.scene.control.MenuBar menuBar=new javafx.scene.control.MenuBar();
 27     String name="";
 28     String style1="-fx-padding: 10;" +
 29             "-fx-border-style: solid inside;" +
 30             "-fx-border-width: 1;" +
 31             "-fx-border-color: green;";
 32     String style2="-fx-padding: 10;" +
 33             "-fx-border-style: solid inside;" +
 34             "-fx-border-width: 1;" +
 35             "-fx-border-color: green;" +
 36             "-fx-background-color:rgb(119, 192, 75, .9);";
 37     private void sendMessage(){
 38         if(Objects.equals(name, "")){
 39             Alert alert=new Alert(Alert.AlertType.WARNING);
 40             alert.setHeaderText("Nameeeee!");
 41             alert.setContentText("Please select a nick name!");
 42             alert.showAndWait();
 43             return;
 44         }
 45         String message=t.getText();
 46         Text temp=new Text(message);
 47         temp.setFont(Font.font(15));
 48         TextFlow l=new TextFlow(temp);
 49         double width=sp.getWidth();
 50         l.setPrefWidth(width);
 51 
 52         Label n=new Label(name);
 53         n.setFont(Font.font(10));
 54         fp.getChildren().add(n);
 55         fp.getChildren().add(l);
 56         sp.vvalueProperty().bind(fp.heightProperty());
 57         t.clear();
 58     }
 59     private ChoiceBox<String> getChoiceBox(){
 60         ChoiceBox<String> cb = new ChoiceBox<>();
 61         ObservableList<String> nameList=FXCollections.observableArrayList();
 62         nameList.add("Lapp");
 63         nameList.add("Licia");
 64         cb.setItems(nameList);
 65         cb.setTooltip(new Tooltip("your nickname"));
 66         cb.getSelectionModel().selectedIndexProperty().addListener(((observableValue, number, t1) -> {
 67             name= nameList.get(t1.intValue());
 68         }));
 69         return cb;
 70     }
 71     private VBox getVBox(){
 72        VBox vBox=new VBox();
 73        vBox.setStyle(style1);
 74        Button[] bt={new Button("Send"),new Button("Quit")};
 75        bt[0].setOnAction(actionEvent -> sendMessage());
 76        t.setOnKeyPressed(Keyevent -> {
 77            if(Keyevent.getCode()== KeyCode.ENTER) sendMessage();
 78        });
 79        bt[1].setOnAction((actionEvent -> {System.exit(0);}));
 80        for(Button i:bt){
 81            VBox.setMargin(i,new Insets(0,0,20,0));
 82            vBox.getChildren().add(i);
 83        }
 84        ChoiceBox<String> choiceBox=getChoiceBox();
 85        VBox.setMargin(choiceBox,new Insets(0,0,20,0));
 86        vBox.getChildren().add(choiceBox);
 87        return vBox;
 88     }
 89     private FlowPane getFlowPane(){
 90         FlowPane flowPane=new FlowPane();
 91         flowPane.setStyle("-fx-padding:10");
 92         flowPane.prefHeightProperty().bind(sp.heightProperty());
 93         flowPane.prefWidthProperty().bind(sp.widthProperty());
 94         return flowPane;
 95     }
 96     private MenuBar getMenuBar(){
 97         MenuBar menuBar=new MenuBar();
 98 
 99         Menu menuFile = new Menu("File");
100         CheckMenuItem quit=new CheckMenuItem("Quit");
101         quit.setOnAction(actionEvent -> {System.exit(0);});
102         menuFile.getItems().add(quit);
103 
104         Menu color=new Menu("Color");
105         CheckMenuItem white=new CheckMenuItem("Beautiful white");
106         CheckMenuItem green=new CheckMenuItem("Also beautiful green");
107         green.setOnAction(actionEvent -> {
108             vBox.setStyle(style2);
109             fp.setStyle("-fx-padding:10;"+
110                     "-fx-background-color:rgb(119, 192, 75, .9);");
111         });
112         white.setOnAction(actionEvent -> {
113             vBox.setStyle(style1);
114             fp.setStyle("-fx-padding:10;"+
115                     "-fx-background-color:white;");
116         });
117         color.getItems().addAll(green,white);
118 
119         menuFile.getItems().add(color);
120 
121         Menu menuHelp = new Menu("Help");
122         CheckMenuItem about=new CheckMenuItem("About");
123         about.setOnAction(actionEvent -> {
124             Alert alert=new Alert(Alert.AlertType.INFORMATION);
125             alert.setHeight(500);
126             alert.setTitle("About the chatroom");
127             alert.setHeaderText("Look here!");
128             alert.setContentText("About the chatroom?That's not important at all.Just enjoy time here!");
129             alert.showAndWait();
130         });
131         menuHelp.getItems().add(about);
132 
133         menuBar.getMenus().addAll(menuFile, menuHelp);
134         return menuBar;
135     }
136     public void start(Stage primaryStage){
137         pane=new BorderPane();
138 
139         pane.setCenter(sp);
140         sp.prefHeightProperty().bind(pane.heightProperty());
141         sp.prefWidthProperty().bind(pane.widthProperty());
142         sp.setContent(fp=getFlowPane());
143 
144         pane.setRight(vBox=getVBox());
145 
146         t.setWrapText(true);
147         t.setPrefHeight(20);
148         pane.setBottom(t);
149 
150         pane.setTop(menuBar=getMenuBar());
151 
152         scene =new Scene(pane,200,250);
153         primaryStage.setTitle("ChatRoom");
154         primaryStage.setScene(scene);
155         primaryStage.setWidth(500);
156         primaryStage.setMinHeight(200);
157         primaryStage.show();
158     }
159     public static void main(String[] args){
160         Application.launch(args);
161     }
162 }
View Code

   

---END---