据说最近要开发简单的IM工具,于是兴起,研究了下QQ聊天窗口,大概模拟了一下群聊的聊天显示界面,遂与大家分享之
画面粗糙还望海涵
图片有点大,原理基本就是使用listview来显示每一条记录,别人的记录显示在左侧,自己的聊天记录显示在右侧,但是他们公用的一个内容控件
贴下main.qml的代码吧,main本来应该是比较简洁的,这里未来得及整理,有需要大家可以自己去整理
import QtQuick 2.0 import QtQuick.XmlListModel 2.0 Item { width: 400 height: 600 // color: "gray" property XmlListModel tmpModel; property string atContents: "@zhuditingyu" XmlListModel { id: xmlModel source:"qrc:/ybemxml_new.xml" query: "/ybdb/img/item" XmlRole { name: "title"; query: "title/string()" } XmlRole { name: "path"; query: "path/string()" } XmlRole { name: "title2"; query: "title2/string()" } Component.onCompleted: { tmpModel = xmlModel; } } Rectangle{ id:atSomeBody anchors.left: parent.left anchors.right: parent.right anchors.top:parent.top anchors.leftMargin: 12 anchors.rightMargin: 12 anchors.topMargin: 12 height: 30 radius: 10 z:2 color: "gray" opacity: 0.0 Text { id: atName text: atContents // anchors.centerIn: parent horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter width: parent.width height: parent.height } } SequentialAnimation { id:animation NumberAnimation { target: atSomeBody; property: "opacity"; to: 0.8; duration: 500 } PauseAnimation { duration: 2000 } NumberAnimation { target: atSomeBody; property: "opacity"; to: 0; duration: 500 } } ListModel{ id:chatModel // ListElement{ // //cid:x1 // chatName1:"yitb" // chatHead1:"http://img01.nduoa.com/apk/658/658954/icon_72.png" // chatFlag1:0 // chatContents1:"way同学们 都在干嘛呢?" // chatTime1:"1397031550073" // } // ListElement{ // // cid:x2 // chatName1:"yitb2" // chatFlag1:1 // chatHead1:"http://img01.nduoa.com/apk/662/662455/icon_72.png" // chatContents1:"群主有福利啦 what a fuck day" // chatTime1:"1397031550088" // } // ListElement{ // // cid:x3 // chatName1:"yitb3" // chatFlag1:1 // chatHead1:"http://img01.nduoa.com/apk/662/662455/icon_72.png" // chatContents1:"呵呵,笑死我拉what a fuck day" // chatTime1:"1397031550073" // } ListElement{ // cid:x4 chatName1:"yitb" chatFlag1:0 chatHead1:"http://img01.nduoa.com/apk/658/658954/icon_72.png" chatContents1:"都在干嘛呢?不吭声的都木有JJ daywhat a fuck daywhat a fuck day" chatTime1:"1397031550000" } } Rectangle{ id:viewRect anchors.left: parent.left anchors.right: parent.right anchors.top:parent.top anchors.leftMargin: 10 anchors.rightMargin: 10 anchors.topMargin: 10 height: parent.height-200 border.width: 2 border.color: "green" radius: 10 ListView{ id:chatView width: parent.width-10 anchors.centerIn: parent height: parent.height-10 spacing:10 delegate: ChatItem{ chatName: chatName1 chatContents: chatContents1 chatFlag: chatFlag1 chatHead: chatHead1 chatTime: chatTime1 chatIndex: index } clip:true model:chatModel } } ChatBiaoq{ id:biaoq anchors.left: parent.left anchors.leftMargin: 10 anchors.bottom: option.top anchors.bottomMargin: 10 opacity: 0 visible: false onSignalClickCurrentImg: { console.log(imgName+" "+strPath); edit.text += imgName; } } SequentialAnimation { id:animation2 NumberAnimation { target: biaoq; property: "opacity"; to: 0.8; duration: 500 } PauseAnimation { duration: 2000 } //NumberAnimation { target: atSomeBody; property: "opacity"; to: 0; duration: 500 } } SequentialAnimation { id:animation3 NumberAnimation { target: biaoq; property: "opacity"; to: 0.0; duration: 500 } PauseAnimation { duration: 2000 } // NumberAnimation { target: biaoq; property: "visible"; to: f; duration: 500 } } ChatOption{ id:option anchors.left: parent.left anchors.leftMargin: 10 anchors.right: parent.right anchors.rightMargin: 10 anchors.top:viewRect.bottom anchors.topMargin: 10 onSignalSendType: { switch(iType) { case 0: { biaoq.visible =true; animation2.stop(); animation2.start(); }break; case 1: { }break; } } } Rectangle{ id:editRect height: 100; anchors.left: parent.left anchors.leftMargin: 10 anchors.right: parent.right anchors.rightMargin: 5 anchors.top:viewRect.bottom anchors.topMargin: 50 border.width: 1 border.color: "gray" radius: 10 Flickable { id: flick width: parent.width-20 height: parent.height-10 anchors.centerIn: parent contentWidth: edit.paintedWidth contentHeight: edit.paintedHeight clip: true function ensureVisible(r) { if (contentX >= r.x) contentX = r.x; else if (contentX+width <= r.x+r.width) contentX = r.x+r.width-width; if (contentY >= r.y) contentY = r.y; else if (contentY+height <= r.y+r.height) contentY = r.y+r.height-height; } TextEdit { id: edit width: flick.width height: flick.height focus: true wrapMode: TextEdit.Wrap onCursorRectangleChanged: flick.ensureVisible(cursorRectangle) Keys.onReturnPressed: { appendData(edit.text,0); edit.text = ""; chatView.positionViewAtEnd(); animation3.stop(); animation3.start(); biaoq.visible = false; } } } } ChatScrollBar{ id:scBar anchors.right: viewRect.right anchors.rightMargin: 2 anchors.top: viewRect.top anchors.bottom: viewRect.bottom width: 10 scrollColor: "#dbe1e5" opacity: 0 orientation: Qt.Vertical position: chatView.visibleArea.yPosition pageSize: chatView.visibleArea.heightRatio clip: true } states: State { name: "ShowBars" when: chatView.movingVertically || chatView.movingHorizontally PropertyChanges { target: scBar; opacity: 1 } } Rectangle{ id:btn anchors.bottom: parent.bottom anchors.right: parent.right anchors.rightMargin: 10 anchors.bottomMargin: 5 width: 100 height: 30 border.width: 1 border.color: "gray" radius: 5 Text{ id:cli text: "Send" anchors.centerIn: parent } MouseArea{ id:ma anchors.fill: parent enabled: edit.text.length?true:false onClicked: { appendData(edit.text,0); edit.text = ""; chatView.positionViewAtEnd(); animation3.stop(); animation3.start(); biaoq.visible = false; } } } Rectangle{ id:btn1 anchors.bottom: parent.bottom anchors.left: parent.left anchors.leftMargin: 10 anchors.bottomMargin: 5 width: 100 height: 30 border.width: 1 border.color: "gray" radius: 5 Text{ id:cli1 text: "Send" anchors.centerIn: parent } MouseArea{ id:ma1 anchors.fill: parent enabled: edit.text.length?true:false onClicked: { appendData(edit.text,1); edit.text = ""; chatView.positionViewAtEnd(); animation3.stop(); animation3.start(); biaoq.visible = false; } } } function findAtSome(str) { for(var i=0;i<chatModel.count;i++) { var tmp = "@"; tmp += chatModel.get(i).chatName1; console.log("tmp "+tmp +" str" +str); if(str.indexOf(tmp) >= 0 ) { console.log("in it.................") return true; }else{ continue; } } return false; } function appendData(str,iflag) { var tmp = str;//.replace(/[\r\n]/g,""); // var curTime = new Date().UTC(); var curTime=new Date().getTime(); // 结果:1280977330748 console.log(curTime); var isRepeat = findSameTime(curTime.toString()); var tmpTime; if(isRepeat) { tmpTime = ""; }else{ tmpTime = curTime.toString(); } if(iflag == 0) { chatModel.append({"chatName1": "卧栏听雨", "chatHead1":"http://img01.nduoa.com/apk/658/658954/icon_72.png", "chatContents1":tmp, "chatFlag1":0,"chatTime1":tmpTime}); }else{ chatModel.append({"chatName1": "寒山居士", "chatHead1":"http://img01.nduoa.com/apk/662/662455/icon_72.png", "chatContents1":tmp, "chatFlag1":1,"chatTime1":tmpTime}); } if(findAtSome(str)) { animation.stop(); atName.text = str; animation.start(); } } function findSameTime(str) { for(var i=0;i<chatModel.count;i++) { if(isSameTime(chatModel.get(i).chatTime1,str)) { console.log("model time: "+chatModel.get(i).chatTime1) return true; }else{ continue; } } return false; } function getFormatData(msecond,iType) { var dates = new Date((msecond - 0)+ 8*3600);; if(iType == 0) { return dates.getUTCFullYear().toString(); }else if(iType == 1) { return dates.getMonth().toString(); }else if(iType == 2) { return dates.getDate().toString(); }else if(iType == 3) { return dates.getHours().toString(); }else if(iType == 4) { return dates.getMinutes().toString(); } } function isSameTime(strModel,str) { var tmpTime = strModel; console.log(tmpTime+" "+str); if(getFormatData(tmpTime,0) == getFormatData(str,0)) { if(getFormatData(tmpTime,1) == getFormatData(str,1)) { if(getFormatData(tmpTime,2) == getFormatData(str,2)) { if(getFormatData(tmpTime,3) == getFormatData(str,3)) { if(getFormatData(tmpTime,4) == getFormatData(str,4)) { console.log("alsfalkflaslakls is True"); return true; }else{ return false; } } } } return false; }else{ return false; } } function filterImg(str){ for(var i=0;i<tmpModel.count;i++) { var curTitle=tmpModel.get(i).title2; var curPath = "<img src="+tmpModel.get(i).path+"/>"; var reg = new RegExp(curTitle, "ig"); str = str.replace(reg, curPath); } var orgStr =str.replace(/<<(.+?)>>/gi,"<<$1>>"); orgStr.replace(/‘/gi,"‘") console.log("orgStrorgStrorgStr "+orgStr); return orgStr; } }其他文件稍后在附件里面附上 代码
原文:http://blog.csdn.net/esonpo/article/details/23344207