一、描述
近来项目中要用到进度条,并且是竖直方向,此组件支持水平或竖直。
二、解决方法
重写组件。主要思路:
1、创建进度背景条UI。
2、创建当前刻度条UI。
3、创建%比文字显示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", type="Number", format="Length", inherit="no")]
[Style(name="barSkin", type="Class", inherit="no")]
[Style(name="trackSkin", type="Class", 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,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);
五、说明
实践出真知
-------------------------------