AS3基于TextField实现图文并排更新于2015.08.05

  几年前写了个在文本中插入图像显示 类,当时比较懒,没有任何说明和Demo,代码中一些用到的类也没提供,只是单纯地想记录下如何解决,以至于直接复制代码保存。所以之前的代码也是无法直接拿去用。

  现在由于需要,刚开始找不到之前的代码,只能复制文章中的代码,发现改起来还挺费劲的,还好后来找到了完整的代码。坑了别人也把自己给坑了。

  现在我把坑填上,还是先贴核心类的代码,代码中写了些注释虽然可能不是很详细。另外附上项目文件,项目中包含需要用到的其它类和简单的使用Demo,下载地址:http://pan.baidu.com/s/1nt8ClN7

package
{
    import flash.display.Bitmap;

    public class ImgObject
    {
        public var src:String;
        public var id:String;
        public var imgWidth:Number;
        public var imgHeight:Number;
        public var imgXml:XML;
        public var image:Bitmap;
        public var index:int; //图像所在文本中的位置
        
        public function ImgObject()
        {
            
        }
    }
}
package
{
    import flash.display.Bitmap;
    import flash.display.DisplayObject;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.geom.Rectangle;
    import flash.net.URLRequest;
    import flash.text.AntiAliasType;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFieldType;
    import flash.text.TextFormat;
    import flash.text.TextLineMetrics;

    /**
     * 文本容器类
     * 该类主要处理在文本中包含显示图像,使文本与图像支持混合排列
     * 只支持显示,不支持编辑
     * 显示图像的格式为<img id='' width='20' height='20' src='图像地址'/>
     * 如:
     * var text:String = "这个一个图文混排的容器类<img width='20',height='20' src='/data/index.jpg'/>
     * var tc:TextContainer = new TextContainer(200,new TextFormat("微软雅黑"));
     * tc.setText(text);
     *
     * 如文本是HTMLText时:
     * var text:String = <P><FONT COLOR='#979797' >这个一个图文混排的容器类</FONT><img width='20',height='20' src='/data/index.jpg'/><br/><FONT COLOR='#babec2' >支持HTML格式文本</FONT><P>
     * var tc:TextContainer = new TextContainer(200);
     * tc.setHtmlText(text);
     *
     * 如果要获取TextContainer的高度,需要等处理显示完成后才能更新到正确的宽高,所以需要监听SETTEXTCOMPLETED事件
     * tc.addEventListener(TextContainer.SETTEXTCOMPLE,onSetTextCompleted,false,0,false);
     * function onSetTextCompleted(e:Event):void{
     *         var w:Number = tc.width;
     *         var h:Number = tc.height;
     *         updateSize();
     * }
     *
     */
    public class TextContainer extends Sprite
    {
        private var _textFormat:TextFormat; //文本格式
        private var _maxWidth:Number;       //容器的宽
        private var textStr:String;
        private var imgObjectArr:Array=new Array();
        private var textArr:Array=new Array();
        
        private var tempTf:TextField;
        
        private var _placeholder:String;
        private var _placeholderColor:uint;
        private var _placeholderMarginH:int;    
        private var _placeholderMarginV:int;
        private var _formatCalculator:TextField;
        
        private var loader:Loader;


        private var loadImgComplete:Function;
        private var loadIoError:Function;
        private var textLineHeight:Number;
        private var originalText:String;
        public static const SETTEXTCOMPLETED:String="SetTextCompleted";
        private const margins:int=5;
        private var _Number;
        private var _height:Number;
        private var isHtml:Boolean = false;
        
        private static var DEFAULT_FONT:String = "微软雅黑,Microsoft YaHei";
        
        /**
         *
         * @param maxWidth 文本容器最大宽度
         * @param format 默认文本
         *
         */
        public function TextContainer(maxWidth:Number=100,format:TextFormat=null)
        {
            this.mouseEnabled = this.mouseChildren = false;
            _textFormat=format;
    
            if(format!=null){
                _textFormat=format;
            }else{
                _textFormat=new TextFormat();
                _textFormat.font=DEFAULT_FONT;
            }
        
            _maxWidth = maxWidth;
            
            _placeholder="&nbsp;";
            _placeholder=StringUtil.DecodingAscIIStr(_placeholder);
            _placeholderColor = 0x000000;
            _placeholderMarginH = 1;
            _placeholderMarginV = 0;
            _formatCalculator = new TextField();            
            _formatCalculator.text = _placeholder;
            textLineHeight=Number(_textFormat.size);
            
        }
        
        /**
         * 设置HTML格式的文本
         * @param htmlText
         *
         */
        public function setHtmlText(htmlText:String):void{
            isHtml = true;
            setText(htmlText);
        }
        
        /**
         * 重置
         *
         */
        private function reset():void{
            
            this.removeChildren();
            if(originalText != null)
                imgObjectArr.length=0;
            originalText = "";
        }
        /**
         * 设置文本
         * @param text
         *
         */
        public function setText(text:String):void{
            reset();
            originalText=text;
            if(text==null||text.length<1){
                trace("the text is has nothing");
                return;
            }
        
            textStr=StringUtil.DecodingAscIIStr(text);
        
            StringUtil.regExp_allMark.lastIndex=0;
            //判断是否纯文本
            if(!StringUtil.regExp_allMark.test(textStr)){
                //纯文本
                isHtml = false;
                showText(textStr);
            }else{
                //判断是否有图片标签
                if(!StringUtil.hasImgTag(textStr)){
                    showText(textStr);
                }else{
                    //截取图片标签
                    catchImgTag(textStr);
                    textStr=textArr.join("");
                    if(imgObjectArr.length<=0){
                        showText(textStr);
                    }else{
                        showText(textStr,imgObjectArr);
                    }
                }
            }
        }
        
        /**
         * 获取文本
         * @return
         *
         */
        public function getText():String{
            return originalText;
        }

        /**
         * 显示文本
         * @param text 显示的文本
         * @param SpritesArr 插入文本中的图像信息数组
         *
         */
        private function showText(text:String,SpritesArr:Array=null):void{
            if(text == null || text.length <1)return;
            var textLength:int=0;
            if(tempTf == null){
                tempTf=createTextField();
            }
            
            if(isHtml){
                tempTf.htmlText = text;
            }else{
                if(text) text=text.split("\r").join("\n");
                tempTf.text=text;
                if(this._textFormat==null){
                    _textFormat = new TextFormat();
                    _textFormat.font = DEFAULT_FONT
                    _textFormat.font=DEFAULT_FONT;
                }
                tempTf.setTextFormat(this._textFormat,0,tempTf.length);
            }
            
            textLength = tempTf.length;
            if(SpritesArr!=null){
                insertSpritesPlaceholder(SpritesArr,textLength);
                loadImages(SpritesArr);
            }else{
                
                this.addChild(tempTf);
                _width=tempTf.width;
                _height=tempTf.height;
                this.dispatchEvent(new Event(TextContainer.SETTEXTCOMPLETED));
            }
        }
        
        /**
         *将sprites数组中的显示元素的宽度占位符插入到指定文本的位置,生成一个有占位符的String
         * @param Sprites
         * @param maxIndex 文本最大长度
         *
         */
        
        private function insertSpritesPlaceholder(Sprites:Array,maxIndex:int):void{
            if(Sprites==null)return;
            Sprites.sortOn("index",Array.NUMERIC);
            if(textLineHeight<=0) textLineHeight=10;
            for (var i:int=0;i<Sprites.length;i++){
                var obj:ImgObject=Sprites[i] as ImgObject;
                if(obj.index<0||obj.index>maxIndex){
                    obj.index=maxIndex;
                    Sprites.splice(i,1);
                    Sprites.push(obj);
                    i--;
                    continue;
                }
                var ai:int=insertASPlaceholder(obj);
            
                if(ai>0){
                    for(var j:int=i+1;j<Sprites.length;j++){
                        Sprites[j].index+=1;
                    }
                    maxIndex+=1;
                }
                maxIndex+=1;
            }
        }
        
        /**
         *生成一个obj位置的占位符
         * @param obj
         *
         */
        private function insertASPlaceholder(obj:Object):int{
            if(obj==null ||!(obj is ImgObject))
                throw Error("The object is not ImgObject");
            var imgObj:ImgObject=obj as ImgObject;
            
            if(imgObj.imgWidth>this._maxWidth){
                var whRate:Number=imgObj.imgWidth/imgObj.imgHeight;
                imgObj.imgWidth=this._maxWidth-margins;
                imgObj.imgHeight=Math.round(imgObj.imgWidth/whRate);
            }
            var index:int=imgObj.index;
            
            var format:TextFormat=CalcPlaceholderFormat(imgObj.imgWidth,textLineHeight);
            var tpindex:Number=0;
            var ct:TextField= createTextField();
            var addIndex:int=0;
            ct.defaultTextFormat=tempTf.defaultTextFormat;
            for(var i:int=0;i<tempTf.numLines;i++){
                if(tempTf.getLineOffset(i)<=index&&index<=tempTf.getLineLength(i)+tempTf.getLineOffset(i)){
                     ct.text=tempTf.getLineText(i).slice(index-tempTf.getLineOffset(i)+1,tempTf.getLineLength(i));
                    if((Number(format.letterSpacing)+tempTf.getLineMetrics(i).width-ct.getLineMetrics(0).width)>tempTf.width){
                        tempTf.replaceText(index,index,"\r");
                        addIndex=1;
                        index+=addIndex;
                    }
                    break;
                }
            }
            
            imgObj.index=index;
            tempTf.replaceText(index,index,_placeholder);
            tempTf.setTextFormat(format,index,index+1);
            return addIndex;
        }
        
        /**
         * 计算显示元素的占位符的文本格式(若使用不同的占位符,可重写此方法)。
         * @param    width 宽度。
         * @param    height 高度。
         * @return
         */
        private function CalcPlaceholderFormat(Number, height:Number):TextFormat
        {
            var format:TextFormat = new TextFormat();
            format.font = "Tahoma";
            format.color = _placeholderColor;
            format.size =Math.round( height + 2 * _placeholderMarginV);        
            _formatCalculator.setTextFormat(format);
            var metrics:TextLineMetrics = _formatCalculator.getLineMetrics(0);
            
            format.letterSpacing =Math.ceil( width - metrics.width + 2 * _placeholderMarginH);
            format.underline = format.italic = format.bold = false;
            return format;
        }
        
        /**
         * 将图像<img>html代码从文本中分离出来,将其存进一个imgObject中,
         * 在插入图像时从imgObject获取它在文本中的位置
         */
        private function catchImgTag(text:String):void{
            
            var imgTagArr:Array;
            imgObjectArr.length=0;
            textArr.length=0;
            if(StringUtil.hasImgTag(text)){
                imgTagArr=text.match(StringUtil.regExp_IMG);
                textArr=text.split(StringUtil.regExp_IMG);
                if(isHtml){
                    //如果是html文本,需要先将其转成普通文本
                    //否则html标签都会被当成字符,影响字符位置的计算
                    //同时要将<img>标签转成非html标签
                    for(var i:int=0;i<imgTagArr.length;i++){
                        text=text.replace(imgTagArr[i],"[IMG]");
                    }
                    var temp:TextField = createTextField();
                    temp.htmlText = text;
                    text = temp.text;
                }
                //创建图像信息对象
                for(var i:int=0;i<imgTagArr.length;i++){
                    var imgObj:ImgObject=new ImgObject();
                    imgObj.imgXml=XML(imgTagArr[i]);
                    imgObj.id=imgObj.imgXml.@id;
                    imgObj.imgWidth=imgObj.imgXml.@width;
                    imgObj.imgHeight=imgObj.imgXml.@height;
                    imgObj.src=imgObj.imgXml.@src;
                    //计算图像在文本中的位置,用于之后的插入图像
                    if(isHtml){
                        imgObj.index = text.indexOf("[IMG]")+i
                        text=text.replace("[IMG]","");
                    }else{
                        imgObj.index=text.indexOf(imgTagArr[i])+i;
                        text=text.replace(imgTagArr[i],"");
                    }
                    
                    imgObjectArr.push(imgObj);
                }
            }
        }
        
        //加载图像数据
        private function loadImages(SpritesArr:Array):void{
            loadOtImages(SpritesArr,0);
        }
        
        /**
         * 循环加载图像数组
         * @param SpritesArr 图像信息数组
         * @param index 加载数组中的项索引
         *
         */

        private function loadOtImages(SpritesArr:Array,index:int):void{
    
            if(index<0||index>SpritesArr.length-1){
                //这里要调用另一个方法,在加载完成后调用
                replacePlaceholderToImg(SpritesArr);
                return;
            }
            
            var imgObj:ImgObject=SpritesArr[index] as ImgObject;
            
            loadImgComplete=function(dobj:DisplayObject):void{OnCompleteHander(dobj,imgObj,SpritesArr,index)};
            loadIoError=function (){OnIOErrorHandler(SpritesArr,index)};
            LoaderPool.getInstance().load(imgObj.src,loadImgComplete,loadIoError);
        }
        
        private function OnCompleteHander(dobj:DisplayObject,imgObj:ImgObject,SpritesArr:Array,index:int):void{
            if(imgObj!=null && dobj != null && (dobj is Bitmap)){
                //imgObj.image=e.target.content as Bitmap;
                
                imgObj.image = cloneBMP(dobj as Bitmap);
                if(imgObj.image!=null){
                    if(imgObj.imgWidth<imgObj.image.width||imgObj.imgHeight<imgObj.image.height){
                        imgObj.image.width=imgObj.imgWidth;
                        imgObj.image.height=imgObj.imgHeight;
                    }
                }
                index++;
                loadOtImages(SpritesArr,index);
            }
        }
        
        private function cloneBMP(bmp:Bitmap):Bitmap{
            if(bmp == null) return null;
            var bmp:Bitmap = new Bitmap(bmp.bitmapData,bmp.pixelSnapping,bmp.smoothing);
            return bmp;
        }
        
        private function OnIOErrorHandler(SpritesArr:Array,index:int):void{
            ImgObject(SpritesArr[index]).image=null;
            index++;
            loadOtImages(SpritesArr,index);
        }
        
        /**
         * 该方法要实现的功能就是从tempTf中分离出有图片所在的那一行文本,从新调整当行的高度和图片的位置,然后再将其插入之前文本所在的位置
         * 根据textField获取每一行的长度(getLineLength)和每行第一个字符索引(getLineOffset)来确定图片所在的行,再用getLineText获取该行
         * 的文本保存到一个新的textField中,该textField的format要与原来的相同。
         *  
         * @return
         *
         */        
        
        private function replacePlaceholderToImg(sprites:Array):void{
            
            var currentLine:int=0; //当前行
            var currentLineStarIndex:int=0; //当前行第一个索引
            var currentLineEndIndex:int=0; //当前行最后一个索引
            var imgLine:int=0;//图片所在文本行
            var tfLineInfo:Object=new Object();
            var imgObj:ImgObject;
            var flag:Boolean=false;
            var tempSprite:Sprite;
            var currentImgs:Array=new Array();
            var heightPos:Number;
            
            sprites.sortOn("index",Array.NUMERIC);
            //创建一个textfield
            var tfpa:TextField=createTextField();
            tfpa.defaultTextFormat=tempTf.defaultTextFormat;
            heightPos=0;
    
            var htmlList:Array;
            htmlList= tempTf.htmlText.match(StringUtil.regExp_P);
            currentLineStarIndex = 0;
            for(var i:int=0;i<htmlList.length;i++){
                currentLine=i;
                var tfitem:TextField=createTextField();
                
                tfitem.htmlText = htmlList[i];
                currentLineStarIndex = currentLineEndIndex + (i==0?0:1);
                currentLineEndIndex= currentLineStarIndex + tfitem.length-1;
                currentImgs.length=0;
                for(var j:int=0;j<sprites.length;j++){
                    imgObj=(sprites[j] as ImgObject);
                    if(currentLineStarIndex<=imgObj.index&&currentLineEndIndex>=imgObj.index){
                        //如果该行有图片,创建一个sprite,该sprite的高是图片的高度,宽度是textfield的宽
                        if(imgObj.image!=null){
                            tfitem.setTextFormat(tempTf.getTextFormat(imgObj.index,imgObj.index+1),imgObj.index-currentLineStarIndex,imgObj.index-currentLineStarIndex+1);
                            currentImgs.push(imgObj);
                            flag=true;
                        }else{
                            //trace(tfitem.text);
                            //tfitem.replaceText(imgObj.index-currentLineStarIndex,imgObj.index-currentLineStarIndex+1,"");
                            //trace(tfitem.text)
                            //tempTf.replaceText(index,index,"\r");
                        }
                    }
                }
                
                //如果该行有图片再创建一个新的textfield继续下一个循环
                var spriteHeight:Number;
                
                if(tfpa != null){
                    tfpa.defaultTextFormat=tempTf.defaultTextFormat;
                }
                if(flag){
                    currentImgs.sortOn("imgHeight",Array.NUMERIC);
                    tempSprite=new Sprite();
                    spriteHeight=currentImgs[currentImgs.length-1].imgHeight>tfitem.textHeight?currentImgs[currentImgs.length-1].imgHeight:tfitem.textHeight;
                    spriteHeight=spriteHeight;
                    currentImgs.sortOn("index",Array.NUMERIC);
                    tempSprite.addChild(tfitem);
                    tfitem.x=0;
                    tfitem.y=spriteHeight-tfitem.height+tfitem.getLineMetrics(0).leading;
                    for(var k:int=0;k<currentImgs.length;k++){
                        var rect:Rectangle =tfitem.getCharBoundaries(currentImgs[k].index-currentLineStarIndex);    
                        if (rect != null)
                        {
                            currentImgs[k].image.x = (rect.x + (rect.width - currentImgs[k].image.width) * 0.5 + 0.5) >> 0;
                            currentImgs[k].image.y=spriteHeight-currentImgs[k].imgHeight;
                            tempSprite.addChild(currentImgs[k].image);
                        }
                    }
                    tempSprite.x=0;
                    tempSprite.y=heightPos;
                    heightPos=tempSprite.y+tempSprite.height;
                    this.addChild(tempSprite);
                    tfpa=createTextField();
                    flag=false;
                }
                else if(!this.contains(tfpa)){
                    tfpa.htmlText = tfpa.htmlText +tfitem.htmlText;
                    tfpa.x=0;
                    tfpa.y=heightPos;
                    this.addChild(tfpa);
                    heightPos=tfpa.y+tfpa.height;
                }else{
                    tfpa.htmlText = tfpa.htmlText +tfitem.htmlText;
                    heightPos=tfpa.y+tfpa.height;
                }
                
                if(i>0){
                    _width=maxWidth;
                }else{
                    _width=tfitem.width;
                }
                
                _height=super.height;
            }
            this.dispatchEvent(new Event(TextContainer.SETTEXTCOMPLETED));
        }
        
        public function update():void{
            if(originalText!=null&&(originalText!=""||originalText.length>0)){
                setText(originalText);
            }
        }

    
        
        /**
         * 创建新的TextField对象
         * @return
         *
         */
        private function createTextField():TextField{
            var tf:TextField=new TextField();
            tf.width=_maxWidth;
            tf.autoSize=TextFieldAutoSize.LEFT;
            tf.multiline=true;
            tf.wordWrap=true;
            if(_textFormat!=null){
                tf.defaultTextFormat=_textFormat;
            }else{
                _textFormat=new TextFormat();
                _textFormat.font=DEFAULT_FONT;
                tf.defaultTextFormat=_textFormat;
            }
            return tf;
        }


        /**
         * 获取默认的文本格式
         * @return
         *
         */
        public function get defalutFormat():TextFormat
        {
            return _textFormat;
        }
        
        /**
         * 设置默认的文本格式
         * @return
         *
         */
        public function set defalutFormat(value:TextFormat):void
        {
            _textFormat = value;
            update();
        }

        /**
         * 获取宽度最大值
         * @param value
         *
         */
        public function get maxWidth():Number
        {
            return _maxWidth;
        }

        /**
         * 设置宽度最大值
         * @param value
         *
         */
        public function set maxWidth(value:Number):void
        {
            _maxWidth = value;
            update();
        }
        
        
        override public function get height():Number
        {
            return _height;
        }
        
        override public function set height(value:Number):void{
            
            //_height = value;
            throw new Error("不能直接设置TextContainer的高度");
        }
        
        override public function get width():Number
        {
            return _width;
        }
        
        override public function set width(value:Number):void{
            //_width = value;
            throw new Error("不能直接设置TextContainer的宽度,请用maxWidth设置最大宽度");
        }

    }
}

原文地址:https://www.cnblogs.com/skybdemq/p/2574893.html