一、描述
近来项目中要用到进度条,并且是竖直方向,此组件支持水平或竖直。
二、解决方法
重写组件。主要思路:
1、创建进度背景条UI。
2、创建当前刻度条UI。
三、原因
已有组件不能满足需求要求
四、实现
1、新建ProcessBar.as,写组件生命周期的几个方法。
package components.processBar { import flash.display.displayObject; import flash.display.Graphics; import flash.events.Event; import flash.text.TextLineMetrics; import mx.core.IFlexdisplayObject; import mx.core.IFlexModuleFactory; import mx.core.IFontContextComponent; import mx.core.iuiTextField; import mx.core.UIComponent; import mx.core.UITextField; import mx.styles.ISimpleStyleClient; [Style(name="labelWidth",type="Number",format="Length",inherit="yes")] [Style(name="backgroundColor",type="uint",format="Color",inherit="no")] /** * The height of the track in pixels. * Track height if direction is horizontal. * Track width if direction is vertical. * * @default NaN */ [Style(name="trackHeight",inherit="no")] [Style(name="barSkin",type="Class",inherit="no")] [Style(name="trackSkin",inherit="no")] public class ProcessBar extends UIComponent implements IFontContextComponent { public function ProcessBar() { super(); } private var _content:UIComponent;// 装载背景与进度条的容器 private var _bar:IFlexdisplayObject;//进度条 private var _track:IFlexdisplayObject;// 背景条 private var _labelField:iuiTextField;// 文字 private var trackSkinChanged:Boolean = false;// 背景条 private var barSkinChanged:Boolean = false;// 进度条 /** * 创建组件 */ override protected function createChildren():void{ super.createChildren(); // 内容 if (!_content){ _content = new UIComponent(); addChild(_content); } // 文字 if (!_labelField){ _labelField = iuiTextField(createInFontContext(UITextField)); _labelField.styleName = this; addChild(displayObject(_labelField)); } } /** * createChildren完成处理 */ override protected function childrenCreated():void{ super.childrenCreated(); // Mark all of our skins as changed so they will get created. trackSkinChanged = true; barSkinChanged = true; } /** * 样式更改 */ override public function styleChanged(styleProp:String):void{ var invalidate:Boolean = false; super.styleChanged(styleProp); if (styleProp == null || styleProp == "styleName"){ // Wholesale change,need to update all skins barSkinChanged = trackSkinChanged = true; invalidate = true; } else if (styleProp == "barSkin"){ barSkinChanged = true; invalidate = true; } else if (styleProp == "trackSkin"){ trackSkinChanged = true; invalidate = true; } if (invalidate){ invalidateProperties(); invalidateSize(); invalidatedisplayList(); } } /** * 属性提交 */ override protected function commitProperties():void{ super.commitProperties(); // labelField if (hasFontContextChanged() && _labelField != null){ var index:int = getChildindex(displayObject(_labelField)); removeChild(displayObject(_labelField)); _labelField = iuiTextField(createInFontContext(UITextField)); _labelField.styleName = this; addChildAt(displayObject(_labelField),index); } // trackSkin背景条 if (trackSkinChanged){ trackSkinChanged = false; createTrack(); } // barSkin进度条 if (barSkinChanged){ barSkinChanged = false; createBar(); } if(label == null){ label = "%3%%"; } } /** * 度量(与高宽设置有关) */ override protected function measure():void{ super.measure(); var prefWidth:Number; var prefheight:Number; var trackHeight:Number = getStyle("trackHeight"); var preferredTrackWidth:Number = _track.measuredWidth; var preferredTrackHeight:Number = isNaN(trackHeight) ? _track.measuredHeight: trackHeight; var labelWidth:Number = getStyle("labelWidth"); var lineMetrics:TextLineMetrics = measureText(predictLabelText()); var textWidth:Number = isNaN(labelWidth) ? lineMetrics.width : labelWidth; var textHeight:Number = lineMetrics.height; if(direction == "horizontal"){ prefWidth = Math.max(preferredTrackWidth,textWidth); prefheight = preferredTrackHeight; }else if(direction == "vertical"){ prefWidth = Math.max(preferredTrackHeight,textWidth); prefheight = preferredTrackWidth; } measuredMinWidth = measuredWidth = prefWidth; measuredMinHeight = measuredHeight = prefheight; } /** * 更新列表 */ override protected function updatedisplayList(unscaledWidth:Number,unscaledHeight:Number):void{ super.updatedisplayList(unscaledWidth,unscaledHeight); // 背景 var bgColor:Object = getStyle("backgroundColor"); if (bgColor === null || isNaN(Number(bgColor))){ bgColor = 0xFFFFFF; } this.graphics.clear(); this.graphics.beginFill(uint(bgColor),1); this.graphics.drawRect(0,unscaledWidth,unscaledHeight); this.graphics.endFill(); var left:Number = 0; var top:Number = 0; // 文字计算 var labelWidth:Number = getStyle("labelWidth"); var lineMetrics:TextLineMetrics = measureText(predictLabelText()); var textWidth:Number = isNaN(labelWidth) ? lineMetrics.width : labelWidth; var textHeight:Number = lineMetrics.height; // 进度条计算 var trackHeight:Number = getStyle("trackHeight"); trackHeight = isNaN(trackHeight) ? _track.measuredHeight : trackHeight; // 布局 if(direction == "horizontal"){ trackHeight = (trackHeight > unscaledHeight) ? unscaledHeight : trackHeight; _content.move(left,top); // 背景条 _track.move(0,(unscaledHeight - trackHeight) / 2); _track.setActualSize(unscaledWidth,trackHeight); // 进度条 _bar.move(0,(unscaledHeight - trackHeight) / 2); var w:Number = Math.max(0,_track.width * percentComplete / 100); _bar.setActualSize(w,_track.height); }else if(direction == "vertical"){ //width trackHeight = (trackHeight > unscaledWidth) ? unscaledWidth : trackHeight; _content.move(left,top); // 背景条 _track.move((unscaledWidth - trackHeight) / 2,0); _track.setActualSize(trackHeight,unscaledHeight); // 进度条 var h:Number = Math.max(0,_track.height * percentComplete / 100); _bar.move((unscaledWidth - trackHeight) / 2,_track.height - h); _bar.setActualSize(_track.width,h); } //文字 var _labelLeft:Number = (textWidth > unscaledWidth) ? 0 : (unscaledWidth - textWidth); _labelField.move(left + _labelLeft / 2,top + unscaledHeight); _labelField.setActualSize(textWidth,textHeight); _labelField.text = getFullLabelText(); } /** * 创建背景条trackSkin */ private function createTrack():void{ if (_track){ _content.removeChild(displayObject(_track)); _track = null; } // Create the track frame var trackClass:Class = getStyle('trackSkin'); if (trackClass) { _track = new trackClass(); if (_track is ISimpleStyleClient) ISimpleStyleClient(_track).styleName = this; _content.addChildAt(displayObject(_track),0);//放在最底层 } } /** * 创建进度条barSkin */ private function createBar():void{ if (_bar){ _content.removeChild(displayObject(_bar)); _bar = null; } // Create the bar var barClass:Class = getStyle('barSkin'); if (barClass){ _bar = new barClass(); if (_bar is ISimpleStyleClient){ ISimpleStyleClient(_bar).styleName = this; } _content.addChild(displayObject(_bar)); } } //---------------------------------- // direction //---------------------------------- private var _direction:String = "horizontal"; [Inspectable(enumeration="horizontal,vertical",defaultValue="horizontal")] public function get direction():String{ return _direction; } public function set direction(value:String):void{ if (value == "horizontal" || value == "vertical"){ _direction = value; } invalidatedisplayList(); } private var _label:String; public function get label():String{ return _label; } public function set label(value:String):void{ _label = (value != null) ? value : resourceManager.getString("controls","label"); invalidatedisplayList(); } //---------------------------------- // value //---------------------------------- private var _value:Number = 0; public function get value():Number{ return _value; } public function set value(_value:Number):void{ this._value = _value; } private var _minimum:Number = 0; private var _maximum:Number = 100; /** * 设置进度条值 */ public function setProgress(value:Number,total:Number):void{ _setProgress(value,total); } private function _setProgress(value:Number,maximum:Number):void{ if (!isNaN(value) && !isNaN(maximum)){ _value = value; _maximum = maximum; // 派发完成事件可以不要 if (_value == _maximum && _value > 0){ dispatchEvent(new Event(Event.COMPLETE)); } invalidatedisplayList(); } } /** * 计算当前进度百分比 */ public function get percentComplete():Number{ if (_value < _minimum || _maximum < _minimum){ return 0; } // Avoid divide by zero fault. if ((_maximum - _minimum) == 0){ return 0; } // 百分比 var perc:Number = 100 * (_value - _minimum) / (_maximum - _minimum); perc = (isNaN(perc) || perc < 0) ? 0 : perc; perc = (perc > 100) ? 100 : perc; return perc; } /** * 获取当前进度的格式文字 */ private function getFullLabelText():String{ var current:Number = Math.max(_value /* - _minimum */,0); var total:Number = Math.max(_maximum /* - _minimum */,0); var labelText:String = label; if (labelText){ labelText = labelText.replace("%1",String(Math.floor(current))); labelText = labelText.replace("%2",String(Math.floor(total))); labelText = labelText.replace("%3",String(Math.floor(percentComplete))); labelText = labelText.replace("%%","%"); } return labelText; } /** * 计算文字 */ private function predictLabelText():String{ if(label == null){ return ""; } var labelText:String = label; var largestValue:Number = (_maximum != 0) ? _maximum : 100000; if(labelText){ labelText = labelText.replace("%1",String(Math.floor(largestValue))); labelText = labelText.replace("%2",String(Math.floor(largestValue))); labelText = labelText.replace("%3","100"); labelText = labelText.replace("%%","%"); } var actualText:String = getFullLabelText(); return ((labelText.length > actualText.length) ? labelText : actualText); } public function get fontContext():IFlexModuleFactory{ return moduleFactory; } public function set fontContext(moduleFactory:IFlexModuleFactory):void{ this.moduleFactory = moduleFactory; } } }2、新建进度条背景条ProcessBarSkin.mxml,此皮肤包括边框与填充色
<?xml version="1.0" encoding="utf-8"?> <s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"> <fx:Script> <![CDATA[ override protected function initializationComplete():void { useChromeColor = false; super.initializationComplete(); } override protected function updatedisplayList(unscaledWidth:Number,unscaledHeight:Number):void{ if(getStyle('barColor')) { activeBar.color = getStyle('barColor'); } super.updatedisplayList(unscaledWidth,unscaledHeight); } ]]> </fx:Script> <!-- layer 1: fill --> <s:Rect left="1" right="1" top="1" bottom="1" > <s:fill> <s:SolidColor id="activeBar" color="#5eb737" alpha="1"/> </s:fill> </s:Rect> </s:SparkSkin>3、新建当前刻度条ProcessBarTrackSkin.mxml
<?xml version="1.0" encoding="utf-8"?> <!--- The Spark skin class for the MX ProgressBar component's track. @see mx.controls.ProgressBar @langversion 3.0 @playerversion Flash 10 @playerversion AIR 1.5 @productversion Flex 4 --> <s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" minHeight="10" > <fx:Script> /** * @private */ override protected function initializationComplete():void { useChromeColor = false; super.initializationComplete(); } </fx:Script> <!-- layer 1: border --> <s:Rect left="0" right="0" top="0" bottom="0" width="199"> <s:stroke> <s:SolidColorstroke color="#5eb737" alpha="1" /> </s:stroke> </s:Rect> <!-- layer 2: inner border --> <s:Rect left="1" right="1" top="1" bottom="1" > <s:fill> <s:SolidColor color="#5eb737" alpha="0.6" /> </s:fill> </s:Rect> </s:SparkSkin>4、App应用
<processBar:ProcessBar id="bar" x="200" y="50" width="50" height="100" backgroundColor="0x000000" trackHeight="30" barSkin="components.processBar.ProcessBarSkin" trackSkin="components.processBar.ProcessBarTrackSkin" direction="vertical" label="%3%%" value="30" styleName="redBar"/>更新刻度值调用:bar.setProgress(70,100);
五、说明
实践出真知
-------------------------------