本文是仿微信聊天程序专栏的第九篇文章,主要记录了【聊天信息】的逻辑实现,下面涉及代码是《仿微信聊天程序 - 09. 聊天窗口》的基础上进行完善的。
实现效果
在《仿微信聊天程序 - 09. 聊天窗口》章节中,已经实现了基本的聊天界面框架,《09. 聊天信息》这里只是补充实现聊天内容部分功能。
界面布局
聊天内容使用的是WebView来显示,所以这里的布局就是HTML来渲染双方的聊天信息,下面一个简单对话的聊天内容布局代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="main.css">
<script src="main.js"></script>
</head>
<body>
<div id="content">
<!--
<div class="message-item">
<div class="user">
<span class="nickname">自己的昵称</span>
<span>时间</span>
</div>
<div class="content">
这是内容
</div>
</div>
<div class="message-item">
<div class="contacts">
<span class="nickname">好友的昵称</span>
<span>时间</span>
</div>
<div class="content">
这是内容
</div>
</div>
-->
</div>
</body>
</html>
样式美化
聊天内容使用的是HTML渲染聊天内容,所以CSS就是也是HTML标准的CSS,完整的CSS代码如下:
#content {
padding: 15px;
}
.message-item {
padding: 5px 0;
}
.nickname {
margin-right: 8px;
}
.user {
color: green;
}
.contacts {
color: blue;
}
.content {
padding: 5px 0;
}
内容管理
聊天内容的管理使用JavaScript实现的,也就是说通过Java调用JavaScript的代码实现对聊天内容的插入和删除,下面是JavaScript实现的功能代码:
var main = {};
main.append = function (html) {
var ele = document.createElement("div");
ele.innerHTML = html;
document.getElementById("content").appendChild(ele);
window.scrollTo(0, document.body.scrollHeight);
};
main.clear = function () {
document.getElementById("content").innerHTML = "";
};
逻辑控制
调整原来ChatMainController的代码,使用WebView引擎构建JavaScript对象,使用JavaScript对象实现对聊天内容的管理:
/**
* @author michong
*/
public class ChatMainController implements UserDataController {
public Label nicknameLabel;
public WebView chatWebView;
public TextArea messageTextArea;
private JSObject js;
private Long contactsId;
@Override
public void initialize(Object data) {
contactsId = (Long) data;
initializeUI();
initializeEvent();
}
void initializeUI() {
chatWebView.setContextMenuEnabled(false);
chatWebView.setBlendMode(BlendMode.DARKEN);
WebEngine engine = chatWebView.getEngine();
engine.setJavaScriptEnabled(true);
engine.getLoadWorker().stateProperty().addListener((obj, ov, nv) -> {
if (nv == Worker.State.SUCCEEDED) {
js = (JSObject) engine.executeScript("window.main");
renderDebugData();
}
});
engine.load(Objects.requireNonNull(getClass().getResource("/html/main.html")).toExternalForm());
}
void initializeEvent() {
messageTextArea.setOnKeyPressed(e -> {
if (e.isControlDown() && e.getCode() == KeyCode.ENTER) {
onSendClick(null);
}
});
}
void renderDebugData() {
nicknameLabel.setText("WxID: " + contactsId);
MessageVO from = new MessageVO();
from.setNickname("米虫1001");
from.setMessage("仿微信聊天程序源码可以搜索微信小程序“Coding鱼塘”获取下载地址");
from.setTimestamp(new Date().getTime());
append(from, true);
}
void append(MessageVO vo, boolean isContacts) {
js.call("append", Html.of(vo.getNickname(), vo.getTimestamp(), vo.getMessage(), isContacts));
}
public void onClearClick(ActionEvent actionEvent) {
js.call("clear");
}
public void onCloseClick(ActionEvent actionEvent) {
}
public void onSendClick(ActionEvent actionEvent) {
String content = messageTextArea.getText();
if (content.trim().length() == 0) {
return;
}
MessageVO from = new MessageVO();
from.setNickname("我");
from.setMessage(content);
from.setTimestamp(new Date().getTime());
append(from, false);
messageTextArea.clear();
}
}
内容构建
聊天内容构建工具Html:
/**
* @author michong
*/
public class Html {
public static String of(String nickname, long timestamp, String content, boolean isContacts) {
String time = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date(timestamp));
// @formatter:off
return "<div class='message-item'>"
+ "<div class='" + (isContacts ? "user" : "contacts") + "'>"
+ "<span class='nickname'>" + nickname + "</span>"
+ "<span>" + time + "</span>"
+ "</div>"
+ "<div class='content'>" + escape(content) + "</div>"
+ "</div>";
// @formatter:on
}
private static String escape(String content) {
// @formatter:off
return content.replace("&", "&")
.replace("\t", " ")
.replace("<", "<")
.replace(">", ">")
.replace(" ", " ")
.replace("'", "'")
.replace("\"", """)
.replace("\r\n", "\n")
.replace("\n", "<br>");
// @formatter:on
}
}