Mina airQQ聊天 客户端篇(三)

来源:互联网 发布:stc正在检测目标单片机 编辑:程序博客网 时间:2024/05/16 14:09

开发工具 (FlashBuilder4.7)

程序类型(Adobe Air)

Flex Air做的桌面程序,效果还挺好看的,最主要是Socket这一块,它也是异步的,并且在Flex中的事件机制比较强大(个人认为)

有改一些样式,重新看看新的效果吧:




大致的实现方式:

在WindowedApplication中包含登陆窗口和主界面,用Flex中的状态来切换,聊天窗口时Window组件,好友列表用树菜单

实现好友分组,好友上线时改成在线图标,收到消息时头像抖动,聊天显示实现图文混排,系统托盘,其它貌似没了。

看下客户端的具体实现吧:

首先我们把Flex air的默认窗口样式改一改

打开 (项目名称-app.xml)文件,把注释去掉对应改成如下
<systemChrome>none</systemChrome>
<transparent>true</transparent>
上面是把默认的样式去掉了,然后新建一个WindowedApplication的皮肤,如下

WindowAppSkin.xml

<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009"    xmlns:s="library://ns.adobe.com/flex/spark"  xmlns:fb="http://ns.adobe.com/flashbuilder/2009" alpha.disabledGroup="0.5" ><fx:Metadata>[HostComponent("spark.components.WindowedApplication")]</fx:Metadata><s:states><s:State name="normal" /><s:State name="disabled" stateGroups="disabledGroup" /><s:State name="normalAndInactive" stateGroups="inactiveGroup" /><s:State name="disabledAndInactive" stateGroups="disabledGroup, inactiveGroup" /></s:states><s:Rect width="100%" height="100%" alpha="0"></s:Rect><s:Rect horizontalCenter="0" verticalCenter="0" width="{this.width-20}" height="{this.height-20}"topLeftRadiusX="6" topRightRadiusX="6"><s:fill><s:SolidColor color="#EBF2F9"/></s:fill><!-- 边框发光效果 --><s:filters><s:GlowFilter color="0x000000" alpha="0.6" blurX="7" blurY="7" strength="1" inner="false" quality="3" knockout="false"/></s:filters></s:Rect><!-- 主题内容 --><s:Group id="contentGroup" top="10" left="10" right="10" bottom="10"/></s:SparkSkin>

然后在主程序中引用这个皮肤 skinClass=“WindowAppSkin” ,这个皮肤文件我们在聊天窗口文件也可以用到

接下来看看主程序(WindowedApplication)的代码,它包含了登陆窗口和主界面,然后通过状态来切换界面显示

chat.mxml

<?xml version="1.0" encoding="utf-8"?><s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"   xmlns:s="library://ns.adobe.com/flex/spark"   xmlns:mx="library://ns.adobe.com/flex/mx"   skinClass="com.bufoon.skins.WindowAppSkin"   width="450" height="350" initialize="init(event)" xmlns:components="com.bufoon.components.*"><fx:Style>  @namespace s "library://ns.adobe.com/flex/spark";  @namespace mx "library://ns.adobe.com/flex/mx";  .treeStyle{  /*去掉默认文件夹图标*/  folderClosedIcon: ClassReference(null);  folderOpenIcon: ClassReference(null);  /*去掉叶子节点图标*/  defaultLeafIcon: ClassReference(null);  textIndent:5;leading:5;/*  defaultLeafIcon    指定叶图标  disclosureClosedIcon    指定的图标旁边显示一个封闭的分支节点。默认的图标是一个黑色三角形。  disclosureOpenIcon    指定的图标旁边显示一个开放的分支节点。默认的图标是一个黑色三角形。  folderClosedIcon    关闭指定的文件夹图标的一个分支节点。  folderOpenIcon    指定打开的文件夹图标的一个分支节点。  例:三角图标修改如下代码使用即可换成自己的了:  disclosureOpenIcon:Embed(source='images/a.png');  disclosureClosedIcon:Embed(source='images/b.png');  */  }  </fx:Style>  <fx:Script><![CDATA[import com.adobe.serialization.json.JSON;import com.bufoon.components.chatWindow;import com.bufoon.socket.CustomeSocket;import com.bufoon.socket.base.MessageType;import com.bufoon.socket.command.ParamHandle;import com.bufoon.socket.event.SocketEvent;import com.bufoon.socket.model.PackageHead;import com.bufoon.socket.util.DispatchEvent;import com.bufoon.util.StringUtils;import mx.events.FlexEvent;public var Stageheight:Number = flash.system.Capabilities.screenResolutionY;  public var Stagewidth:Number = flash.system.Capabilities.screenResolutionX; public var clientSocket:CustomeSocket;public var selfUserName:String;public var selfUserNum:String;[Bindable][Embed(source="assets/images/mhead_online.png")] public var manHeadOnline:Class;[Bindable][Embed(source="assets/images/mhead_offline.png")] public var manHeadOffline:Class;[Bindable][Embed(source="assets/images/whead_online.png")] public var womanHeadOnline:Class;[Bindable][Embed(source="assets/images/whead_offline.png")] public var womanHeadOffline:Class;[Bindable][Embed(source="assets/swf/mhead_active.swf")] public var manHeadActive:Class;[Bindable][Embed(source="assets/swf/whead_active.swf")] public var womanHeadActive:Class;public var openWinUserNum:Array = new Array();[Bindable]public var friendXML:XML = null;private function init(e:FlexEvent):void {this.move(Stagewidth/2-this.width/2,Stageheight/2-this.height/2); //(this当前窗体)this.showStatusBar = false;this.addEventListener(Event.ADDED_TO_STAGE, function(e:*):void {bgLogin.addEventListener(MouseEvent.MOUSE_DOWN, winMove);});clientSocket = new CustomeSocket("127.0.0.1", 7073);DispatchEvent.getInstance().addEventListener(SocketEvent.CONNECT, socketHandle);//监听Socket因错误连接失败事件DispatchEvent.getInstance().addEventListener(SocketEvent.IO_ERROR, socketHandle);DispatchEvent.getInstance().addEventListener(SocketEvent.NOTICE_RECEIVE , serverNotice);}private function serverNotice(event:SocketEvent):void{var ph:PackageHead = PackageHead(event.data.ph);var jsonObj:Object = com.adobe.serialization.json.JSON.decode(ph.packageBodyContent);if(ph.messageCommand == MessageType.SEND_MESSAGE_ACK_NOTICE){var messageXML:XML = friendXML.node.node.(@userNum == jsonObj.senderNum)[0];if(messageXML != null && !this.isExitArray(jsonObj.senderNum)){messageXML.@message = jsonObj.sendInfo;}}if(ph.messageCommand == MessageType.USER_ON_OFF_LINE_NOTICE){var userNum:String = jsonObj.userNum;var status:String = jsonObj.status;var xml:XML = friendXML.node.node.(@userNum == userNum)[0];if(xml != null){xml.@isOnline = status;}}}private function isExitArray(str:String):Boolean{var flag:Boolean = false;for(var i:int = 0; i < openWinUserNum.length; i++){if(openWinUserNum[i] == str){flag = true;break;}}return flag;}private function winMove(e:MouseEvent):void {nativeWindow.startMove();}protected function loginHandle(event:MouseEvent):void{this.initSocketConnect();}/** 初始化连接socket **/public function initSocketConnect():void{//初始化socket连接if(!clientSocket.connected){clientSocket.connect();}}private function socketHandle(e:com.bufoon.socket.event.SocketEvent):void{if(!clientSocket.connected){return;}var username:String = userTI.text;var password:String = passTI.text;var paramObj:Object = new Object();paramObj.username = username;paramObj.password = password;var param:String = com.adobe.serialization.json.JSON.encode(paramObj);trace(param);var byteArray:ByteArray = ParamHandle.setSendData(MessageType.LOGIN_VERIFY, param);clientSocket.send(byteArray, doLoginReceive);}private function doLoginReceive(obj:Object):void{var ph:PackageHead = PackageHead(obj);if(ph.messageCommand == MessageType.LOGIN_VERIFY_ACK){var jsonObj:Object = com.adobe.serialization.json.JSON.decode(ph.packageBodyContent);if(jsonObj.status == "0"){this.currentState = "stateMain";this.move(Stagewidth-300,50); //(this当前窗体)this.width = 280;this.height = 550;username.text = jsonObj.username;selfUserNum = jsonObj.userNumselfUserName = jsonObj.username;trace(jsonObj.userVO);trace(jsonObj.userVO.username);//请求用户列表var paramObj:Object = new Object();paramObj.userId = jsonObj.userVO.id;var param:String = com.adobe.serialization.json.JSON.encode(paramObj);var byteArray:ByteArray = ParamHandle.setSendData(MessageType.FRIEND_LIST, param);clientSocket.send(byteArray, doLoginReceive);} else {this.clientSocket.close();}}//请求用户列表响应if(ph.messageCommand == MessageType.FRIEND_LIST_ACK){var jsonArr:Array = com.adobe.serialization.json.JSON.decode(ph.packageBodyContent) as Array;var friendStr:String = "<root label='好友列表'> "for(var i:int = 0; i < jsonArr.length; i++){var friend:Object = jsonArr[i];friendStr += "<node id='" + friend.id + "' label='" + friend.name + "' isBranch='true'>"var list:Array = friend.list as Array;for(var j:int = 0;  j < list.length; j++){friendStr += "<node id='" + list[j].id + "' message='' label='" + list[j].username +"' userNum='" + list[j].userNum + "' categoryId='" + friend.id + "' sex='" + list[j].sex + "' isOnline='" + list[j].isOnline + "' signature='" + list[j].signature + "' />";}friendStr += "</node>";}friendStr += "</root>";friendXML = new XML(friendStr);}}protected function tree_doubleClickHandler(event:MouseEvent):void{var node:XML = tree.selectedItem as XML;if(node == null || node.@isBranch == "true"){return;}var cw:chatWindow = new chatWindow();cw.chatName = node.@label;cw.chatNum = node.@userNum;cw.open(true);cw.initChatWIN(node.@message, node.@sex);openWinUserNum.push(node.@userNum);node.@message = "";}//列表树图标处理函数private function treeIconHandle(item:Object):Class{var iconClass:Class;var xml:XML= XML(item); if(xml.@isBranch == "true"){return null;}if(!StringUtils.getInstance().isEmpty(xml.@message)){if(xml.@sex == "男"){iconClass = manHeadActive;}else{iconClass = womanHeadActive;}return iconClass;}if(xml.@sex == "男"){if(xml.@isOnline == "0"){iconClass = manHeadOnline;}else {iconClass = manHeadOffline;}} else{if(xml.@isOnline == "0"){iconClass = womanHeadOnline;}else {iconClass = womanHeadOffline;}}return iconClass;}]]></fx:Script><s:states><s:State name="stateLogin"/><s:State name="stateMain"/></s:states><fx:Declarations><!-- 将非可视元素(例如服务、值对象)放在此处 --></fx:Declarations><s:Group width="100%" height="100%" includeIn="stateLogin"><s:Group width="100%" height="150" id="bgLogin"><s:Rect id="background" left="0" right="0" top="0" bottom="0" topLeftRadiusX="4" topRightRadiusX="4"><s:fill><s:LinearGradient rotation="135"><s:entries><s:GradientEntry alpha="1" color="#09C6E6" ratio="0" /><s:GradientEntry alpha="1" color="#069DD6" ratio="0.77"/></s:entries></s:LinearGradient></s:fill></s:Rect><s:HGroup left="6" top="5" gap="4" verticalAlign="middle"><s:Image source="assets/images/logo.png"/><s:Label color="#464646" fontWeight="bold" text="BufoonChat"/></s:HGroup><s:HGroup right="0" top="0" gap="0"><components:LocalButton click="minimize()" upIcon="@Embed(source='assets/images/normal_minimize.png')" downIcon="@Embed(source='assets/images/click_minimize.jpg')"overIcon="@Embed(source='assets/images/over_minimize.png')"disabledIcon="@Embed(source='assets/images/click_minimize.jpg')"/><components:LocalButton click="exit()" upIcon="@Embed(source='assets/images/normal_close.png')" downIcon="@Embed(source='assets/images/close_down.jpg')"overIcon="@Embed(source='assets/images/close.jpg')"disabledIcon="@Embed(source='assets/images/click_minimize.jpg')"/></s:HGroup><s:Image source="assets/images/qq_login_head.png" verticalCenter="15" horizontalCenter="0"/></s:Group><s:HGroup top="170" horizontalCenter="0" gap="8"><s:Image source="assets/images/qq.jpg"/><s:VGroup gap="6" horizontalAlign="right"><s:TextInput id="userTI" width="194" text="12345" borderAlpha="0.7" height="25" borderColor="#D1D1D1"/><s:TextInput id="passTI" text="12345" borderAlpha="0.7" height="25" borderColor="#D1D1D1"  width="194" displayAsPassword="true"/><s:HGroup gap="0" verticalAlign="middle"><components:LocalButton upIcon="@Embed(source='assets/images/check_normal.jpg')" downIcon="@Embed(source='assets/images/check_click.jpg')"overIcon="@Embed(source='assets/images/check_over.jpg')"disabledIcon="@Embed(source='assets/images/up.jpg')"/><s:Label text="记住密码" color="0x666666" fontSize="13" fontFamily="SimSun"/></s:HGroup><s:Group height="60"><components:LocalButton upIcon="@Embed(source='assets/images/btn_login_normal.jpg')" downIcon="@Embed(source='assets/images/btn_login_click.jpg')"overIcon="@Embed(source='assets/images/btn_login_over.jpg')"disabledIcon="@Embed(source='assets/images/up.jpg')"verticalCenter="0" click="loginHandle(event)" top="10"/></s:Group></s:VGroup></s:HGroup><s:HGroup bottom="-2" height="40" verticalAlign="middle" left="10"><s:Label text="注册" color="#999999" fontFamily="Microsoft YaHei" fontSize="12"/><s:Label text="|" color="#999999" fontFamily="Microsoft YaHei" fontSize="12"/><s:Label text="关于" color="#999999" fontFamily="Microsoft YaHei" fontSize="12"/></s:HGroup></s:Group><s:Group width="100%" id="bgMain" height="100%" includeIn="stateMain"><s:Group id="mainGroup" mouseDown="nativeWindow.startMove();" width="100%" height="110"><s:Rect left="0" right="0" top="0" bottom="0" topLeftRadiusX="4" topRightRadiusX="4"><s:fill><s:LinearGradient rotation="135"><s:entries><s:GradientEntry alpha="1" color="#09C6E6" ratio="0" /><s:GradientEntry alpha="1" color="#069DD6" ratio="0.77"/></s:entries></s:LinearGradient></s:fill></s:Rect><s:HGroup left="6" top="5" gap="4" verticalAlign="middle"><s:Image source="assets/images/logo.png"/><s:Label color="#464646" fontWeight="bold" text="AirQQ"/></s:HGroup><s:HGroup right="0" top="0" gap="0"><components:LocalButton click="minimize()" upIcon="@Embed(source='assets/images/normal_minimize.png')" downIcon="@Embed(source='assets/images/click_minimize.jpg')"overIcon="@Embed(source='assets/images/over_minimize.png')"disabledIcon="@Embed(source='assets/images/click_minimize.jpg')"/><components:LocalButton click="exit()" upIcon="@Embed(source='assets/images/normal_close.png')" downIcon="@Embed(source='assets/images/close_down.jpg')"overIcon="@Embed(source='assets/images/close.jpg')"disabledIcon="@Embed(source='assets/images/click_minimize.jpg')"/></s:HGroup><s:Image source="@Embed('assets/images/mhead_online.png')" bottom="20" left="5" width="50" height="50" scaleMode="zoom"/><s:VGroup height="50" left="65" bottom="20" paddingTop="3"><s:Label id="username" fontFamily="Microsoft YaHei" fontSize="14" fontWeight="bold"/><s:Label text="不要轻言放弃,否则对不起自己" fontFamily="Microsoft YaHei" fontSize="13"/></s:VGroup></s:Group><mx:Tree id="tree" top="110" bottom="50" width="100%" height="100%" borderVisible="false"   contentBackgroundColor="#FEFEFE" iconFunction="treeIconHandle" dataProvider="{friendXML}" focusColor="#6EF5ED" selectedItem="{}" styleName="treeStyle" variableRowHeight="true" wordWrap="true" itemRenderer="com.bufoon.render.MyTreeItemRenderer" labelField="@label" selectionColor="#3DDDDB" showRoot="false" doubleClickEnabled="true" doubleClick="tree_doubleClickHandler(event)">  </mx:Tree><s:BorderContainer height="50" width="100%" bottom="0" backgroundColor="0xCFE5F8" borderVisible="false"><components:LocalButton upIcon="@Embed(source='assets/images/search_normal.jpg')" downIcon="@Embed(source='assets/images/search_click.jpg')"overIcon="@Embed(source='assets/images/search_over.jpg')"disabledIcon="@Embed(source='assets/images/click_minimize.jpg')"toolTip="添加好友" verticalCenter="0" left="15"/></s:BorderContainer></s:Group></s:WindowedApplication>

再开聊天窗口(chatWindow.mxml)

<?xml version="1.0" encoding="utf-8"?><s:Window xmlns:fx="http://ns.adobe.com/mxml/2009"  xmlns:s="library://ns.adobe.com/flex/spark" systemChrome="none" transparent="true" resizable="false" showStatusBar="false"   xmlns:mx="library://ns.adobe.com/flex/mx" width="470" height="490" close="window1_closeHandler(event)"  initialize="window1_initializeHandler(event)" skinClass="com.bufoon.skins.WindowAppSkin" xmlns:components="com.bufoon.components.*"><fx:Style>@namespace s "library://ns.adobe.com/flex/spark";@namespace mx "library://ns.adobe.com/flex/mx";.receiveTaStyle{color:#575AEC;fontSize: 14;fontWeight:bold;}.sendTaStyle{color:#157528;fontSize: 14;fontWeight:bold;}</fx:Style><fx:Script><![CDATA[import com.adobe.serialization.json.JSON;import com.bufoon.socket.base.MessageType;import com.bufoon.socket.command.ParamHandle;import com.bufoon.socket.event.SocketEvent;import com.bufoon.socket.model.PackageHead;import com.bufoon.socket.util.DispatchEvent;import com.bufoon.util.CustomEvent;import com.bufoon.util.DateUtil;import mx.core.FlexGlobals;import mx.events.FlexEvent;import mx.managers.PopUpManager;import spark.components.RichEditableText;import spark.layouts.VerticalAlign;import flashx.textLayout.elements.FlowLeafElement;import flashx.textLayout.elements.InlineGraphicElement;import flashx.textLayout.elements.ParagraphElement;import flashx.textLayout.elements.SpanElement;import flashx.textLayout.elements.TextFlow;[Bindable]public var chatName:String;[Bindable]public var chatNum:String;private var _chatSendTA:TextFlow;private var _chatReceiveTA:TextFlow;private function get chatSendTA():TextFlow{return RichEditableText(sendTA.textDisplay).textFlow;}private function get chatReceiveTA():TextFlow{return RichEditableText(receiveTA.textDisplay).textFlow;}protected function window1_initializeHandler(event:FlexEvent):void{// TODO Auto-generated method stubgroupWin.addEventListener(MouseEvent.MOUSE_DOWN, function move(e:MouseEvent):void{nativeWindow.startMove();});//监听服务器通知DispatchEvent.getInstance().addEventListener(SocketEvent.NOTICE_RECEIVE , serverNotice);sendTA.addEventListener(FocusEvent.FOCUS_IN, focusInHandlers);}private function focusInHandlers(event:FocusEvent):void{IME.enabled = true;}private function serverNotice(event:SocketEvent):void{var ph:PackageHead = PackageHead(event.data.ph);if(ph.messageCommand == MessageType.SEND_MESSAGE_ACK_NOTICE){var jsonObj:Object = com.adobe.serialization.json.JSON.decode(ph.packageBodyContent);if(jsonObj.senderNum != chatNum){return;}var p:ParagraphElement = new ParagraphElement();var span:SpanElement = new SpanElement();p.lineHeight = 30;span.text = jsonObj.sender + "(" + jsonObj.senderNum + ")  " + DateUtil.formatDate();span.setStyle("color", 0x575AEC);span.setStyle("fontWeight", "bold");span.setStyle("fontSize", "15");p.addChild(span);chatReceiveTA.addChild(p);var sendInfo:String = jsonObj.sendInfo;var re:RegExp = /\/\d{1,3}\.swf/g; //加上g表示找到所有匹配的字符串var receivePE:ParagraphElement = new ParagraphElement();receivePE.lineHeight = 30;receivePE.textIndent = 25;if(re.test(sendInfo)){ //有图片,进行解析var faceArr:Array = sendInfo.match(re);for(var i:int = 0; i <faceArr.length; i++){//先替换,好做分割sendInfo = sendInfo.replace(faceArr[i], "ā" + faceArr[i] + "ā"); }var sendInfoArr:Array = sendInfo.split("ā");for(var j:int = 0; j <sendInfoArr.length; j++){var temp:String = sendInfoArr[j];if(re.test(temp)){ //图片var img:InlineGraphicElement = new InlineGraphicElement();img.source = "assets/swf/face" + temp;receivePE.addChild(img);} else{ //文字var text:SpanElement = new SpanElement();text.text = temp;text.setStyle("fontSize", 13);receivePE.addChild(text);}}} else { //没图片var text1:SpanElement = new SpanElement();text1.text = sendInfo;text1.setStyle("fontSize", 13);receivePE.addChild(text1);}chatReceiveTA.addChild(receivePE);}receiveTA.validateNow();receiveTA.scroller.verticalScrollBar.value = receiveTA.scroller.verticalScrollBar.maximum;receiveTA.validateNow();receiveTA.scroller.verticalScrollBar.value = receiveTA.scroller.verticalScrollBar.maximum;}protected function sendMessageHandle(event:MouseEvent):void{var re:RegExp = /^\s*$/;var msg:String = sendTA.text;if(re.test(msg)){//如果输入的字符串仅包含空格、回车或者空,就不能发送信息sendTA.setFocus();return;}var p:ParagraphElement = input.deepCopy() as ParagraphElement;var arr:Array = p.mxmlChildren;var sendInfo:String = "";for(var i:int = 0; i < arr.length; i++){if(arr[i] is SpanElement){sendInfo += (arr[i] as SpanElement).text;}if(arr[i] is InlineGraphicElement){var temp:String = String((arr[i] as InlineGraphicElement).source);temp = temp.substr(temp.lastIndexOf("/"));sendInfo += temp;}}trace(sendInfo);//p.format = chatSendTA.hostFormat;p.textIndent = 25;p.setStyle("fontSize", 13);p.lineHeight=30;p.verticalAlign = VerticalAlign.MIDDLE;var p1:ParagraphElement = new ParagraphElement();var span:SpanElement = new SpanElement();span.text = "我(" + chatNum + ")  " + DateUtil.formatDate();span.setStyle("color", 0x157528);span.setStyle("fontWeight", "bold");span.setStyle("fontSize", "15");p1.lineHeight = 30;p1.addChild(span);chatReceiveTA.addChild(p1);chatReceiveTA.addChild(p);sendTA.text = "";sendTA.setFocus();var obj:Object = new Object();obj.sender = FlexGlobals.topLevelApplication.selfUserName;obj.receiver = chatName;obj.sendInfo = sendInfo;obj.receiverNum = chatNum;obj.senderNum = FlexGlobals.topLevelApplication.selfUserNumvar param:String = com.adobe.serialization.json.JSON.encode(obj);var byteArrays:ByteArray = ParamHandle.setSendData(MessageType.SEND_MESSAGE, param);trace(MessageType.SEND_MESSAGE);FlexGlobals.topLevelApplication.clientSocket.send(byteArrays, receiveHandle);if(receiveTA != null){receiveTA.validateNow();receiveTA.scroller.verticalScrollBar.value = receiveTA.scroller.verticalScrollBar.maximum;receiveTA.validateNow();receiveTA.scroller.verticalScrollBar.value = receiveTA.scroller.verticalScrollBar.maximum;}}private function receiveHandle(obj:Object):void{var ph:PackageHead = PackageHead(obj);if(ph.messageCommand == MessageType.SEND_MESSAGE_ACK){trace("发送成功");}}protected function emotionSelected(event:MouseEvent):void{var ep:FaceWindow = new FaceWindow();ep.addEventListener(CustomEvent.EMOTION_INSERT, selectEmotionHandler);var point:Point = event.currentTarget.localToGlobal(new Point(ep.x, ep.y-190));ep.x = point.x;ep.y = point.y;PopUpManager.addPopUp(ep, this);}private function selectEmotionHandler(evt:CustomEvent):void{var img:InlineGraphicElement = new InlineGraphicElement();img.source = evt.data;var anchor:int = sendTA.selectionAnchorPosition;anchor = (anchor == -1) ? 0 : anchor;var leaf:FlowLeafElement = input.findLeaf(anchor);//获取被拆分的span的子索引,并将表情图标插入到本child后方var index:int = input.getChildIndex(leaf);input.addChildAt(index + 1, img);//设置光标sendTA.textDisplay.selectRange(anchor + 1, anchor + 1);}private function get input():ParagraphElement{return chatSendTA.getChildAt(0) as ParagraphElement;}protected function window1_closeHandler(event:Event):void{var arr:Array = FlexGlobals.topLevelApplication.openWinUserNum;for(var i:int; i < arr.length; i++){if(arr[i] == chatNum){arr.splice(i-1, 1);}}}public function initChatWIN(sendInfo:String, headType:String):void{if(headType == "男"){winHeadImg.source = FlexGlobals.topLevelApplication.manHeadOnline;}else{winHeadImg.source = FlexGlobals.topLevelApplication.womanHeadOnline;}if(sendInfo == ""){return;}var p:ParagraphElement = new ParagraphElement();var span:SpanElement = new SpanElement();p.lineHeight = 30;span.text = chatName + "(" + chatNum + ")  " + DateUtil.formatDate();span.setStyle("color", 0x575AEC);span.setStyle("fontWeight", "bold");span.setStyle("fontSize", "15");p.addChild(span);chatReceiveTA.addChild(p);var sendInfo:String = sendInfo;var re:RegExp = /\/\d{1,3}\.swf/g; //加上g表示找到所有匹配的字符串var receivePE:ParagraphElement = new ParagraphElement();receivePE.lineHeight = 30;receivePE.textIndent = 25;if(re.test(sendInfo)){ //有图片,进行解析var faceArr:Array = sendInfo.match(re);for(var i:int = 0; i <faceArr.length; i++){//先替换,好做分割sendInfo = sendInfo.replace(faceArr[i], "ā" + faceArr[i] + "ā"); }var sendInfoArr:Array = sendInfo.split("ā");for(var j:int = 0; j <sendInfoArr.length; j++){var temp:String = sendInfoArr[j];if(re.test(temp)){ //图片var img:InlineGraphicElement = new InlineGraphicElement();img.source = "assets/swf/face" + temp;receivePE.addChild(img);} else{ //文字var text:SpanElement = new SpanElement();text.text = temp;text.setStyle("fontSize", 13);receivePE.addChild(text);}}} else { //没图片var text1:SpanElement = new SpanElement();text1.text = sendInfo;text1.setStyle("fontSize", 13);receivePE.addChild(text1);}chatReceiveTA.addChild(receivePE);}]]></fx:Script><fx:Declarations><!-- 将非可视元素(例如服务、值对象)放在此处 --></fx:Declarations><s:Group id="groupWin" width="100%" height="70"><s:Rect id="background1" left="0" right="0" top="0" bottom="0" topLeftRadiusX="4" topRightRadiusX="4"><s:fill><s:LinearGradient rotation="135"><s:entries><s:GradientEntry alpha="1" color="#09C6E6" ratio="0" /><s:GradientEntry alpha="1" color="#069DD6" ratio="0.77"/></s:entries></s:LinearGradient></s:fill></s:Rect><s:HGroup left="6" top="5" gap="4" verticalAlign="middle"><s:Image id="winHeadImg"/><s:Label color="#464646" fontWeight="bold" fontFamily="Microsoft YaHei" fontSize="14" text="与 {chatName}({chatNum}) 聊天中"/></s:HGroup><s:HGroup right="0" top="0" gap="0"><components:LocalButton click="minimize()" upIcon="@Embed(source='assets/images/normal_minimize.png')" downIcon="@Embed(source='assets/images/click_minimize.jpg')"overIcon="@Embed(source='assets/images/over_minimize.png')"disabledIcon="@Embed(source='assets/images/click_minimize.jpg')"/><components:LocalButton click="close()" upIcon="@Embed(source='assets/images/normal_close.png')" downIcon="@Embed(source='assets/images/close_down.jpg')"overIcon="@Embed(source='assets/images/close.jpg')"disabledIcon="@Embed(source='assets/images/click_minimize.jpg')"/></s:HGroup></s:Group><s:VGroup width="100%" top="70"><s:TextArea id="receiveTA" width="100%" height="240" editable="false" borderVisible="false"/><s:BorderContainer width="100%" borderVisible="false" height="30" backgroundColor="0xD2D79A"><s:Image source="@Embed('/assets/emotions/grin.png')" left="10" verticalCenter="0" click="emotionSelected(event)" buttonMode="true"/><mx:LinkButton right="10" click="receiveTA.text=''" verticalCenter="0" label="清除消息" fontFamily="Microsoft YaHei" fontSize="14" color="0x3A11E1"/></s:BorderContainer><s:TextArea id="sendTA" fontSize="13" height="80" width="100%" borderVisible="false" focusAlpha="0"/><s:BorderContainer borderVisible="false" backgroundColor="0xD2D79A" width="100%" height="32"><components:LocalButton upIcon="@Embed(source='assets/images/send_normal.png')" downIcon="@Embed(source='assets/images/send_click.png')"overIcon="@Embed(source='assets/images/send_over.png')"disabledIcon="@Embed(source='assets/images/send_over.png')"right="10" verticalCenter="0" click="sendMessageHandle(event)" /></s:BorderContainer></s:VGroup></s:Window>

还有一个标胶主要的Socket类

CustomeSocket.as

package com.bufoon.socket{import com.bufoon.socket.base.BaseSocket;import com.bufoon.socket.command.ParamHandle;import com.bufoon.socket.event.SocketEvent;import com.bufoon.socket.model.PackageHead;import com.bufoon.socket.util.DispatchEvent;import flash.utils.ByteArray;import flash.utils.Endian;public class CustomeSocket{/**Socket实例**/private var socket:BaseSocket;/**主机地址的私有变量**/private var _host:String;/**端口的私有变量**/private var _port:int;/**接收到socket进行回调处理**/private var resultFunction:Function;/**是否已经读取了消息头**/private var isReadHead:Boolean = false;/**消息的最大长度**/private var msgMaxLength:int = 2048;/**消息头长度**/private var headLength:int = 10;/**包体长度**/private var dataLength:int;/**临时存储byte数组的对象**/private var byteArray:ByteArray ;public function CustomeSocket(host:String , port:int){this._host = host;this._port = port;socket = new BaseSocket();byteArray = new ByteArray();DispatchEvent.getInstance().addEventListener(SocketEvent.READY , onResultFunction);DispatchEvent.getInstance().addEventListener(SocketEvent.RECEIEVED , onReceieveByteArray);}/**建立Socket连接**/public function connect():void{trace(_host + "," + _port)socket.connect(_host, _port);}/**获取当前socket的连接状态**/public function get connected():Boolean{return socket.connected;}/**断开Socket连接**/public function close():void{socket.close();}/**发送客户端Socket消息 **通过ByteArray,启用 */public function send(byteArray:ByteArray , rsFunction:Function):void{//如果未连接,则返回if(!socket.connected){return;}resultFunction = rsFunction;socket.writeBytes(byteArray);socket.flush();}/**处理指令工厂的回调数据**/private function onResultFunction(event:SocketEvent):void{var cmd:int = event.data.msgReceiveType;//将数据传到resultFunctionresultFunction.call(resultFunction, event.data.ph);}/**接收服务器端的byteArray信息 *用于和C的直接交互 */private function onReceieveByteArray(event:SocketEvent = null):void{//暂时用不到这个byteArray.clear();var ph:PackageHead; //包头if(!isReadHead){ //是否已经读取了消息头if(socket.bytesAvailable >= headLength){//读取包头信息socket.endian = Endian.LITTLE_ENDIAN;ph = new PackageHead();ph.packageHeadLength = socket.readShort();ph.messageType = socket.readByte();ph.contentType = socket.readByte();ph.messageCommand = socket.readShort();ph.packageBodyLength = socket.readInt();dataLength = ph.packageBodyLength;isReadHead = true;}}if(dataLength <= msgMaxLength && isReadHead){if(ph == null || ph.packageHeadLength != 10){return; //包头信息有误,直接不处理}if(socket.bytesAvailable >= dataLength){   //读取包体内容ph.packageBodyContent = socket.readUTFBytes(dataLength);//派发内容if(ph.messageType == 0){ //服务器推送通知ParamHandle.boardNotice(ph);}else{ //普通的基于请求响应ParamHandle.parseSocketData(ph);}isReadHead = false;}}//如果还有可读字节,递归if(socket.connected){if(socket.bytesAvailable >= headLength){onReceieveByteArray();}}}}}

BaseSocket.as

/** * 自定义一个基本的Socket继承系统Socket *内部仅重写和对服务器的响应做了简单的判断 *@author bufoon **/package com.bufoon.socket.base{import com.bufoon.socket.event.SocketEvent;import com.bufoon.socket.util.DispatchEvent;import flash.events.Event;import flash.events.IOErrorEvent;import flash.events.ProgressEvent;import flash.events.SecurityErrorEvent;import flash.net.ObjectEncoding;import flash.net.Socket;import flash.system.Security;import mx.core.FlexGlobals;import mx.logging.LogEventLevel;public class BaseSocket extends Socket{public function BaseSocket(){/**AMF3只在写Object时才会用到,用于约定通信协议**/this.objectEncoding = ObjectEncoding.AMF3;/**监听Socket加载过程中的所有事件**/this.addEventListener(Event.CONNECT, onConnect);this.addEventListener(Event.CLOSE, onClose);this.addEventListener(IOErrorEvent.IO_ERROR, onIOError);this.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);this.addEventListener(ProgressEvent.SOCKET_DATA, onData);}/**重写connect方法,其中加入策略文件的加载**/override public function connect(host:String , port:int):void{Security.loadPolicyFile("xmlsocket://" + host + ":" + 843);super.connect(host, port);}/**关闭当前socket连接**/override public function close():void{if(this.connected){super.close();}}/**对socket数据的更新进行监听操作**/private function onData(e:ProgressEvent):void{DispatchEvent.getInstance().dispatchEvent(new SocketEvent(SocketEvent.RECEIEVED));}private function onConnect(e:Event):void{trace('连接socket服务器成功!');DispatchEvent.getInstance().dispatchEvent(new SocketEvent(SocketEvent.CONNECT));}private function onClose(e:Event):void{trace('断开了和socket服务器的连接!');//Alert.show("已经断开与Socket服务器的连接");DispatchEvent.getInstance().dispatchEvent(new SocketEvent(SocketEvent.CLOSE));}private function onIOError(e:IOErrorEvent):void{trace('连接服务器失败,请稍候操作');//Alert.show("连接Socket服务器失败");DispatchEvent.getInstance().dispatchEvent(new SocketEvent(SocketEvent.IO_ERROR));}private function onSecurityError(e:SecurityErrorEvent):void{trace('发生沙箱安全错误!');DispatchEvent.getInstance().dispatchEvent(new SocketEvent(SocketEvent.SECURITY_ERROR_EVENT));}}}

ParamHandle.as

package com.bufoon.socket.command{import com.bufoon.socket.base.MessageType;import com.bufoon.socket.event.SocketEvent;import com.bufoon.socket.model.PackageHead;import com.bufoon.socket.util.DispatchEvent;import com.bufoon.util.StringUtils;import flash.events.EventDispatcher;import flash.utils.ByteArray;import flash.utils.Endian;/** * socket发送和接收参数处理类 **/public class ParamHandle extends EventDispatcher{private var byteArray:ByteArray;/** * 设置发送的数据包 **/public static function setSendData(messageCommand:int = -1, message:String = ""):ByteArray{var byteArray:ByteArray;byteArray = new ByteArray();byteArray.endian = Endian.LITTLE_ENDIAN; //字节序byteArray.writeShort(MessageType.HEAD_LENGTH);//包头大小byteArray.writeByte(MessageType.MESSAGE_TYPE);//消息类型byteArray.writeByte(MessageType.CONTENT_TYPE);//内容类型byteArray.writeShort(messageCommand);//消息命令byteArray.writeInt(StringUtils.getInstance().convertStringToByteArray(message).length);//包体长度if(message.length != 0){byteArray.writeUTFBytes(message); //包体内容}return byteArray;}/** * 派发事件 **/ public static function parseSocketData(ph:PackageHead):void{var type:int = ph.messageCommand;DispatchEvent.getInstance().dispatchEvent(new SocketEvent(SocketEvent.READY, {msgReceiveType:type, ph:ph}));}public static function boardNotice(ph:PackageHead):void{DispatchEvent.getInstance().dispatchEvent(new SocketEvent(SocketEvent.NOTICE_RECEIVE, {ph:ph}));}}}

注:主要就是这几个界面组件和Socket的操作类,还有就是Flex air同一个应用程序不能开多个窗口,这点不知道为什么,要开多个窗口就是新建几个项目名称不一样就行,还有就是导出air安装包的时候将项目-app.xml文件中的ID,name,filename改掉,再安装就可以。


0 0