[转]自制Flash表情聊天

http://blog.163.com/caty_nuaa/blog/static/90390720103811359879/

[转]自制Flash表情聊天


因为项目需要,自己仿照其他flash游戏中的聊天写了一个带表情的聊天,思路还是基于网上的使用多个TextField来组织聊天信息的显示,每个TextField上再摆放一层用于显示表情的图层,这样做效率还是不错的,不过不可以一次选择多行了,目前也没有做表情的复制功能,所以选中复制的时候只能复制占位符

点此下载工程

ChatTextField.as

package com.lx.chat
{
    import fl.containers.ScrollPane;
    import fl.controls.ScrollBarDirection;
    import fl.controls.ScrollPolicy;
    import fl.events.ScrollEvent;
    
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.events.TextEvent;
    
    public class ChatTextField extends Sprite
    {
        private var _textContainer:Sprite;
        private var _scroll:ScrollPane;
        private var _maxLines:uint;
        private var _msgArr:Array;
        
        public function ChatTextField(w:uint = 300, h:uint = 200):void
        {
            _msgArr = new Array();
            _textContainer = new Sprite();
            _scroll = new ScrollPane();
            //_scroll.verticalScrollPolicy = ScrollPolicy.ON;
            _scroll.horizontalScrollPolicy = ScrollPolicy.OFF;
            _scroll.focusEnabled = false; // 此设置可以解决text不能选择的问题,也可以解决多个选择的问题
            _scroll.source = _textContainer;
            _scroll.setSize(w, h);
            _scroll.addEventListener(ScrollEvent.SCROLL, onScroll);
            //_scroll.useBitmapScrolling = true;
            addChild(_scroll);
            maxLines = 30;
        }
        
        public function appendMessage(channel:String, srcName:String, dstName:String, id:uint, content:String, bSelf:Boolean):void
        {
            var tf:FaceTextField = new FaceTextField(_scroll.width - _scroll.verticalScrollBar.width);
            tf.appendChatText(channel, srcName, dstName, id, content, bSelf);
            _msgArr.push(tf);
            tf.x = 0;
            tf.y = _textContainer.height;
            _textContainer.addChild(tf);
            if (_msgArr.length > _maxLines)
            {
                var obj:DisplayObject = _msgArr.shift();
                if (obj)
                {
                    _textContainer.removeChild(obj);
                    for each (var item:FaceTextField in _msgArr)
                    {
                        item.y = item.y - obj.height;
                    }
                }
            }
            if (_textContainer.height > _scroll.height)
            {
                _scroll.update();
                _scroll.verticalScrollPosition = _scroll.maxVerticalScrollPosition;
            }
            else
                _scroll.refreshPane();
            tf._txtField.addEventListener(TextEvent.LINK, onLink);
        }
        
        private function onScroll(e:ScrollEvent):void
        {
            if (e.direction == ScrollBarDirection.VERTICAL)
            {
                for each(var item:FaceTextField in _msgArr)
                {
                    if (item.y + item.height < _scroll.verticalScrollPosition || 
                        item.y > _scroll.verticalScrollPosition + this.height)
                    {
                        item.visible = false;
                    }
                    else
                    {
                        item.visible = true;
                    }
                } 
            }
        }
        
        private function onLink(e:TextEvent):void
        {
            this.dispatchEvent(e);
        }
        
        public function set maxLines(n:uint):void
        {
            _maxLines = n;
        }
        
        public function get maxLines():uint
        {
            return _maxLines;
        }
    }
}



FaceTextField.as

package com.lx.chat
{
    import com.lx.sprites.Animation;
    
    import flash.display.Bitmap;
    import flash.display.Sprite;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.text.TextFormatAlign;
    import flash.utils.*;

    public class FaceTextField extends Sprite
    {
        //the text format
        private var _fmtName:TextFormat;
        private var _fmtMsg:TextFormat;
        //the instance of textfield
        public var _txtField:TextField;
        //indicates line height.
        public var lineHeight:Number;
        //only contain sprites which are inserted in _textfield
        public var _spriteContainer:Sprite;
        //the default textformat of _textfield
        private var _defaultTextFormat:TextFormat;
        //the length of the _textfield.text
        private var _length:int;
        //specify the sprite&39;s vspace/hspace in _textfield
        private var _spriteVspace:int;
        private var _spriteHspace:int;
        //save the selection begin/end indexes of _textfield
        private var _selectBegin:int;
        private var _selectEnd:int;
        //use it to mark the TextField.replaceText() time during addSprite
        private var _replacing:Boolean;
        //regular expression for the chat face
        private static var _reg:RegExp;
        //width of the textfield
        private var _widthTxt:uint;
        //the text align is center?
        private var _bCenter:Boolean;
        
        /**
         * trick, a sprite&39;s placeholder
         * special character: ﹒ unicode is 65106
         * special font: Arial
         */
        private var PLACEHOLDER:String = "○";
        private var PLACEHOLDER_FONT:String = "Arial";
        private var PLACEHOLDER_COLOR:uint = 0x000000;
        private static const _lt:String="/:";
        //dictionary of the emote
        public static var _dict:Dictionary;

        public function FaceTextField(width:Number, center:Boolean = false)
        {
            _fmtName = new TextFormat("Arial", 12, 0xFF0000, true);    
            _fmtMsg = new TextFormat("Courier New", 12, 0xFFFFFF, false);
            _bCenter = center;
            if (center)
            {
                _fmtName.align = TextFormatAlign.CENTER;
                _fmtMsg.align = TextFormatAlign.CENTER;
            }
            //initTextField(width);            
            _spriteContainer = new Sprite();                
            //default lineHeight is 0(ignore)
            lineHeight = 0;
            //default sprite vspace/hspace is 2 (changes to 1, 2009-3-3)
            _spriteHspace = _spriteVspace = 1;        
            _widthTxt = width;
        }
        
        public static function buildRegExp(arrExpress:Array):void 
        {
            var strReg:String = "";
            var arrReg:Array = new Array();
            var last:String = arrExpress[arrExpress.length - 1];
            for each(var item:* in arrExpress)
            {
                arrReg.push(_lt);
                arrReg.push(item);
                if (item != last)
                    arrReg.push("|");
            }
            strReg = arrReg.join("");
            _reg = new RegExp(strReg, "g");
        }
        
        /**
         * initialize the _textfield
         * @param    width
         */
        private function initTextField(width:Number):void
        {
            _txtField = new TextField();            
            _txtField.width = width;
            _txtField.height = 400;
            _txtField.multiline = true;
            _txtField.wordWrap = true;
            //_txtField.filters = [GlobalFunction.getGlowFilter()];
        }
        
         /**
         * 将加工过的字符串转换为包含表情的内容.
         * @param str
         * */
        public function convertStringToRich(str:String):Object 
        {
            var array:Array = [];
            var arrExp:Array = str.match(_reg);
            var faceObj:Object = {src:null, index:null};
            for (var i:int = 0; i < arrExp.length; ++i)
            {
                try
                {
                    var className:String = arrExp[i].slice(_lt.length, arrExp[i].length);
                    var idx:int = str.indexOf(arrExp[i]);
                    faceObj = {src:className, index:idx};
                    str = str.substring(0, idx) + str.substring(idx + arrExp[i].length, str.length);
                }
                catch (err:Error)
                {
                    continue;
                }
                array.push(faceObj);
            }
            
            var result:Object = {mess:str, faces:array};
            return result;
        }
        
        public function get numSprite():int
        {
            return _spriteContainer.numChildren;
        }
        
        public function getSpriteIndexAt(depth:int):int
        {            
            var sprite:Bitmap = getSpriteAt(depth);
            if (sprite) 
                return int(sprite.name);
            else 
                return -1;
        }
        
        public function getSpriteAt(depth:int):Bitmap
        {
            if (depth >= _spriteContainer.numChildren) 
                return null;
            return _spriteContainer.getChildAt(depth) as Bitmap;
        }    
        
        public function getSpriteByName(name:String):Bitmap
        {
            return _spriteContainer.getChildByName(name) as Bitmap;            
        }
        
        public function removeSpriteByName(name:String):void
        {
            var sp:Bitmap = _spriteContainer.getChildByName(name) as Bitmap;
            if (sp)
            {
                _spriteContainer.removeChild(sp);
            }
        }
        
        public function set spriteVspace(value:int):void
        {
            _spriteVspace = value;
        }
        
        public function set spriteHspace(value:int):void
        {
            _spriteHspace = value;
        }
        
        public function set defaultTextFormat(format:TextFormat):void
        {
            //set the default textformat and effect immediately
            if (format.letterSpacing == null) 
                format.letterSpacing = 0;
            _defaultTextFormat = format;
            _txtField.defaultTextFormat = format;
        }
        
        public function get defaultTextFormat():TextFormat
        {
            return _defaultTextFormat;
        }
        
        public function set placeholderColor(value:uint):void
        {
            PLACEHOLDER_COLOR = value;
        }
        
        /**
         * clear all properties, back to original status.
         */
        public function clear():void
        {
            _txtField.text = "";
            recoverDefaultTextFormat();
            _spriteContainer.y = 0;
            while (_spriteContainer.numChildren > 0)
                _spriteContainer.removeChildAt(0);
        }
        
        public function appendChatText(channel:String, srcName:String, dstName:String, id:uint, 
            content:String, bSelf:Boolean = false, autoWordWrap:Boolean = true):void
        {
            if (_txtField)
            {
                clear();
                removeChild(_txtField);
            }
            initTextField(_widthTxt);
            var obj:Object = convertStringToRich(content);
            appendText(channel, srcName, dstName, id, obj.mess, bSelf, _fmtName, _fmtMsg);    
            
            if (obj.faces)
            {
                var _arrEmote:Array = new Array();
                for (var i:int = 0; i < obj.faces.length; i++)
                {
                    var index:int = obj.faces[i].index;
                    if (index == -1) 
                        index = obj.mess.length;
                    else if (autoWordWrap) 
                        index -= 1;
                    index += _txtField.length - obj.mess.length;                    
                    //trace("addSprite", object[i].src, index, _textfield.length);
                    //the last sprite should be added before newline character(\n).
                    //if (autoWordWrap && index == _textfield.length) index --;
                    //modified at 12-05-2008
                    if (autoWordWrap && index >= _txtField.length) 
                        index = _txtField.length - 1;
                    //if specify lineHeight(>0), all lines will be same height.
                    index = addPlaceHolder(index);
                    _arrEmote.push({cname:obj.faces[i].src, idx:index});
                    //addSprite(obj.faces[i].src, index, 0, lineHeight);
                }
                for each(var item:Object in _arrEmote)
                {
                    addSprite(item.cname, item.idx, 0, lineHeight);
                }
            }
            _txtField.height = (int)(_txtField.textHeight) + 5;
            addChild(_txtField);
            addChild(_spriteContainer);
        }
        
        private function addPlaceHolder(caretIndex:int):int
        {
            //insert a placeholder for target and format it
            if (caretIndex == -1) 
                caretIndex = _txtField.caretIndex;
            //fix the bug that supplied index is out of bound, 11-24-2008
            //if caretIndex is out of bound, add sprite to the end.            
            if (caretIndex > _txtField.length) 
            {
                caretIndex = _txtField.length;
            }
            var format:TextFormat = getPlaceholderFormat();        
            
            _txtField.replaceText(caretIndex, caretIndex, PLACEHOLDER);
            _txtField.setTextFormat(format, caretIndex);
            return caretIndex;
        }
        
        /**
         * add a sprite with a placeholder to the right place
         * @param    target Class or Sprite instance
         * @param    width
         * @param    height
         * @param    caretIndex
         */
        private function addSprite(target:String, caretIndex:int = -1, width:Number = -1, height:Number = -1):void
        {            
            //create a target sprite
            var targetClass:Class;
            var ani:Animation;
            if (target != null && target != "")
            {
                var arrBmp:Array;
                var str:String = target.toUpperCase();
                arrBmp = _dict[str];
                ani = new Animation();
                ani.fps = 250;            
                
                var rectPlaceholder:Rectangle = getCharBoundaries(caretIndex);
                //var h:int = target.height;
                var x:int = _spriteContainer.x + rectPlaceholder.left - _spriteHspace;
                var y:int = rectPlaceholder.top + rectPlaceholder.height - arrBmp[0].height - _spriteVspace;
                for each (var item:* in arrBmp)
                {
                    ani.push(new Bitmap(item), x, y);
                }
                ani.name = String(caretIndex);
                _spriteContainer.addChild(ani);
            }
        }
        
        private function appendText(channel:String, srcName:String, dstName:String, id:uint, text:String, bSelf:Boolean, formatName:TextFormat = null, formatText:TextFormat = null):void
        {
            recoverDefaultTextFormat();
            if (channel)
            {
                channel = channel.replace(/</g, "&lt;");
                channel = channel.replace(/>/g, "&gt;");
            }
            if (srcName)
            {
                srcName = srcName.replace(/</g, "&lt;");
                srcName = srcName.replace(/>/g, "&gt;");
            }
            if (dstName)
            {
                dstName = dstName.replace(/</g, "&lt;");
                dstName = dstName.replace(/>/g, "&gt;");
            }
            if (text)
            {
                text = text.replace(/</g, "&lt;");
                text = text.replace(/>/g, "&gt;");
            }
            var arrStr:Array = new Array();
            if (_bCenter)
                arrStr.push("<P ALIGN=\"CENTER\">");
            else
                arrStr.push("<P ALIGN=\"LEFT\">");
            var addText:String = "";
            if ((channel && channel.length > 0) || (srcName && srcName.length > 0))
            {
                if (formatName)
                {
                    arrStr.push("<FONT FACE=\"");
                    arrStr.push(formatName.font as String);
                    arrStr.push("\" SIZE=\"");
                    arrStr.push(formatName.size.toString());
                    arrStr.push("\" COLOR=\"#");
                    arrStr.push((formatName.color as int).toString(16));
                    arrStr.push("\">");
                }
                if (channel && channel.length > 0)
                    arrStr.push(channel);
                if (srcName && srcName.length > 0)
                {
                    if (!bSelf)
                    {
                        arrStr.push("<A HREF=\"event:");
                        arrStr.push(srcName + "$" + id);
                        arrStr.push("\">");
                        arrStr.push(srcName);
                        arrStr.push("</A>");
                    }
                    else
                    {
                        arrStr.push(srcName);
                    }
                }
                if (dstName && dstName.length > 0)
                {
                    arrStr.push("悄悄地对");
                    if (dstName != "你")
                    {
                        arrStr.push("<A HREF=\"event:");
                        arrStr.push(dstName + "$" + id);
                        arrStr.push("\">");
                        arrStr.push(dstName);
                        arrStr.push("</A>");
                    }
                    else
                        arrStr.push(dstName);
                    arrStr.push("说");
                }
                if (srcName && srcName.length > 0)
                    arrStr.push(":");
                if (formatName)
                    arrStr.push("</FONT>");
            }
            
            if (text)
            {
                if (formatText)
                {
                    arrStr.push("<FONT FACE=\"");
                    arrStr.push(formatText.font as String);
                    arrStr.push("\" SIZE=\"");
                    arrStr.push(formatText.size.toString());
                    arrStr.push("\" COLOR=\"#");
                    arrStr.push((formatText.color as int).toString(16));
                    arrStr.push("\">");
                }
                arrStr.push(text);
                if (formatText)
                    arrStr.push("</FONT>");
            }
            arrStr.push("</P>");
            //because the carriage return escape character(\r) has some bug in text copying
            //so change all of them to newline escape character(\n)        
            addText = arrStr.join("");
            //addText = addText.split("\r").join("\n");
            
            var arrHtml:Array = new Array();
            arrHtml.push(_txtField.htmlText);
            arrHtml.push(addText);
            _txtField.htmlText = arrHtml.join("");
        }
        
        /**
         * replace the textfield&39;s getCharBoundaries method
         * get char boundaries in the specify char index
         * @param    charIndex
         * @return
         */
        private function getCharBoundaries(charIndex:int):Rectangle
        {
            var rect:Rectangle = _txtField.getCharBoundaries(charIndex);
            return rect;
        }        
        
        /**
         * recover the default textformat
         */
        private function recoverDefaultTextFormat():void
        {
            if (_defaultTextFormat) 
                defaultTextFormat = _defaultTextFormat;            
        }
        
        /**
         * return a textformat for placeholder corresponding to the given width and height.
         * @param    width
         * @param    height
         * @return
         */
        private function getPlaceholderFormat():TextFormat
        {
            var format:TextFormat = new TextFormat();
            format.font = PLACEHOLDER_FONT;
            format.color = PLACEHOLDER_COLOR;
            //format.size = height + 2 * _spriteVspace;
            format.size = 22;
            format.underline = false;    
            if (_bCenter)
                format.align = TextFormatAlign.CENTER;
            //format.letterSpacing = 1;        
            //format.letterSpacing = width - height - _spriteVspace + _spriteHspace;
            return format;
        }        
    }
}


 


你可能感兴趣的:(游戏,Blog,Flash)