用QML做聊天界面的思路一般都是用ListView来展示聊天记录。我也做了一个简易的聊天框,效果如下:
虽然思路很简单,但是实现时遇到不少QML的Bug,比如滚动时图片闪烁、拉伸时位置/宽度绑定错误(所以我放弃了Layout)等,可能在别的版本还会有其他QML渲染上的Bug。
代码链接:https://github.com/gongjianbo/MyTestCode/tree/master/Qml/TalkList
不同类型的消息我用了不同的delegate,因为要动态选择delegate又使用了Loader进行加载。为了在Loader的sourceComponent里像一般的ListView delegate那样访问model数据,需要把Component声明到Loader里。
delegate: Loader{
sourceComponent: {
switch(model.type){
case TalkData.Text:
return text_comp;
case TalkData.Audio:
return audio_comp;
}
return none_comp;
}
//放到delegate才能attach model
Component{
id: text_comp
TalkItemText{ }
}
Component{
id: audio_comp
TalkItemAudio{ }
}
Component{
id: none_comp
Item{ }
}
}
Text不能选中文本,所以我用TextEdit来展示消息。不过这样也只能选中一个Label里的文本,不能全选。
下面贴两个主体代码,即文本行delegate的代码(完整代码请在上面的github连接下):
code 1.delegate item 基类
import QtQuick 2.12
import QtQuick.Layouts 1.12
//信息delegate的基类
//龚建波 2021-3-29
Item {
id: control
width: parent.ListView.view.width
height: Math.max(profileHeight,
content_item.height)
//profile picture头像
property int profileHeight: 48
property int messageHeight: 48
property int leftWidth: 110
property int rightWidth: 110
property int contentWidth: Math.max(10,control.width-control.leftWidth-control.rightWidth)
property bool isUser: (model.user===model.sender)
property color messageBgColor: control.isUser?"#98E892":"#FFFFFF"
default property alias contentItems: content_item.children
Item{
id: left_item
height: control.height
width: control.leftWidth
//目前头像为固定,动态值可以放model
Image {
visible: !control.isUser
width: control.profileHeight
height: control.profileHeight
anchors.horizontalCenter: parent.horizontalCenter
source: "qrc:/Image/profile_48_gray.png"
//fillMode: Image.PreserveAspectFit
}
}
Column{
id: content_item
x: control.leftWidth
width: control.contentWidth
spacing: 6
//layoutDirection: control.isUser?Qt.RightToLeft:Qt.LeftToRight
}
Item {
id: right_item
height: control.height
width: control.rightWidth
anchors.right: parent.right
//头像没必要放两个,可以动态锚定
//但是区分开后可以写一些差异化的操作
Image {
visible: control.isUser
width: control.profileHeight
height: control.profileHeight
anchors.horizontalCenter: parent.horizontalCenter
source: "qrc:/Image/profile_48_gray.png"
//fillMode: Image.PreserveAspectFit
}
}
//Component.onCompleted: {
// console.log('init',index);
//}
//Component.onDestruction: {
// console.log('free',index);
//}
}
code 2.delegate text
import QtQuick 2.12
//文本信息delegate
//龚建波 2021-3-29
TalkItemBase {
id: control
//ColumnLayout在拉伸时计算有问题,暂时用Column+Row
Row{
width: control.contentWidth
layoutDirection: control.isUser?Qt.RightToLeft:Qt.LeftToRight
Rectangle{
id: wrap_item
radius: 4
width: text_item.width
height: text_item.height
color: control.messageBgColor
//指向发言人小三角
Rectangle{
width: 10
height: 10
y: control.messageHeight/2-10
anchors.horizontalCenter: control.isUser?parent.right:parent.left
rotation: 45
color: control.messageBgColor
}
TalkLabel{
id: text_item
text: model.text_text
width: Math.min(control.contentWidth,textWidth)
}
}
}
Row{
width: control.contentWidth
layoutDirection: control.isUser?Qt.RightToLeft:Qt.LeftToRight
TalkLabel{
text: model.datetime
padding: 0
}
}
}