开发工具 (FlashBuilder4.7)

程序类型(Adobe Air)

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

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

大致的实现方式:

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

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

看下client的详细实现吧:

首先我们把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.userNum
selfUserName = 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 stub
groupWin.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.selfUserNum
var 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();
} /**发送clientSocket消息
**通过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;
//将数据传到resultFunction
resultFunction.call(resultFunction, event.data.ph);
} /**接收server端的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){ //server推送通知
ParamHandle.boardNotice(ph);
}else{ //普通的基于请求响应
ParamHandle.parseSocketData(ph);
}
isReadHead = false;
}
} //假设还有可读字节,递归
if(socket.connected){
if(socket.bytesAvailable >= headLength){
onReceieveByteArray();
}
} }
} }

BaseSocket.as

/**
* 自己定义一个主要的Socket继承系统Socket
*内部仅重写和对server的响应做了简单的推断
*@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('连接socketserver成功! ');
DispatchEvent.getInstance().dispatchEvent(new SocketEvent(SocketEvent.CONNECT));
} private function onClose(e:Event):void
{
trace('断开了和socketserver的连接。');
//Alert.show("已经断开与Socketserver的连接");
DispatchEvent.getInstance().dispatchEvent(new SocketEvent(SocketEvent.CLOSE));
} private function onIOError(e:IOErrorEvent):void
{
trace('连接server失败。请稍候操作');
//Alert.show("连接Socketserver失败");
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改掉,再安装就能够。

最新文章

  1. WCF中的错误及解决办法
  2. Url通配符映射
  3. IOS的一些小技巧
  4. CentOS7下Firewall防火墙配置用法详解
  5. 一些实用的linux命令
  6. sqlite3之基本操作(二)
  7. Python开发【第一篇】Python模块中特殊变量
  8. java.util.HashMap源码分析
  9. POJ 1001 Exponentiation 无限大数的指数乘法 题解
  10. pytesser的使用
  11. 双向链表设计与API实现
  12. Android之Realm详解
  13. 【工利其器】必会工具之(一)Source Insight篇
  14. 【CF765F】Souvenirs 主席树
  15. Niagara workbench 介绍文档---翻译
  16. [svc]glusterfs的简单部署
  17. DataSet &amp; DataTable &amp;DataRow 深入浅出
  18. Java 中的 JVM、堆和栈 -- 初步了解
  19. 20164321 王君陶 Exp1 PC平台逆向破解
  20. C++中的随机函数

热门文章

  1. javascript设计模式-继承
  2. mysql在第一次查询的时候很慢,第二次查询就比较快的原因?
  3. Kettle通用数据贴源作业设计
  4. Java语言基础(数组)
  5. 一个不错的学习android的网站
  6. 强大的JQuery链式操作风格
  7. NTP学习路线
  8. UEditor如何读取数据库信息?
  9. 洛谷P1427 小鱼的数字游戏
  10. P3378 【模板】堆