本文是仿微信聊天程序专栏的第五篇文章,主要记录了【聊天列表】的界面实现。
界面设计
聊天列表在主界面左边,主要显示最近的聊天记录,以及添加好友的信息等,总体界面设计如下:
界面布局
聊天列表的布局分为两部,列表布局和列表中的每一行的布局,其中列表布局的完整fxml如下:
<HBox prefHeight="640.0" prefWidth="250.0" xmlns:fx="http://javafx.com/fxml" stylesheets="@ChatListController.css" fx:controller="michong.javafx.wx.view.chat.ChatListController"> <children> <VBox prefWidth="250.0"> <children> <HBox prefHeight="70.0" prefWidth="200.0" spacing="10.0"> <children> <TextField prefHeight="30.0" HBox.hgrow="ALWAYS" promptText="聊天搜索"/> <Button prefHeight="30.0" prefWidth="30.0" text="+" onAction="#onApplyClick"/> </children> <padding> <Insets left="10.0" right="10.0" top="20.0"/> </padding> </HBox> <ListView fx:id="chatListView" VBox.vgrow="ALWAYS"/> </children> </VBox> </children> </HBox>
列表项的布局完整fxml如下:
<HBox xmlns:fx="http://javafx.com/fxml" stylesheets="@ChatRowController.css" alignment="CENTER" prefHeight="60.0" prefWidth="235.0" spacing="5.0" fx:id="chatRowHBox"> <children> <Pane prefHeight="60.0" prefWidth="48.0"> <children> <ImageView fx:id="avatarImageView" fitHeight="40.0" fitWidth="40.0" layoutX="5.0" layoutY="10.0"/> <Label fx:id="messageCountLabel" layoutX="35.0" prefHeight="20.0" styleClass="message-count-label"/> </children> </Pane> <VBox prefWidth="116.0" spacing="4.0" HBox.hgrow="ALWAYS"> <children> <Label fx:id="nicknameLabel" styleClass="name-label"/> <Label fx:id="messageLabel" styleClass="message-label"/> </children> <padding> <Insets bottom="5.0" left="5.0" right="5.0" top="8.0"/> </padding> </VBox> <VBox alignment="TOP_RIGHT" prefWidth="85.0"> <children> <Label fx:id="timestampLabel" styleClass="timestamp-label"/> </children> <padding> <Insets bottom="5.0" left="5.0" right="5.0" top="10.0"/> </padding> </VBox> </children> </HBox>
构建控件
从上面的界面布局中可以看到列表项并没有直接关联Controller,因为这里我实现动态构建,所以在初始化的时候绑定Controller,具体实现如下:
/** * @author michong */ public class ChatRowController extends ListCell<MessageVO> { public HBox chatRowHBox; public ImageView avatarImageView; public Label messageCountLabel; public Label nicknameLabel; public Label messageLabel; public Label timestampLabel; FXMLLoader loader; @Override protected void updateItem(MessageVO item, boolean empty) { super.updateItem(item, empty); if (empty || Objects.isNull(item)) { setGraphic(null); return; } if (Objects.isNull(loader)) { loader = new FXMLLoader(getClass().getResource("/" + getClass().getName().replace(".", "/") + ".fxml")); loader.setController(this); try { loader.load(); } catch (IOException e) { throw new RuntimeException(e); } } if (item.getSource() == MessageSource.APPLY) { avatarImageView.setImage(FXAvatar.apply()); nicknameLabel.setText("新朋友"); messageLabel.setText(item.getNickname() + " 申请添加你为好友。"); } else { avatarImageView.setImage(FXAvatar.load(item.getAvatar())); nicknameLabel.setText(item.getNickname()); messageLabel.setText(item.getMessage()); } timestampLabel.setText(new SimpleDateFormat("yy/MM/dd").format(new Date(item.getTimestamp()))); messageCountLabel.setText(item.getMessageCount() > 99 ? "99+" : String.valueOf(item.getMessageCount())); messageCountLabel.setLayoutX(item.getMessageCount() > 0 ? 35 : -35); setGraphic(chatRowHBox); } }
样式美化
聊天列表使用的是JavaFX的ListView控件,默认情况下,这个控件不是很美观,所以需要对其进行样式美化,完整的css如下:
* { -fx-font-family: "微软雅黑", "sans-serif"; -fx-font-size: 14px; -fx-border-radius: 0; -fx-background-radius: 0; } .list-view { -fx-background-color: transparent; } .list-cell { -fx-background-color: #f4f4f4; -fx-text-fill: black; } .list-cell:empty { visibility: hidden; } .list-cell:hover { -fx-background-color: derive(#c8c8c8, 50%); } .list-cell:filled:selected:focused { -fx-background-color: #c8c8c8; -fx-text-fill: black; } .split-pane > .split-pane-divider { -fx-background-color: transparent; } .menu-button > .arrow-button { -fx-padding: 0; } .menu-button > .arrow-button > .arrow { -fx-padding: 0; } .track { -fx-background-color: transparent; } .thumb { -fx-pref-width: 10px; -fx-background-color: derive(#969696, 50%); -fx-background-radius: 0em; } .increment-arrow, .decrement-arrow { -fx-padding: 0; } .increment-button, .decrement-button { -fx-padding: 0 0 0 0; } .scroll-bar { -fx-opacity: 0; } :hover .scroll-bar { -fx-opacity: 1; } .list-view .scroll-bar, .text-area .scroll-bar { -fx-background-color: transparent; } .list-view .decrement-button { -fx-padding: 0 10 0 0; }
事件处理
重新实现第四篇【主界面】中的切换列表事件:
public void onChatClick(ActionEvent actionEvent) { listVBox.getChildren().clear(); listVBox.getChildren().add(FXComponent.chatListController()); chatButton.setDisable(true); contactsButton.setDisable(false); }
缓存控件逻辑:
public static Parent chatListController() { return cache.computeIfAbsent("chatListController", k -> FX.fxml(ChatListController.class)); }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库