[AS3]心理测试题的开发

很久以前在《i时代报》看到心理测试题,就一直想开发这个好玩的程序!只是没时间开发,这次又报纸上看到测试题,突然又来兴趣了!这个程序逻辑我早就考虑过了,也不太难!正好用这个来练习自己对AS3编程感觉了!
    这次编程让我掌握了AS3中的XML,Stage,URLLoader,TextField,Sprite类的用法,还有AS3自带的组件使用。Flash帮助文件一直是我的老师,我的字典。它里面的调用实例一直很不错的。建议大家要养成看帮助文件的习惯,有些问题都可以在帮助文件中找到解决办法的,这个也是此次编程的心得。
   这个程序除使用Flash自带的组件外,其他全部是用代码实现的,而且采用了document class的方式,fla文件里没有一点代码。算是真正意义上的代码和设计分离吧!

演示地址: http://www.klstudio.com/demo/topic/topic.html?path=t1.xml
Main类
view plain copy to clipboard print ?
  1.   
  2. package project.topic {   
  3.        
  4.     import flash.display.*;   
  5.     import flash.net.*;   
  6.     import flash.events.*;   
  7.     import flash.errors.*;   
  8.     import flash.text.*;       
  9.     import flash.xml.*;   
  10.        
  11.     import fl.controls.Button;   
  12.     import fl.controls.RadioButton;   
  13.     import fl.controls.RadioButtonGroup;   
  14.     import fl.controls.ScrollPolicy;   
  15.     import fl.containers.ScrollPane;   
  16.   
  17.        
  18.   
  19.     public class Main extends Sprite{   
  20.            
  21.         private var type:String;   
  22.         private var pointer:uint;   
  23.         private var value:uint;   
  24.         private var xmlData:XML;   
  25.         private var loader:URLLoader;   
  26.            
  27.         private var preWidth:uint;   
  28.         private var preHeight:uint;   
  29.            
  30.         private var borderColor:uint;   
  31.         private var bgColor:uint;   
  32.            
  33.         private var bg:Shape;   
  34.         private var button:Button;   
  35.         private var title:TextField;   
  36.         private var copyright:TextField;   
  37.         private var content:Sprite;   
  38.         private var group:RadioButtonGroup;   
  39.         private var pane:ScrollPane;   
  40.            
  41.         public function Main(){   
  42.             this.init();   
  43.             this.drawSkin();               
  44.             this.readXml(this.loaderInfo.parameters.path);                     
  45.         }   
  46.            
  47.         private function init():void{   
  48.                
  49.             this.bgColor = 0xeeeeee;   
  50.             this.borderColor = 0x666666;   
  51.                
  52.             this.preWidth = 500;   
  53.             this.preHeight = 400;          
  54.                
  55.             this.type = "jump";   
  56.             this.value = 0;    
  57.             this.loader = new URLLoader();             
  58.             this.configureURLLoaderListeners(this.loader);                             
  59.                
  60.             stage.scaleMode = StageScaleMode.NO_SCALE;   
  61.             stage.align = StageAlign.TOP_LEFT;   
  62.             stage.showDefaultContextMenu = false;   
  63.             stage.addEventListener(Event.RESIZE, resizeHandler);   
  64.         }   
  65.            
  66.         private function drawSkin():void{   
  67.                
  68.             //bg;   
  69.             bg = new Shape();   
  70.             addChild(bg);   
  71.                
  72.             //title;   
  73.             title = new TextField();   
  74.             title.autoSize = TextFieldAutoSize.LEFT;   
  75.             title.selectable = false;   
  76.             title.defaultTextFormat = new TextFormat("Courier New",18,0x333333,true);   
  77.             title.x = 5;   
  78.             title.y = 5;   
  79.             title.text = "[心理测试]-";   
  80.             addChild(title);   
  81.                
  82.                
  83.             //copyright;   
  84.             copyright = new TextField();   
  85.             copyright.autoSize = TextFieldAutoSize.LEFT;   
  86.             copyright.selectable = false;   
  87.             copyright.defaultTextFormat = new TextFormat("Verdana",9,0x666666,true,null,null,"http://www.kltudio.com","_blank");   
  88.             copyright.text = "POWERED BY KINGLONG";   
  89.             addChild(copyright);   
  90.                
  91.             //content   
  92.             content = new Sprite();    
  93.                
  94.             //scrollPane;   
  95.             pane = new ScrollPane();   
  96.             pane.horizontalScrollPolicy = ScrollPolicy.OFF;   
  97.             pane.verticalScrollPolicy = ScrollPolicy.ON;   
  98.             pane.source = content;   
  99.             addChild(pane);   
  100.                
  101.             //Button;   
  102.             button = new Button();   
  103.             button.width = 100;   
  104.             button.setStyle("textFormat"new TextFormat("Courier New",12));   
  105.             button.label = "开始测试";   
  106.             button.enabled = false;   
  107.             button.addEventListener(MouseEvent.CLICK, buttonClickHandler);   
  108.             addChild(button);   
  109.                
  110.             updateSkin();   
  111.         }   
  112.            
  113.            
  114.         private function updateSkin():void{            
  115.                
  116.             if(copyright != null){   
  117.                 copyright.x = 10;   
  118.                 copyright.y = preHeight - 24;   
  119.             }              
  120.                
  121.             if(bg != null){   
  122.                 bg.graphics.clear();   
  123.                 bg.graphics.beginFill(this.bgColor);   
  124.                 bg.graphics.drawRect(0,0,preWidth,34);   
  125.                 bg.graphics.drawRect(0,preHeight - 34,preWidth,34);   
  126.                 bg.graphics.endFill();   
  127.                    
  128.                 bg.graphics.beginFill(this.borderColor);   
  129.                 bg.graphics.drawRect(0,33,preWidth,1);                 
  130.                 bg.graphics.endFill();   
  131.             }   
  132.                
  133.             if(button != null){   
  134.                 button.move(preWidth - button.width - 5,preHeight - button.height - 5);   
  135.             }   
  136.                
  137.             if(pane != null){   
  138.                 pane.move(0,34);   
  139.                 pane.setSize(preWidth,preHeight-34*2);   
  140.                 updatePaneContent();   
  141.             }   
  142.         }   
  143.            
  144.         private function updatePaneContent(){   
  145.             if(this.content.numChildren > 0){   
  146.                 var dy:uint = 5;   
  147.                 var sw:uint = pane.verticalScrollBar.width;   
  148.                 for(var i:uint = 0;i< this.content.numChildren;i++){   
  149.                     var item:Object = this.content.getChildAt(i);   
  150.                     if(item is TextField){   
  151.                         item.x = 0;   
  152.                         item.y = dy;   
  153.                         item.width = preWidth - sw;                        
  154.                         dy += item.height + 5;   
  155.                     }else if(item is Answer){   
  156.                         item.x = 0;   
  157.                         item.y = dy;   
  158.                         item.update(preWidth - sw);   
  159.                         dy += item.getHeight() + 5;   
  160.                     }                      
  161.                 }   
  162.             }   
  163.             pane.refreshPane();   
  164.         }   
  165.            
  166.         public function setTitle(title:String):void{   
  167.             this.title.text = "[测试]-"+title;   
  168.         }   
  169.            
  170.         public function clearPaneContent():void{   
  171.             while(this.content.numChildren > 0){   
  172.                 this.content.removeChildAt(0);   
  173.             }   
  174.         }   
  175.            
  176.         public function setPaneContentList(node:XMLList){   
  177.             this.clearPaneContent();   
  178.             this.button.enabled = false;   
  179.             var tt:TextField = new TextField();   
  180.             tt.autoSize = TextFieldAutoSize.LEFT;   
  181.             tt.selectable = false;   
  182.             tt.multiline = true;   
  183.             tt.wordWrap = true;   
  184.             tt.defaultTextFormat = new TextFormat("Courier New",16,0x444444,true,true,null,null,null,null,5,5,null,5);   
  185.             tt.text = node.@id+"."+node.problem;   
  186.             this.content.addChild(tt);   
  187.             var opt:XML = null;   
  188.             group = new RadioButtonGroup("opts");   
  189.             group.addEventListener(MouseEvent.CLICK, groupClickHandler);   
  190.             group.addEventListener(Event.CHANGE, groupClickHandler);   
  191.             for each(opt in node.answer.option){   
  192.                 var answer:Answer = new Answer(group,opt);   
  193.                 answer.update(preWidth-pane.verticalScrollBar.width);   
  194.                 this.content.addChild(answer);   
  195.             }                  
  196.             updatePaneContent();   
  197.         }   
  198.            
  199.         public function setPaneContentString(ti:String,msg:String):void{   
  200.             this.clearPaneContent();               
  201.             var tt:TextField = new TextField();   
  202.             tt.autoSize = TextFieldAutoSize.LEFT;   
  203.             tt.selectable = false;   
  204.             tt.multiline = true;   
  205.             tt.wordWrap = true;   
  206.             tt.defaultTextFormat = new TextFormat("Courier New",16,0x444444,true,true,null,null,null,null,5,5,null,5);   
  207.             tt.text = ti;   
  208.             this.content.addChild(tt);   
  209.                
  210.             var desc:TextField = new TextField();   
  211.             desc.autoSize = TextFieldAutoSize.LEFT;   
  212.             desc.selectable = false;   
  213.             desc.multiline = true;   
  214.             desc.wordWrap = true;   
  215.             desc.defaultTextFormat = new TextFormat("Courier New",12,0x666666,null,null,null,null,null,null,5,5,null,5);   
  216.             desc.htmlText = msg;   
  217.             this.content.addChild(desc);   
  218.             updatePaneContent();   
  219.         }   
  220.            
  221.         public function readXml(url:String):void{   
  222.             this.setPaneContentString("提示信息","正在加载["+url+"]文件...");   
  223.             try{   
  224.                 this.loader.load(new URLRequest(url));   
  225.             } catch (error:Error) {   
  226.                 this.errorHandler(new ErrorEvent(ErrorEvent.ERROR));   
  227.             }              
  228.         }          
  229.   
  230.         private function buttonClickHandler(event:Event):void {   
  231.            switch(event.target.label){   
  232.                case "开始测试":   
  233.                     this.value = 0;    
  234.                     this.pointer = 0;   
  235.                     this.pointer ++;   
  236.                     this.setPaneContentList(this.xmlData.list.item.(@id==this.pointer));   
  237.                     this.button.label = "下一步";   
  238.                break;   
  239.                case "下一步":   
  240.                     var res:String = ""+group.selectedData;   
  241.                     switch(this.type){   
  242.                         case "jump":   
  243.                             var arr:Array = res.split("_");                            
  244.                             if(arr[0] == "p"){   
  245.                                 this.setPaneContentList(this.xmlData.list.item.(@id==arr[1]));   
  246.                             }else{   
  247.                                 this.setPaneContentString("分析结果",this.xmlData.result.item.(@id==arr[1]));   
  248.                                 this.button.label = "重新测试";                                
  249.                             }   
  250.                         break;   
  251.                         case "sum":    
  252.                             this.value += Number(res);   
  253.                             this.pointer ++;   
  254.                             if(this.pointer <= this.xmlData.list.item.length()){                                       
  255.                                 this.setPaneContentList(this.xmlData.list.item.(@id==this.pointer));   
  256.                             }else{   
  257.                                 this.setPaneContentString("分析结果",this.getResultContent(this.value));   
  258.                                 this.button.label = "重新测试";    
  259.                             }   
  260.                         break;                         
  261.                     }   
  262.                break;   
  263.                case "重新测试":   
  264.                     this.value = 0;    
  265.                     this.pointer = 0;      
  266.                     this.button.label = "开始测试";    
  267.                     this.setPaneContentString("简述",xmlData.description);   
  268.                break;   
  269.            }   
  270.         }   
  271.            
  272.         private function getResultContent(num:uint):String{   
  273.             var opt:XML = null;   
  274.             for each(opt in this.xmlData.result.item){   
  275.                 var min:uint = Number(opt.@min);   
  276.                 var max:uint = Number(opt.@max);   
  277.                 if(((min == 0)||(num >= min)) && ((max == 0)||(num <= max))){   
  278.                     return opt + "";   
  279.                 }   
  280.             }   
  281.             return "";   
  282.         }   
  283.            
  284.         private function groupClickHandler(event:Event):void {   
  285.             this.button.enabled = true;               
  286.         }   
  287.   
  288.         private function resizeHandler(event:Event):void {   
  289.             preWidth = (stage.stageWidth > 500)?stage.stageWidth:500;   
  290.             preHeight = (stage.stageHeight > 400)?stage.stageHeight:400;       
  291.             updateSkin();   
  292.         }   
  293.   
  294.         private function configureURLLoaderListeners(dispatcher:IEventDispatcher):void {   
  295.             dispatcher.addEventListener(Event.COMPLETE, completeHandler);   
  296.             dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler);               
  297.             dispatcher.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);   
  298.         }   
  299.         private function completeHandler(event:Event):void{            
  300.             xmlData = new XML(this.loader.data);               
  301.             xmlData.ignoreWhitespace = true;   
  302.             xmlData.ignoreComments = true;   
  303.             this.setTitle(xmlData.title);   
  304.             this.setPaneContentString("简述",xmlData.description);   
  305.             this.type = xmlData.type;   
  306.             button.enabled = true;             
  307.         }   
  308.         private function errorHandler(event:Event):void{   
  309.             this.setPaneContentString("错误信息",event.toString());   
  310.         }   
  311.     }      
  312. }   
package project.topic { import flash.display.*; import flash.net.*; import flash.events.*; import flash.errors.*; import flash.text.*; import flash.xml.*; import fl.controls.Button; import fl.controls.RadioButton; import fl.controls.RadioButtonGroup; import fl.controls.ScrollPolicy; import fl.containers.ScrollPane; public class Main extends Sprite{ private var type:String; private var pointer:uint; private var value:uint; private var xmlData:XML; private var loader:URLLoader; private var preWidth:uint; private var preHeight:uint; private var borderColor:uint; private var bgColor:uint; private var bg:Shape; private var button:Button; private var title:TextField; private var copyright:TextField; private var content:Sprite; private var group:RadioButtonGroup; private var pane:ScrollPane; public function Main(){ this.init(); this.drawSkin(); this.readXml(this.loaderInfo.parameters.path); } private function init():void{ this.bgColor = 0xeeeeee; this.borderColor = 0x666666; this.preWidth = 500; this.preHeight = 400; this.type = "jump"; this.value = 0; this.loader = new URLLoader(); this.configureURLLoaderListeners(this.loader); stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; stage.showDefaultContextMenu = false; stage.addEventListener(Event.RESIZE, resizeHandler); } private function drawSkin():void{ //bg; bg = new Shape(); addChild(bg); //title; title = new TextField(); title.autoSize = TextFieldAutoSize.LEFT; title.selectable = false; title.defaultTextFormat = new TextFormat("Courier New",18,0x333333,true); title.x = 5; title.y = 5; title.text = "[心理测试]-"; addChild(title); //copyright; copyright = new TextField(); copyright.autoSize = TextFieldAutoSize.LEFT; copyright.selectable = false; copyright.defaultTextFormat = new TextFormat("Verdana",9,0x666666,true,null,null,"http://www.kltudio.com","_blank"); copyright.text = "POWERED BY KINGLONG"; addChild(copyright); //content content = new Sprite(); //scrollPane; pane = new ScrollPane(); pane.horizontalScrollPolicy = ScrollPolicy.OFF; pane.verticalScrollPolicy = ScrollPolicy.ON; pane.source = content; addChild(pane); //Button; button = new Button(); button.width = 100; button.setStyle("textFormat", new TextFormat("Courier New",12)); button.label = "开始测试"; button.enabled = false; button.addEventListener(MouseEvent.CLICK, buttonClickHandler); addChild(button); updateSkin(); } private function updateSkin():void{ if(copyright != null){ copyright.x = 10; copyright.y = preHeight - 24; } if(bg != null){ bg.graphics.clear(); bg.graphics.beginFill(this.bgColor); bg.graphics.drawRect(0,0,preWidth,34); bg.graphics.drawRect(0,preHeight - 34,preWidth,34); bg.graphics.endFill(); bg.graphics.beginFill(this.borderColor); bg.graphics.drawRect(0,33,preWidth,1); bg.graphics.endFill(); } if(button != null){ button.move(preWidth - button.width - 5,preHeight - button.height - 5); } if(pane != null){ pane.move(0,34); pane.setSize(preWidth,preHeight-34*2); updatePaneContent(); } } private function updatePaneContent(){ if(this.content.numChildren > 0){ var dy:uint = 5; var sw:uint = pane.verticalScrollBar.width; for(var i:uint = 0;i< this.content.numChildren;i++){ var item:Object = this.content.getChildAt(i); if(item is TextField){ item.x = 0; item.y = dy; item.width = preWidth - sw; dy += item.height + 5; }else if(item is Answer){ item.x = 0; item.y = dy; item.update(preWidth - sw); dy += item.getHeight() + 5; } } } pane.refreshPane(); } public function setTitle(title:String):void{ this.title.text = "[测试]-"+title; } public function clearPaneContent():void{ while(this.content.numChildren > 0){ this.content.removeChildAt(0); } } public function setPaneContentList(node:XMLList){ this.clearPaneContent(); this.button.enabled = false; var tt:TextField = new TextField(); tt.autoSize = TextFieldAutoSize.LEFT; tt.selectable = false; tt.multiline = true; tt.wordWrap = true; tt.defaultTextFormat = new TextFormat("Courier New",16,0x444444,true,true,null,null,null,null,5,5,null,5); tt.text = node.@id+"."+node.problem; this.content.addChild(tt); var opt:XML = null; group = new RadioButtonGroup("opts"); group.addEventListener(MouseEvent.CLICK, groupClickHandler); group.addEventListener(Event.CHANGE, groupClickHandler); for each(opt in node.answer.option){ var answer:Answer = new Answer(group,opt); answer.update(preWidth-pane.verticalScrollBar.width); this.content.addChild(answer); } updatePaneContent(); } public function setPaneContentString(ti:String,msg:String):void{ this.clearPaneContent(); var tt:TextField = new TextField(); tt.autoSize = TextFieldAutoSize.LEFT; tt.selectable = false; tt.multiline = true; tt.wordWrap = true; tt.defaultTextFormat = new TextFormat("Courier New",16,0x444444,true,true,null,null,null,null,5,5,null,5); tt.text = ti; this.content.addChild(tt); var desc:TextField = new TextField(); desc.autoSize = TextFieldAutoSize.LEFT; desc.selectable = false; desc.multiline = true; desc.wordWrap = true; desc.defaultTextFormat = new TextFormat("Courier New",12,0x666666,null,null,null,null,null,null,5,5,null,5); desc.htmlText = msg; this.content.addChild(desc); updatePaneContent(); } public function readXml(url:String):void{ this.setPaneContentString("提示信息","正在加载["+url+"]文件..."); try{ this.loader.load(new URLRequest(url)); } catch (error:Error) { this.errorHandler(new ErrorEvent(ErrorEvent.ERROR)); } } private function buttonClickHandler(event:Event):void { switch(event.target.label){ case "开始测试": this.value = 0; this.pointer = 0; this.pointer ++; this.setPaneContentList(this.xmlData.list.item.(@id==this.pointer)); this.button.label = "下一步"; break; case "下一步": var res:String = ""+group.selectedData; switch(this.type){ case "jump": var arr:Array = res.split("_"); if(arr[0] == "p"){ this.setPaneContentList(this.xmlData.list.item.(@id==arr[1])); }else{ this.setPaneContentString("分析结果",this.xmlData.result.item.(@id==arr[1])); this.button.label = "重新测试"; } break; case "sum": this.value += Number(res); this.pointer ++; if(this.pointer <= this.xmlData.list.item.length()){ this.setPaneContentList(this.xmlData.list.item.(@id==this.pointer)); }else{ this.setPaneContentString("分析结果",this.getResultContent(this.value)); this.button.label = "重新测试"; } break; } break; case "重新测试": this.value = 0; this.pointer = 0; this.button.label = "开始测试"; this.setPaneContentString("简述",xmlData.description); break; } } private function getResultContent(num:uint):String{ var opt:XML = null; for each(opt in this.xmlData.result.item){ var min:uint = Number(opt.@min); var max:uint = Number(opt.@max); if(((min == 0)||(num >= min)) && ((max == 0)||(num <= max))){ return opt + ""; } } return ""; } private function groupClickHandler(event:Event):void { this.button.enabled = true; } private function resizeHandler(event:Event):void { preWidth = (stage.stageWidth > 500)?stage.stageWidth:500; preHeight = (stage.stageHeight > 400)?stage.stageHeight:400; updateSkin(); } private function configureURLLoaderListeners(dispatcher:IEventDispatcher):void { dispatcher.addEventListener(Event.COMPLETE, completeHandler); dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler); dispatcher.addEventListener(IOErrorEvent.IO_ERROR, errorHandler); } private function completeHandler(event:Event):void{ xmlData = new XML(this.loader.data); xmlData.ignoreWhitespace = true; xmlData.ignoreComments = true; this.setTitle(xmlData.title); this.setPaneContentString("简述",xmlData.description); this.type = xmlData.type; button.enabled = true; } private function errorHandler(event:Event):void{ this.setPaneContentString("错误信息",event.toString()); } } }

Answer类
view plain copy to clipboard print ?
  1. package project.topic {   
  2.        
  3.     import flash.display.*;   
  4.     import flash.text.*;   
  5.     import flash.xml.*;   
  6.     import flash.events.*;   
  7.     import fl.controls.RadioButton;   
  8.     import fl.controls.RadioButtonGroup;   
  9.        
  10.     public class Answer extends Sprite{   
  11.         private var node:XML;   
  12.         private var radio:RadioButton;   
  13.         private var lbl:TextField;   
  14.         private var group:RadioButtonGroup;   
  15.         public function Answer(group:RadioButtonGroup,node:XML){       
  16.             this.node = node;   
  17.             radio = new RadioButton();   
  18.             radio.group = group;   
  19.             radio.value = node.@value;   
  20.             radio.label = "";   
  21.             radio.move(0,0);   
  22.             radio.width = 20;   
  23.             radio.height = 20;             
  24.             addChild(this.radio);   
  25.                
  26.             lbl = new TextField();   
  27.             lbl.autoSize = TextFieldAutoSize.LEFT;   
  28.             lbl.selectable = false;   
  29.             lbl.multiline = true;   
  30.             lbl.wordWrap = true;   
  31.             lbl.x = 20;   
  32.             lbl.y = 0;   
  33.             lbl.defaultTextFormat = new TextFormat("Courier New",12,0x666666,null,null,null,null,null,null,5,5,null,5);   
  34.             lbl.text = node;   
  35.             lbl.addEventListener(MouseEvent.CLICK, clickHandler);   
  36.             addChild(lbl);   
  37.         }   
  38.         public function getHeight():uint{   
  39.             return this.lbl.height;   
  40.         }   
  41.         public function update(width:uint):void{   
  42.             this.lbl.width = width - 20;   
  43.         }   
  44.         private function clickHandler(event:Event):void {   
  45.             this.radio.selected = true;              
  46.         }   
  47.     }      
  48. }   
package project.topic { import flash.display.*; import flash.text.*; import flash.xml.*; import flash.events.*; import fl.controls.RadioButton; import fl.controls.RadioButtonGroup; public class Answer extends Sprite{ private var node:XML; private var radio:RadioButton; private var lbl:TextField; private var group:RadioButtonGroup; public function Answer(group:RadioButtonGroup,node:XML){ this.node = node; radio = new RadioButton(); radio.group = group; radio.value = node.@value; radio.label = ""; radio.move(0,0); radio.width = 20; radio.height = 20; addChild(this.radio); lbl = new TextField(); lbl.autoSize = TextFieldAutoSize.LEFT; lbl.selectable = false; lbl.multiline = true; lbl.wordWrap = true; lbl.x = 20; lbl.y = 0; lbl.defaultTextFormat = new TextFormat("Courier New",12,0x666666,null,null,null,null,null,null,5,5,null,5); lbl.text = node; lbl.addEventListener(MouseEvent.CLICK, clickHandler); addChild(lbl); } public function getHeight():uint{ return this.lbl.height; } public function update(width:uint):void{ this.lbl.width = width - 20; } private function clickHandler(event:Event):void { this.radio.selected = true; } } }


    代码使用时,只要把"RadioButton","Button","ScrollPane"放到fla文件库就可以了,然后只在fla文件属性里设置document class值为"project.topic.Main"就可以了

     所使用到的xml文件路径:“ t1.xml” ,当然你也可以继续完善代码!  

你可能感兴趣的:([AS3]心理测试题的开发)