案例18游戏聊天框的制作

赶在过年前为列位道友奉献一篇文章解决一下很多人头疼的“图文混排”问题。何谓图文混排问题呢?我们知道在Flash里面使用TextField可以输入或显示文本,但是它并不支持像QQ聊天框一样能够输入表情和图片的功能。那怎么办呢?怎么办呢?到底怎么办?999皮炎平来啦,迅速消灭真菌……啊?啊?刚才发生什么事了?我只感觉闻到一阵脚臭味之后就两眼一黑,之后神马都不知道了。好吧,回到正题。我们知道TextField中有一个htmlText属性,可以设置它为一串带HTML标签的字符串,如:
  1. textField.htmlText = "<font color='#ff0000'>我勒个去</font>";
复制代码
这样子做的话就会看到TextField中会显示出一串颜色为红色的字了(对HTML标签知识不熟的道友可以去网上搜索学习一二)。在HTML标签中存在一个插入图片的标签是:<img source="..."/>看到这里,很多Flash开发人员仿佛捡到了一颗救命稻草,纷纷在TextField的htmlText属性里写上了带有img标签的字符串,但是显示出来的效果却不如人意,且不能够控制其位置。
     为此,别无选择的我们只好使用拼接的方式来达到预期效果,即做把图文混排组件做成两层,底层是一个TextField,负责显示文本,上面盖一层Sprite负责显示表情与图片。说起来简单做起来难,因为我们还需要考虑很多其他的因素,比如:在一个点插入表情后怎样不让上层表情图片覆盖住文本;删除表情的时候事实上我们是在删除一段表请图案下的文本,那如何让文本被删除的过程中实现表情的删除呢;如何控制根据表情图片的大小动态调整一行文本的行高与行宽呢……
    达奇同学在Google Code(Google公司提供的一个在线的开发人员共享库,更多信息请自行搜索)上找到一个很不错的AS组件,名字叫做RichTextField,比较不错地解决了图文混排的难题,细节做得也非常到位,体积小,运行效率高。(点击此链接来查看作者博客中对此组件的相关介绍及演示:http://www.riaidea.com/blog/archives/295.html)对于此组件的作者:Flashlizi  我了解不是很多,但是看他的代码让我学了不少东西,小生对其表示强烈的敬意,并对我们国人能开发出如此不错的组件表示自豪啊。他的博客也值得我们关注哦~(http://www.riaidea.com
    下面让我们开始学习如何使用之吧。首先去Google Code上下载最新版本的RichTextField代码压缩包(RichTextField2.0_src.zip )。压缩包中有好多文件夹,我们看到:
<ignore_js_op>1.jpg 
    在bin文件夹中能够看到组件演示的swf,要是想在Flash Builder中自己编译此演示应用的话就需要用到src和lib文件夹中的文件。首先,新建一个ActionScript项目,把src文件夹下面的全部文件拷贝到我们项目的src目录下,然后右键点击tests目录下的Test.as文件,在弹出目录中选择“将此文件作为默认应用程序”(Flash CS工具就直接把src\tests目录下的Test.as文件作为文档类即可)。之后如果你编译,将会报错提示文档类中有一些类没有定义。这是因为我们没有将那些定义在swc中的代码编译进项目中。复制lib文件夹并粘贴至我们工程根目录下,然后设置项目属性把lib文件夹作为swc目录编译进我们的项目(在Flash Buidler中进行此操作的具体流程参看案例16的开始部分)。这样我们就可以运行查看演示应用的效果了。
   在doc目录下面我们可以双击index.html文件查看RichTextField组件提供的一些API信息,再结合演示应用Test.as文件的代码掌握此组件的使用方法。如果正在浏览此帖子的你对自己阅读代码的能力信心不足,就让我带着可爱的,卡哇伊的你们一起来熟悉一下此组件的使用方法好了。
   打开Test.as文件,结合说明文档我们可以很快理解RichTextField一些属性的意义,这样就迅速略过一系列的初始化的代码,看到下面这一段代码段
  1.                 //output scrollbar
  2.                         _outputScrollBar = getUIScrollBar();
  3.                         _outputScrollBar.scrollTarget = _output.textfield;
  4.                         _outputScrollBar.x = _output.x + _output.width;
  5.                         _outputScrollBar.y = _output.y;
  6.                         _outputScrollBar.height = _output.viewHeight;
  7.                         addChild(_outputScrollBar);
复制代码
首先调用getUIScrollBar方法返回一个设置了皮肤的滚动条(滚动条组件UIScrollBar是Flash CS工具自带的fl包中的组件,关于如何在FB或者FD中使用CS专属组件请参见:http://bbs.9ria.com/viewthread.php?tid=38420&rpid=343886&ordertype=0&page=1#pid343886。另外,用到的这些皮肤类都在kingnarestyle.swc中已经制作好了,有关组件皮肤的制作方法,你需要为FLASH CS安装一个插件,具体操作还还还请您自行搜索之),之后我们为滚动条指定滚动对象为_output.textfield,这是RichTextField中的一个TextField对象。设置了之后你还需要为滚动条设置高度它才能够正常使用。看完上面这几条语句之后,如果有人问你如何给一个TextField对象添加上滚动条,你就叫他去看这一段代码好了。
     在init函数中作者声明了两个RichTextField组件,分别是_output 和 _input,他们的type属性是不一样的,RichTextField的type属性可选参数有:
RichTextField.DYNAMIC: 动态文本;
RichTextField.INPUT:可输入文本;
     在不设置type属性时默认类型是动态文本,作者没有设置_output的类型,所以它是动态文本,仅负责显示;设置了 _input类型为可输入文本,所以它就成了用户的文本输入框。之后作者写了这么一段:
  1.                         //predefine text
  2.                         _input.html = false;
  3.                         _input.append("你好,welcome", [ { index:2, src:_smileys[1] }, { index:15, src:_smileys[5] }, { src:_smileys[6] } ]);        
  4.                         _input.replace(4, 11, "欢迎", [ { src:_smileys[4] } ]);
  5.                         _input.caretIndex = _input.contentLength;
复制代码
是在告诉我们以下几点:
1.如不需要超链接,设置颜色等需要用到HTML标签元素时还是把html属性设置为false较好。
2.告诉我们用一个表情替换一段字符串的方法。
3.设置caretIndex 属性可以设置文本框中光标的位置
      接下来我们看到作者在为表情容器_smileyContainer进行初始化。_smileyContainer中将存放所有可用的表情,而这些表情类都存放于_smileys数组当中,而且他这个例子中用到的表情都集成在了lib文件夹下的smileys.swc中了。了解到这一点后我们就知道应该怎样添加我们想添加的表情了。我带着大家一起来做一个QQ表情放进来吧。先打开QQ,随便找一个倒霉蛋聊天,在聊天框里面选择一个表情,之后右键点击此表情并选择“另存为”:
<ignore_js_op>2.jpg 
     在另存为框中随便取个名字,如果是有动画的表情请选择保存类型为.gif类型,若是静态图片表情么格式就随便你了。之后,打开Flash CS工具,选择文件-->导入-->导入到库,之后选择我们刚才保存的gif表情文件。这样我们就会看到库中出现了很多gif动画用到的图片以及CS工具为我军自动生成的一个已做成动画的元件。
<ignore_js_op>3.jpg 
     虽然我们需要的动画元件CS工具已自动帮咱们做好了,但该元件的属性还没有设置过呢,打开此元件的属性设置面板,将它导出为Actionscript,让“在第一帧中导出”的钩被勾选上以保证它能被顺利地编译成swc。
<ignore_js_op>4.jpg 
   之后要做的就是去文件-->发布设置中把“导出SWC”的钩钩上

  好,最后保存,然后Ctrl + Enter测试影片,之后我们可以在fla保存目录下找到编译生成的swc文件,把它拷贝到我们的lib目录下面,这样就把我们的自定义表情编译到项目里面啦。把自定义表情元件的类定义加入到_smileys数组中去吧:
  1. private var _smileys:Array =[....., kouBiKong]
复制代码
好了,编译运行一下,就会看到我们的自定义表情出现在了表情选择框中。为了保证表情选择框中所有表情大小一致,我们需要为createSmileys方法中加上两句话限制表情大小:
  1.                 private function createSmileys():void
  2.                 {
  3.                         for (var i:int = 0; i < _smileys.length; i++)
  4.                         {
  5.                                 var smiley:Sprite = new _smileys[i]() as Sprite;
  6.                                 smiley.width = 26; 
  7.                                 smiley.height = 23;
  8.                                 ……        
  9.                         }
  10.                 }
复制代码
<ignore_js_op>6.jpg 
   最后,让我们属性一下RichTextField组件为我们提供的强大的外部插件功能。作者已经为我们提供了一个元素快捷符号输入插件ShortcutPlugin,并在Test.as中使用上去了。
  1. //shortcut plugin
  2.                         var splugin:ShortcutPlugin = new ShortcutPlugin();                        
  3.                         var shortcuts:Array = [ { shortcut:"/a", src:_smileys[0] },
  4.                                                                         …… ];
  5.                         splugin.addShortcut(shortcuts);
  6.                         _input.addPlugin(splugin);
复制代码
这段代码已经清楚地告诉我们如何为RichTextField“安装”插件并演示了元素快捷符号输入插件的使用方法,当用户输入shortcut中的字符后此字符会自动转换成src中定义的表情。若有任何功能扩展上的需要,我们大可自己编写插件。
<ignore_js_op>

5.jpg (54.63 KB, 下载次数: 8)

 

5.jpg

虽然在Flex4以及Flash CS5中存在textLayout这个东西可以用,不过这需要最新版本SDK的支持,且如果使用Flex4 SDK的话会使编译结果大小增大数倍,代价有些大,所以一般的开发者大可使用这款存AS的轻量级组件。
请允许寡人做一些小小的拓展
      RichTextField组件提供给我们了图文混排的功能,但是在一些应用中我们仍需要更多的一些自定义功能,让我们一起来做两个需求度最高的功能。
改变字体颜色与大小
      当遇到要改变字体颜色,大小等格式时第一时间就会想到TextFormat,于是我们先试着这样做一下看看:
  1. public function setTextColor( newColor:uint ):void{
  2.         _textRenderer.defaultTextFormat.color = newColor;
  3.         _textRenderer.setTextFormat( _textRenderer.defaultTextFormat );
  4. }
  5.         
  6. public function setTextSize( newSize:uint ):void{
  7.         _textRenderer.defaultTextFormat.size = newSize;
  8.         _textRenderer.setTextFormat( _textRenderer.defaultTextFormat );
  9. }
复制代码
运行结果如下:
改变字体颜色:
<ignore_js_op>1.jpg 
设置更大号的字体:
<ignore_js_op>2.jpg 
纳尼?what the fuck!!我们设置了新格式的文本有的会被表情所遮挡,这是为什么?!看来事情并没有想象中的那样简单啊。先来研究一下RichTextField实现图文混排的原理再说,我们看到RichTextField中插入表情的方法insertSprite:
  1. public function insertSprite(newSprite:Object, index:int = -1, autoRender:Boolean = true, cache:Boolean = false):void
  2. {
  3.         ……                        
  4.                                 
  5.         //insert a placeholder into textfield by using replaceText method
  6.         _textRenderer.replaceText(index, index, _placeholder);                        
  7.         //calculate a special textFormat for spriteObj's placeholder
  8.         var format:TextFormat = calcPlaceholderFormat(spriteObj.width, spriteObj.height);
  9.         //apply the textFormat to placeholder to make it as same size as the spriteObj
  10.         _textRenderer.setTextFormat(format, index, index + 1);        
  11.         
  12.                  ……                
  13.         }
复制代码
这里的三句话是关键,我们之前说过,实现图文混排的思路就是在文本区域textField上方再盖一层MovieClip或者Sprite表情层,不过为了让表情插入后编辑的文字不会被表情所覆盖,作者在表情所在位置下方的textField上放上了一个占位符(这里设置的是一个全角的空格字符),并需要通过calcPlaceholderFormat方法计算出一个根据表情宽度设置的textFormat格式应用到占位符上以保证占位符和表情等宽。因此,我们发现,在文本区域中,一般文字和表情占位符所采用的textFormat是不一样的。
    所以我们就发现了问题的所在,不能把一般文字和表情占位符应用同一个textFormat,否则就不能保证表情占位符和其所对应的表情等宽。既然这样我们就只能在改变颜色,字体等格式时忽略表情占位符,在RichTextField中写出我们新增的方法:
  1. private var _startIndex:int = -1;
  2. /**
  3. * 设置字体颜色 
  4. * @param newColor
  5. */                
  6. public function setTextColor( newColor:uint ):void{
  7.         _textRenderer.defaultTextFormat.color = newColor;
  8.         _startIndex = -1;
  9.         LoopFunction();
  10. }
  11.                 
  12. /**
  13. * 设置字体大小 
  14. * @param newSize
  15. */                
  16. public function setTextSize( newSize:uint ):void{
  17.         _textRenderer.defaultTextFormat.size = newSize;
  18.         _startIndex = -1;
  19.         LoopFunction();
  20. }
  21.                 
  22. //递归寻找占位符所在索引,把除占位符以外字符全部更新字体格式
  23. private function LoopFunction( ):void{
  24.         var index:int = _textRenderer.text.indexOf( _placeholder, _startIndex );
  25.         if( index > -1 ){
  26.                 _textRenderer.setTextFormat( _textRenderer.defaultTextFormat, _startIndex, index );
  27.                 _startIndex = index+1;
  28.                 LoopFunction();
  29.         }else{
  30.                 _textRenderer.setTextFormat( _textRenderer.defaultTextFormat, _startIndex, _textRenderer.length );
  31.         }
  32. }
复制代码
好,这样一来,更改字体颜色时是不会有问题了,但是在缩放字体大小时还是会发生表情遮挡文字的现象,这是因为表情图案没有随着字体改变而改变位置,那么有没有现成的神马函数可以提供给我们刷新表情位置的功能呢?哦也,还真有,在表情渲染器SpriteRender的render方法中我们看到它会用到一个叫做renderSprite的方法,此方法会根据文本中占位符的位置来调整表情的x,y位置。为了避免重复地addChild同一个表情,我们为此方法最后一句addChild语句加上一个条件:
  1. private function renderSprite(sprite:DisplayObject, index:int):void
  2.                 {                        
  3.                         var rect:Rectangle = textRenderer.getCharBoundaries(index);        
  4.                         if (rect != null)
  5.                         {
  6.                                 ……
  7.                                            //仅在未渲染此表情时才addChild
  8.                                 if( !_spriteContainer.contains(sprite) )_spriteContainer.addChild(sprite);
  9.                         }
  10.                 }
复制代码
最后别忘了回到RichTextField中的LoopFunction方法中给else语句最后加上刷新表情的语句:
  1. private function LoopFunction( ):void{
  2. ……        
  3. else{
  4.          _textRenderer.setTextFormat( _textRenderer.defaultTextFormat, _startIndex, _textRenderer.length );
  5.          _spriteRenderer.render();  //刷新表情坐标
  6. }
  7. }[/
复制代码
好了,大功告成……
嵌入特殊元素
        玩过魔兽世界或者其他类似网游的道友们应该见识过在聊天框里面出现一个以特殊格式出现的武器名或者人物名,点击之会弹出一个窗口显示该武器或人物的详细信息,我把这一类可点击的东西叫做特殊元素。
        那么这是如何做到的呢?在网上搜了半天也没有搜索到什么参考资料,看来国内的高手们都比较低调,那只能让贫道来给出一些引导性的方案仅供参考。我在参考手册里找到TextField拥有一个叫做Link的事件,对于此事件,帮助手册上的解释如下:
当用户单击启用 HTML 的文本字段中的超链接(其中的 URL 以“event:”开头)时调度。 URL 中“event:”后的其余部分将放在 LINK 事件的文本属性中。
        在帮助手册中给出的例子里我们能看到详细的用法,若想触发此事件,我们必须以如下格式来修饰一段文本:
<a href="event:track1.mp3">Track 1</a>
        这样做的话当鼠标移到Track 1上时会显示手型光标,且点击后TextField会抛出一个Link事件,此事件中的text属性中保存了event后面的一段文本,即track1.mp3。但Link事件的一个局限性就是只能通过text属性来传递一个字符串,而不能传递别的对象,那为了让外部得知用户点击的是哪个物品或者人物,就只能给每个特殊元素对象设置一个唯一的id,并且让Link事件来携带此id字符串,当外部侦听到Link事件时可以根据text属性所携带的id信息去数据模型里面查找此id所对应的特殊元素对象。我们来看看代码:
       首先需要定义一个特殊元素的VO类型:
ItemVO.as:
  1.         public class ItemVO
  2.         {
  3.                 public var id:String;
  4.                 public var name:String;
  5.                 public var type:String;
  6.                 public var attack:Number;
  7.                 public var defence:Number;
  8.                 public var weight:Number;
  9.         }
复制代码
这和“案例15--游戏物品栏的制作”中的物品VO是一样的。接下来需要定义一个简单的数据模型(单例):
GameModel.as:
  1.         public class GameModel
  2.         {
  3.                 public var itemList:Array = new Array();
  4.                 private static var instance:GameModel = null;
  5.                 
  6.                 public function GameModel()
  7.                 {
  8.                 }
  9.                 
  10.                 public static function getInstance():GameModel{
  11.                         return instance ||= new GameModel();
  12.                 }
  13.         }
复制代码
这个单例只是随手定义的,严谨性神马的都懒得管了,贫道追求的是速度~其中的itemList数组保存了全部被嵌入聊天框的ItemVO。接下来我们需要先考虑一下降低耦合性的问题,Link事件的触发者来自RichTextField中的一个私有属性_textRenderer,所以我们最好把事件侦听函数直接写在RichTextField里面,那么外部如何获知用户点击的是哪个特殊元素呢?自然是通过发送自定义事件来得快速方便。于是我们定义一个自定义事件ElemClickedEvent:
ElemClickedEvent.as:
  1.         public class ElemClickedEvent extends Event
  2.         {
  3.                 public var ElemType:int;
  4.                 public var id:String;
  5.                 
  6.                 public function ElemClickedEvent(type:String, ElemType:int=0, id:String="", bubbles:Boolean=false, cancelable:Boolean=false)
  7.                 {
  8.                         super(type, bubbles, cancelable);
  9.                         this.ElemType = ElemType;
  10.                         this.id = id;
  11.                 }
  12.         }
复制代码
此事件可以携带两个信息:特殊元素类型(物品或者人物)以及特殊元素id,可以用它去数据模型中查找id所对应的ItemVO实例。OK,准备工作做好,先稍作休息,一会儿我们继续开工~
 
好了,各位休息够了吧?Are you fucking ready? Let`s fucking go!
       首先要做的自然是在RichTextField构造函数中为文本区域对象_textRenderer侦听Link事件,不过不是每个RichTextField对象中都会存在嵌入元素,所以如果直接为他们都添加Link事件侦听将会让那些没有嵌入元素的RichTextField对象中存在着没有用的Link侦听器使Flash Player占有更多的CPU。有的人认为用不到的东西都会被Flash Player垃圾回收,实则不然,对于一般的事件侦听器一旦被添加就不会被垃圾回收,除非给它们设置弱引用,所幸Adobe官方在addEventListener提供了一个参数来为事件侦听器设置弱引用,这个参数在addEventListener的第5个参数位置,我们保持第3,4个参数为默认值,给第五个参数设置为true即可让事件侦听器采用弱引用机制,当该侦听器一段时间被闲置后就会被垃圾回收,节省内存。
  1. public function RichTextField()
  2.                 {
  3.                         ……
  4.                         _textRenderer.addEventListener( TextEvent.LINK, onLink, false, 0, true );
  5.                 }
复制代码
接下来为RichTextField加上两个新方法:
  1. public static const ELEM_CLICKED_EVENT:String = "elem clicked event";
  2.                 /**
  3.                  * 嵌入特殊元素,特殊元素在被点击时会显示出其详细信息 
  4.                  * @param type        元素类别
  5.                  * @param text        元素文本
  6.                  * @param info        元素实例
  7.                  * 
  8.                  */                
  9.                 public function insertSpecialElem(type:int, text:String, info:ItemVO):void{
  10.                         //注意在</a>末尾加一个空格,不然在超链接文本之后编辑的任何文字都会带有超链接
  11.                         _textRenderer.htmlText += "<a href='event:" + type + "_" + info.id + "'><font color='#9681b6'><u>" + text + "</u></font></a> ";
  12.                         GameModel.getInstance().itemList.push( info );
  13.                 }
  14.                 
  15.                 private function onLink(event:TextEvent):void{
  16.                         var ary:Array = event.text.split("_");
  17.                         dispatchEvent( new ElemClickedEvent(ELEM_CLICKED_EVENT, ary[0], ary[1]) );
  18.                 }
复制代码
这两个方法,一个是提供给外部的API,可以调用此方法来给RichTextfield添加一个特殊元素,它有三个参数,其中text是显示在文本区域的文本;type是特殊元素的类别,外部可以根据类别的不同采用不同的行为,比如物品类被点击后会显示一个物品详情窗口,而人物被点击后会出现一个菜单,菜单上写着“交易”,“加为好友”,“打他娘的”之类的选项;info参数是嵌入RichTextfield中的特殊元素的VO实例。由于Link的text属性只能存一个字符串,那我们若想传递一个type一个id信息的话就得把他们合并到一个字符串里面,之间用一个下划线分隔符分开,到时候取数据的时候可以把他们在拆分回来~表忘了在数据模型里面保存一个info实例,到时候我们要根据点击目标的id来取它。
      第二个方法是Link事件的侦听函数,当点击一个特殊元素的超链接文本时我们可以捕获Link事件,由于之前我们把type和id信息合并到了一个字符串中,所以现在需要把这两个信息从event.text中拆分出来并传递给一个ElemClickedEvent事件里面让它携带出去。
      好了,是时候去应用程序里面把之前写的API用起来了,在Test.as的初始化函数init里面末尾位置写上以下代码:
  1. public static const WEAPON_TYPE:int = 1001;//武器类型
  2. private function init(e:Event = null):void 
  3. {
  4.    ……
  5.    //嵌入聊天框中的道具
  6. var item:ItemVO = new ItemVO();
  7. item.id = "drak";
  8. item.name = "黑暗之剑";
  9. item.type = "剑";
  10. item.attack = 10;
  11. item.defence = 10;
  12. item.weight = 5;
  13. _input.insertSpecialElem( WEAPON_TYPE, "黑暗之剑", item );
  14. _input.addEventListener( RichTextField.ELEM_CLICKED_EVENT, onElemClick);
  15. _output.addEventListener( RichTextField.ELEM_CLICKED_EVENT, onElemClick);
  16. }
  17. private function onElemClick(event:ElemClickedEvent):void{
  18.         var ary:Array = GameModel.getInstance().itemList;
  19.         var item:ItemVO;
  20.         for( var i:int=0; i<ary.length; i++ ){
  21.                 var elem:ItemVO = ary[i] as ItemVO;
  22.                 if( elem.id == event.id ){
  23.                         item = elem;
  24.                         break;
  25.                 }
  26.         }
  27. }
复制代码
很容易懂吧?这样做就可以在外部正确地获取用户点击的是RichTextfield中嵌入元素的实例了。接下来我们需要一个弹出框来显示嵌入元素的详情。写一个弹出窗口类先:
ElemInfoWindow.as:
  1. public class ElemInfoWindow extends Sprite
  2.         {
  3.                 public var item:ItemVO;
  4.                 private var closeBtn:Sprite;
  5.                 private var tf:TextFormat = new TextFormat(null, null, 0xffffff);
  6.                 
  7.                 public function ElemInfoWindow( item:ItemVO )
  8.                 {
  9.                         this.item = item;
  10.                         draw();
  11.                 }
  12.                 
  13.                 public function draw():void{
  14.                         var X:int = 20;
  15.                         var Y:int = 40;
  16.                         if( item.name.length > 0 ){
  17.                                 generateNewTF( "name", new Point(X,Y), new Point(X+50,Y) );
  18.                                 Y += 20;
  19.                         }
  20.                         if( item.type.length > 0 ){
  21.                                 generateNewTF( "type", new Point(X,Y), new Point(X+50,Y) );
  22.                                 Y += 20;
  23.                         }
  24.                         if( item.attack != 0 ){
  25.                                 generateNewTF( "attack", new Point(X,Y), new Point(X+50,Y) );
  26.                                 Y += 20;
  27.                         }
  28.                         if( item.defence != 0 ){
  29.                                 generateNewTF( "defence", new Point(X,Y), new Point(X+50,Y) );
  30.                                 Y += 20;
  31.                         }
  32.                         if( item.weight != 0 ){
  33.                                 generateNewTF( "weight", new Point(X,Y), new Point(X+50,Y) );
  34.                                 Y += 20;
  35.                         }
  36.                         this.graphics.lineStyle(1);
  37.                         this.graphics.beginFill(0x000000, 0.5);
  38.                         this.graphics.drawRect( 0, 0, this.width + 20, this.height );
  39.                         this.graphics.endFill();
  40.                         drawCloseBtn();
  41.                 }
  42.                 
  43.                 private function generateNewTF( property:String, desPos:Point, valuePos:Point ):void{
  44.                         var description:TextField = new TextField();
  45.                         description.defaultTextFormat = tf;//注意
  46.                         switch( property ){
  47.                                 case "name":
  48.                                         description.text = "名称:";
  49.                                         break;
  50.                                 case "attack":
  51.                                         description.text = "攻击力:";
  52.                                         break;
  53.                                 case "defence":
  54.                                         description.text = "防御力:";
  55.                                         break;
  56.                                 case "type":
  57.                                         description.text = "类型:";
  58.                                         break;
  59.                                 case "weight":
  60.                                         description.text = "重量:";
  61.                                         break;
  62.                         }
  63.                         description.selectable = false;
  64.                         description.x = desPos.x;
  65.                         description.y = desPos.y;
  66.                         addChild( description );
  67.                         var value:TextField = new TextField();
  68.                         value.defaultTextFormat = tf;
  69.                         value.selectable = false;
  70.                         value.text = item[property];
  71.                         value.x = valuePos.x;
  72.                         value.y = valuePos.y;
  73.                         addChild( value );
  74.                 }
  75.                 
  76.                 private function drawCloseBtn():void{
  77.                         closeBtn = new Sprite();
  78.                         closeBtn.graphics.lineStyle(1);
  79.                         closeBtn.graphics.beginFill(0);
  80.                         closeBtn.graphics.drawRect(0,0,20,20);
  81.                         closeBtn.graphics.endFill();
  82.                         closeBtn.graphics.lineStyle(1, 0xff0000);
  83.                         closeBtn.graphics.moveTo(5,5);
  84.                         closeBtn.graphics.lineTo(15,15);
  85.                         closeBtn.graphics.moveTo(5,15);
  86.                         closeBtn.graphics.lineTo(15,5);
  87.                         closeBtn.addEventListener(MouseEvent.CLICK, onClick);
  88.                         addChild( closeBtn );
  89.                         closeBtn.x = this.width - 25;
  90.                         closeBtn.y = 5;
  91.                         closeBtn.buttonMode = true;
  92.                 }
  93.                 
  94.                 private function onClick(event:MouseEvent):void{
  95.                         if(this.parent){
  96.                                 this.parent.removeChild( this );
  97.                                 closeBtn.removeEventListener(MouseEvent.CLICK, onClick);
  98.                         }
  99.                 }
  100.         }
复制代码
这段代码我觉得也没啥东西要解释的,如果你坚持读完了我之前写的教程,理解这段代码应该是不成问题的。最后在文档类中的onElemClick方法里面把它用起来~
  1.                 private function onElemClick(event:ElemClickedEvent):void{
  2.                         var ary:Array = GameModel.getInstance().itemList;
  3.                         var item:ItemVO;
  4.                         for( var i:int=0; i<ary.length; i++ ){
  5.                                 var elem:ItemVO = ary[i] as ItemVO;
  6.                                 if( elem.id == event.id ){
  7.                                         item = elem;
  8.                                         break;
  9.                                 }
  10.                         }
  11.                         if( item ){
  12.                                 var window:ElemInfoWindow = new ElemInfoWindow( item );
  13.                                 window.x = stage.mouseX;
  14.                                 window.y = stage.mouseY;
  15.                                 addChild( window );
  16.                         }
  17.                 }
复制代码
好啦,大功告成~想看看运行结果么?那就来看看吧:http://www.iamsevent.com/upload/RichTextFieldExtends.swf
这里存在一个问题想必各位也发现了,一旦设置了字体格式后原先紫色带下划线的特殊元素格式也会被同化,至于这一点我们刚才在做改变字体格式功能时也碰到过,要解决此问题还需要把特殊元素的文本,一般文本,表情占位符的格式分开来处理,感觉比较麻烦我也懒得去做了,不过在魔兽世界里面是仅提供了插入嵌入元素,并没有提供改变字体格式的功能,所以如果这样的话你就不必为设置了字体格式后会改变嵌入元素格式而烦恼了,因为你没有开放改变字体格式的功能~
      对于聊天框的开发,存在许多不同的解决方案,我这里只是给出其中一个思路,列位爱卿可以尽管拿去参考不要给寡人留面子~
源码奉上: <ignore_js_op> RichTextFieldTest.rar (242.84 KB, 下载次数: 867) 
最后申明:依此组件目前的开发进度,并不适合用于实际项目中,此帖子仅供学习图文混排基本原理。若要在实际项目中使用,可选择:6DN RichTextArea
原文地址:https://www.cnblogs.com/keng333/p/2712340.html