欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > 25.4.20学习总结

25.4.20学习总结

2025/5/23 2:48:45 来源:https://blog.csdn.net/The_cute_cat/article/details/147376555  浏览:    关键词:25.4.20学习总结

如何使用listView组件来做聊天界面

1. 什么是CellFactory

在JavaFX中,控件(比如ListViewTableView等)用Cell来显示每一条数据。

  • Cell:代表这个单元格(即每个列表项)中显示的内容和样式。

  • CellFactory:是一个工厂接口,负责创建和配置每个Cell

简单来说,

CellFactory用于定义如何将数据对象(比如好友信息)转化为界面显示的单元格(Cell)。


2. 为什么要自定义CellFactory

默认的Cell只显示数据的toString()方法的内容,
如果你需要自定义界面布局(比如显示头像、名字、状态指示等),
就需要自定义Cell,通过实现自己的CellFactory

3. 如何自定义CellFactory

  • 自定义CellFactory的步骤:

    1. 调用listView.setCellFactory()

    2. call()中返回一个自定义的ListCell<T>

    3. 重写updateItem(),设置每个单元格的内容。

详细的步骤:

 1.创建一个消息属性类;

例如,以聊天室的聊天界面为例

import javafx.scene.image.Image;public class TextMessage {private final String content;private final int MessageType;private final String userName;private final Image  profilePicture;private final boolean isSender;public TextMessage(String content, int MessageType, String userName, Image profilePicture, boolean isSender) {this.content = content;this.MessageType = MessageType;this.userName = userName;this.profilePicture = profilePicture;this.isSender = isSender;}public String getContent() {return content;}public int getMessageType() {return MessageType;}public String getUserName() {return userName;}public Image getProfilePicture() {return profilePicture;}public boolean getIsSender() {return isSender;}}

2.创建Cell类(稍后写详细代码)

其中,ListCell后跟着的是你创建的属性类。重写方法后,我们将在else里写具体代码。 

public class ChatTextMessageCell extends ListCell<TextMessage> {@Overrideprotected void updateItem(TextMessage item, boolean empty) {super.updateItem(item, empty);if (empty || item == null) {setText(null);setGraphic(null);} else {}}
}

3.在控制器类里初始化

首先,在类里添加如下代码:

private ObservableList<TextMessage> messages;

其中,ObservableList是 JavaFX 中用于实现数据绑定的重要接口。它允许 UI 控件自动响应数据变化。

将你的listView组件声明一下,如下,

@FXML public ListView<TextMessage> chatList;

初始化方法(注意:以initialize为名的无参方法无论加不加注解都会在应用启动时执行):

@FXML public void initialize(){messages = FXCollections.observableArrayList();chatList.setItems(messages);chatList.setCellFactory(param -> new ChatTextMessageCell());}

 4.具体写Cell类

注意:这个类对于初学者来说并不好写,可能需调整多次才能达到预期。

首先,我们需要知道VBox和HBox的区别。

VBox 和 HBox 的区别:
VBox (Vertical Box):
  • 含义: VBox 是一个垂直布局容器,它将子节点按照垂直方向依次排列。

  • 排列方式: 子节点从上到下堆叠,每个节点占据一行。

  • 常用场景:

    • 创建垂直菜单或工具栏。

    • 在表单中排列标签和输入框。

    • 组织用户信息,例如头像、姓名、联系方式等。

  • 关键属性:

    • alignment: 设置子节点在 VBox 内的对齐方式(例如,居左、居中、居右)。 默认情况下,JavaFX 中的 VBox 采用 TOP_LEFT 对齐,即顶部左对齐。

    • spacing: 设置子节点之间的垂直间距。

HBox (Horizontal Box):
  • 含义: HBox 是一个水平布局容器,它将子节点按照水平方向依次排列。

  • 排列方式: 子节点从左到右排列,每个节点占据一列。

  • 常用场景:

    • 创建水平工具栏。

    • 在窗口顶部或底部排列按钮。

    • 创建自定义组件,例如包含图标和文本的标签。

  • 关键属性:

    • alignment: 设置子节点在 HBox 内的对齐方式(例如,顶部对齐、居中对齐、底部对齐)。 默认情况下,JavaFX 中的 HBox 采用 TOP_LEFT 对齐,即顶部左对齐。

    • spacing: 设置子节点之间的水平间距。

经过上述简单的了解,已经知道了VBox和HBox的区别,我们将在下面可能频繁使用这两个容器。

我们需要聊天界面显示的每条消息由头像,昵称,发出的消息等组成,其中,头像位于最左端或者最右端,昵称和发出的消息共同组成上下结构位于头像的另一端。也就是说,根容器,是需要展示左右两端的,我们使用HBox作为根容器,如下:

HBox root = new HBox();

之后,我们分别声明头像的组件和另一端的容器,又因为另一端是上下结构,我们使用VBox作为容器。如下:

ImageView imageView = new ImageView(item.getProfilePicture());
VBox messageBox = new VBox();

之后,我们将两者放入根容器中,如下:

root.getChildren().addAll(imageView, messageBox);

 之后便是相同的操作了,依次处理上下结构即可。

显然,这样的结果不符合要求,我们还行对组件进行设置。在这便需要各位查阅官方文档了。

记得在最后加上setGraphic(root);应用设置!!!

如何使用(这样就会添加一条):

TextMessage textMessage = new TextMessage(InputArea.getText(), 2, "书雨星", null, false);
messages.add(textMessage);

完整代码引用:

import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Circle;public class ChatTextMessageCell extends ListCell<TextMessage> {@Overrideprotected void updateItem(TextMessage item, boolean empty) {super.updateItem(item, empty);if (empty || item == null) {setText(null);setGraphic(null);} else {HBox root = new HBox();if (item.getMessageType() == 1) {// 时间消息样式保持不变Label timeLabel = new Label(item.getContent());timeLabel.setStyle("-fx-font-size: 10px; -fx-text-fill: gray;");root.getChildren().add(timeLabel);root.setAlignment(Pos.CENTER);} else {// ================== 头像区域 ==================ImageView imageView = new ImageView(item.getProfilePicture());imageView.setFitHeight(40);imageView.setFitWidth(40);imageView.setPreserveRatio(false);imageView.setClip(new Circle(20, 20, 15));// ================== 消息内容区域 ==================VBox messageBox = new VBox();// 昵称标签Label nameLabel = new Label(item.getUserName());nameLabel.setStyle("-fx-font-size: 12px; -fx-text-fill: #666; /*-fx-padding: 0 0 2px 8px;*/");// 消息气泡if(item.getMessageType()==2){HBox bubble = new HBox();bubble.setMaxWidth(200);bubble.setStyle("-fx-background-radius: 15px; " +"-fx-padding: 8px 12px; " +"-fx-background-color: " + (item.getIsSender() ? "#f5f5f5" : "#0099ff") + ";");// 消息文本Label messageLabel = new Label(item.getContent());messageLabel.setStyle("-fx-font-size: 14px; " +"-fx-text-fill: " + (item.getIsSender() ? "black" : "white") + ";");messageLabel.setWrapText(true);// ================== 布局组装 ==================bubble.getChildren().add(messageLabel);messageBox.getChildren().addAll(nameLabel, bubble);// 根容器设置root.setSpacing(5);if (item.getIsSender()) {messageBox.setAlignment(Pos.CENTER_LEFT);root.setAlignment(Pos.CENTER_LEFT);root.getChildren().addAll(imageView, messageBox);} else {messageBox.setAlignment(Pos.CENTER_RIGHT);root.setAlignment(Pos.CENTER_RIGHT);root.getChildren().addAll(messageBox, imageView);}}}setGraphic(root);}}
}

5.将listView的默认样式去除

在你的resources文件中,创建css文件,内容如下:

.list-view { /* 应用于整个ListView */-fx-background-color: WHITE; /* ListView 背景透明 */
}.list-cell {-fx-background-color: transparent; /* 单元格背景透明 */-fx-padding: 0;                  /* 移除默认内边距 */-fx-border: none;                /* 移除边框 */-fx-focus-color: transparent;     /* 移除焦点效果 */-fx-control-inner-background: transparent; /* 解决部分主题背景问题 */-fx-text-fill: black;           /* 默认文本颜色,并保持 */
}.list-cell:hover {-fx-background-color: transparent; /* 悬停时透明 */
}.list-cell:selected {-fx-background-color: transparent; /* 选中时透明 */
}.list-cell:selected:focused {-fx-background-color: transparent; /* 选中并聚焦时透明 */
}.list-cell:pressed {-fx-background-color: transparent; /* 按下时透明 */
}

将此css文件导入即可。

FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 900, 618);
// 加载 CSS 文件
scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("style.css")).toExternalForm());

当然,如果你的界面不止一个ListView组件,那么不能直接导入,因为会被应用到所有ListView组件上,需要将通用选择器替换为自定义类选择器。

步骤如下:

首先,起一个自定义类名,如chat-list-view。

然后,在你的FXML文件的ListView组件后加上styleClass="chat-list-view",引号里的为自定义类名。

如下:

<ListView fx:id="friendList" onMouseClicked="#FriendListAreaClick" prefHeight="558.0" prefWidth="250.0" style="-fx-background-color: #ffffff;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="60.0" AnchorPane.topAnchor="60.0" styleClass="friend-list-view"/>

之后,在之前的css文件的.list-cell前加上.自定义的类名,如下(注意:两者之间必须有空格):

.chat-list-view .list-cell:hover {-fx-background-color: transparent; /* 悬停时透明 */
}

 最后,在initialize方法里加上如下代码:

String cssPath = Objects.requireNonNull(getClass().getResource("/css/Style.css")).toExternalForm();
chatList.getStylesheets().add(cssPath);

其中,"/css/Style.css"为你的css文件在资源文件夹的位置,就是从资源文件夹开始的路径。

chatList为需要指定的ListView组件。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词