写一个(自己和自己)聊天的界面。
效果图:
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;"); });
这就行了。
完整代码(应该不会被查水表吧):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
---END---