本文是仿微信聊天程序专栏的第四篇文章,主要记录了【主界面】的实现。
界面设计
仿微信聊天程序的主界面总体跟微信的主界面差不多,但是比微信的主界面简单得多,如下图所示:
界面布局
主界面整个界面采用HBox布局,分为左中右三个部分,最左边是菜单栏,中间是联系人列表或对话信息列表,最右边是聊天窗口或设置信息窗口,完整的fxml如下:
<HBox prefHeight="640.0"
prefWidth="860.0" stylesheets="@MainController.css" xmlns:fx="http://javafx.com/fxml"
fx:controller="michong.javafx.wx.view.main.MainController">
<children>
<VBox alignment="CENTER" prefWidth="64.0" spacing="30.0" styleClass="left-menu">
<children>
<ImageView fx:id="avatarImageView" fitWidth="40" fitHeight="40"/>
<ToggleButton fx:id="chatButton" selected="true" styleClass="chat-button"
onAction="#onChatClick"/>
<ToggleButton fx:id="contactsButton" styleClass="contacts-button" onAction="#onContactsClick"/>
<Pane VBox.vgrow="ALWAYS"/>
<Button styleClass="setting-button"/>
</children>
<padding>
<Insets bottom="20.0" top="20.0"/>
</padding>
</VBox>
<VBox fx:id="listVBox" prefWidth="250.0" alignment="CENTER"/>
<VBox HBox.hgrow="ALWAYS">
<children>
<HBox prefHeight="30.0" styleClass="top-menu">
<children>
<Pane HBox.hgrow="ALWAYS"/>
<Button onAction="#onMinimizeClick" styleClass="minimize-button"/>
<Button onAction="#onExitClick" styleClass="exit-button"/>
</children>
</HBox>
<Pane fx:id="mainPane" VBox.vgrow="ALWAYS">
</Pane>
</children>
</VBox>
</children>
</HBox>
样式优化
在界面设计的图中,可以看到主界面针对一些按钮进行了样式优化,采用图片代替原始的按钮样式,具体的css样式如下:
.contacts-button {
-fx-cursor: hand;
-fx-background-color: transparent;
-fx-border-color: transparent;
-fx-pref-width: 32px;
-fx-pref-height: 32px;
-fx-background-size: 32px 32px;
-fx-background-image: url("/icon/contacts_0.png");
}
.contacts-button:selected {
-fx-background-image: url("/icon/contacts_1.png");
}
事件处理
主界面的事件主要涉及两个部分,菜单的切换以及最小化和关闭事件处理,具体的处理代码如下:
public class MainController implements Initializable {
public ImageView avatarImageView;
public ToggleButton chatButton;
public ToggleButton contactsButton;
public VBox listVBox;
public Pane mainPane;
@Override
public void initialize(URL location, ResourceBundle resources) {
initializeUI();
initializeEvent();
}
void initializeUI() {
ToggleGroup leftMenuToggleGroup = new ToggleGroup();
chatButton.setToggleGroup(leftMenuToggleGroup);
contactsButton.setToggleGroup(leftMenuToggleGroup);
onChatClick(null);
}
void initializeEvent() {
}
public void onChatClick(ActionEvent actionEvent) {
listVBox.getChildren().clear();
listVBox.getChildren().add(FXComponent.chatListController());
}
public void onContactsClick(ActionEvent actionEvent) {
listVBox.getChildren().clear();
listVBox.getChildren().add(FXComponent.contactsListController());
}
public void onMinimizeClick(ActionEvent actionEvent) {
FXContext.getPrimaryStage().setIconified(true);
}
public void onExitClick(ActionEvent actionEvent) {
Optional.ofNullable(FXContext.getPrimaryStage().getOnCloseRequest()).ifPresent(
e -> e.handle(new WindowEvent(FXContext.getPrimaryStage(), WindowEvent.WINDOW_CLOSE_REQUEST)));
FXContext.getPrimaryStage().close();
}
}
拖动支持
从界面设计图中可以看出,主界面抛弃了JavaFX原始的窗体外壳,自定义了最小化和关闭按钮来实现这些功能,具体的窗体配置如下:
primaryStage.initStyle(StageStyle.TRANSPARENT);
将窗体设置为StageStyle.TRANSPARENT后,需要自己实现窗体的拖动功能:
Parent root = FX.fxml(MainController.class);
FX.drag(primaryStage, root);
其中FX.drag的实现如下:
public static void drag(Stage stage, Parent root) {
EventHandler<MouseEvent> eventHandler = new EventHandler<MouseEvent>() {
private double xOffset = 0;
private double yOffset = 0;
@Override
public void handle(MouseEvent event) {
if (event.getEventType() == MouseEvent.MOUSE_PRESSED) {
xOffset = event.getSceneX();
yOffset = event.getSceneY();
} else if (event.getEventType() == MouseEvent.MOUSE_DRAGGED) {
stage.setX(event.getScreenX() - xOffset);
if (event.getScreenY() - yOffset < 0) {
stage.setY(0);
} else {
stage.setY(event.getScreenY() - yOffset);
}
}
}
};
root.setOnMousePressed(eventHandler);
root.setOnMouseDragged(eventHandler);
}
登录流程
调整原来的登录流程,登录成功后关闭登录窗口,显示主窗口:
public void onLoginClick(ActionEvent actionEvent) {
form.getForm().persist();
if (form.getForm().isValid()) {
FXContext.getLoginStage().close();
FXContext.getPrimaryStage().show();
}
}