不知道为什么,Flex一直没有提供时间输入控件,今天因为项目的需要就实现了一个,实现的原理很简单就是扩展mx.controls.NumericStepper然后添加两个TextInput分别用于小时和分钟的录入,源码如下…

不知道为什么,Flex一直没有提供时间输入控件,今天因为项目的需要就实现了一个,实现的原理很简单就是扩展mx.controls.NumericStepper然后添加两个TextInput分别用于小时和分钟的录入,源码如下:

[java]
package com.eshangrao.controls
{
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.text.TextLineMetrics;
	import mx.core.UITextField;
	import mx.containers.HBox;
	import mx.controls.TextInput;
	import mx.controls.Button;
	import mx.events.FlexEvent;
	import mx.controls.Text;
	import mx.core.mx_internal;
	import mx.managers.IFocusManager;
	import mx.controls.NumericStepper;

	import com.eshangrao.util.StringToolkit;

	use namespace mx_internal;

	/**
	* Dispatched when the time changes, which could be either the hour, minute, or dayPart.
	*/
	[Event(name="change",type="flash.events.Event")]
	/**
	* Dispatched when the hour changes.
	*/
	[Event(name="hoursChange",type="flash.events.Event")]
	/**
	* Dispatched when the minutes change.
	*/
	[Event(name="minutesChange",type="flash.events.Event")]

	/**
	 *  A time input control
	 *
	 *  @mxml
	 *
	 *  <p>The <code>&lt;eshangrao:TimeInput&gt;</code> tag inherits all of the tag
	 *  attributes of its superclass and adds the following tag attributes:</p>
	 *
	 *  <pre>
	 *  &lt;eshangrao:TimeInput
	 *     <strong>Properties</strong>
	 *     hours="0"
	 *     minutes="30"
	 *     time="Date time"
	 *      ...
	 *  /&gt;
	 *  </pre>
	 *
	 *  @see mx.controls.NumericStepper
	 */
	public class TimeInput extends NumericStepper
	{
		public function TimeInput()
		{
			super();
			this.maxChars=2;
			this.minimum=0;
			this.maximum=23;
			this.stepSize=1;
			this.addEventListener(FlexEvent.VALUE_COMMIT,valueCommandHandler);
		}
		//--------------------------------------------------------------------------
	    //
	    //  Variables
	    //
	    //--------------------------------------------------------------------------

		/**
	     *  @private
	     */
	    protected  var inputBox:HBox;

	    /**
	     *  @private
	     */
	    protected  var sText:Text;

	    /**
	     *  @private
	     */
	    protected  var hoursInputField:TextInput;

	    /**
	     *  @private
	     */
	    protected  var minutesInputField:TextInput;

	    /**
	     *  @private
	     */
	    protected var _hours:Number = 0;
	    /**
	     *  @private
	     */
		protected var _minutes:Number = 30;
		/**
	     *  @private
	     */
		protected var _time:Date;
	     /**
	     *  @private
	     */
	    private var _enabled:Boolean=true;

	    override protected function createChildren():void
	    {
			super.createChildren();
			if(!inputBox)
			{
				inputBox=new HBox();
				inputBox.setStyle("paddingLeft",0);
				inputBox.setStyle("paddingRight",0);
				inputBox.setStyle("paddingTop",0);
				inputBox.setStyle("paddingBottom",0);
				inputBox.setStyle("horizontalGap",0);
				inputBox.setStyle("borderStyle","solid");
				inputBox.setStyle("verticalAlign","middle");
				addChild(inputBox);
			}

			var widestNumber:Number=61;
	        var lineMetrics:TextLineMetrics = measureText(widestNumber.toString());
	        var textWidth:Number = lineMetrics.width + UITextField.TEXT_WIDTH_PADDING+4;
	        if (!hoursInputField)
	        {
	            hoursInputField = new TextInput();
            	hoursInputField.focusEnabled = false;
	            hoursInputField.styleName = this;
				hoursInputField.width=textWidth;
	            // restrict to numbers - dashes - commas - decimals
	            hoursInputField.restrict = "0-9";
	            hoursInputField.maxChars = 2;
	            hoursInputField.text = StringToolkit.formatNumberWithChar(_hours,2,"0");
	            //hoursInputField.parentDrawsFocus = true;
	            hoursInputField.setStyle("textAlign","right");
				hoursInputField.setStyle("borderStyle","none");
				hoursInputField.setStyle("paddingLeft",0);
				hoursInputField.setStyle("paddingRight",0);
				hoursInputField.setStyle("paddingTop",0);
				hoursInputField.setStyle("paddingBottom",0);
				hoursInputField.setStyle("horizontalGap",0);

				hoursInputField.addEventListener(FocusEvent.FOCUS_IN,inputField_focusInHandler);
            	hoursInputField.addEventListener(FocusEvent.FOCUS_OUT, inputField_focusOutHandler);

				inputBox.addChild(hoursInputField);
	        }
	        inputField=hoursInputField;
			if(!sText){
				sText=new Text();
				sText.text=":";
				sText.setStyle("textAlign","center");
				sText.setStyle("paddingLeft",0);
				sText.setStyle("paddingRight",0);
				sText.setStyle("paddingTop",0);
				sText.setStyle("paddingBottom",0);
				sText.setStyle("horizontalGap",0);
				inputBox.addChild(sText);
			}
	        if (!minutesInputField)
	        {
	            minutesInputField = new TextInput();
            	minutesInputField.focusEnabled = false;
	            minutesInputField.styleName = this;
				minutesInputField.width=textWidth;

	            // restrict to numbers - dashes - commas - decimals
	            minutesInputField.restrict = "0-9";

	            minutesInputField.maxChars = 2;
	            minutesInputField.text = StringToolkit.formatNumberWithChar(_minutes,2,"0");
	            //minutesInputField.parentDrawsFocus = true;

	            minutesInputField.setStyle("textAlign","left");
				minutesInputField.setStyle("borderStyle","none");
				minutesInputField.setStyle("paddingLeft",0);
				minutesInputField.setStyle("paddingRight",0);
				minutesInputField.setStyle("paddingTop",0);
				minutesInputField.setStyle("paddingBottom",0);
				minutesInputField.setStyle("horizontalGap",0);
				minutesInputField.addEventListener(FocusEvent.FOCUS_IN,inputField_focusInHandler);
            	minutesInputField.addEventListener(FocusEvent.FOCUS_OUT, inputField_focusOutHandler);

				inputBox.addChild(minutesInputField);
	        }
	    }

	     /**
	     *  @private
	     *  Return the preferred sizes of the stepper.
	     */
	    override protected function measure():void
	    {
			super.measure();
	        var inputBoxHeight:Number = inputBox.getExplicitOrMeasuredHeight();
	        var buttonHeight:Number = prevButton.getExplicitOrMeasuredHeight() +
	                                  nextButton.getExplicitOrMeasuredHeight();

	        var h:Number = Math.max(inputBoxHeight, buttonHeight);
	        h = Math.max(DEFAULT_MEASURED_MIN_HEIGHT, h);

	        var inputBoxWidth:Number = inputBox.getExplicitOrMeasuredWidth();
	        var buttonWidth:Number = Math.max(prevButton.getExplicitOrMeasuredWidth(),
	                                          nextButton.getExplicitOrMeasuredWidth());

	        var w:Number = inputBoxWidth + buttonWidth;
	        w = Math.max(DEFAULT_MEASURED_MIN_WIDTH, w);

	        measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH;
	        measuredMinHeight = DEFAULT_MEASURED_MIN_HEIGHT;

	        measuredWidth = w;
	        measuredHeight = h;
	    }
	    /**
	     *  @private
	     *  Place the buttons to the right of the text field.
	     */
		override protected function updateDisplayList(unscaledWidth:Number,
													  unscaledHeight:Number):void
	    {
			super.updateDisplayList(unscaledWidth, unscaledHeight);

	        var w:Number = nextButton.getExplicitOrMeasuredWidth();
	        var h:Number = Math.round(unscaledHeight / 2);
	        var h2:Number = unscaledHeight - h;

	        nextButton.x = unscaledWidth - w;
	        nextButton.y = 0;
	        nextButton.setActualSize(w, h2);

			prevButton.x = unscaledWidth - w;
	        prevButton.y = unscaledHeight - h;
	        prevButton.setActualSize(w, h);
	        var inputBoxHeight:Number = inputBox.getExplicitOrMeasuredHeight();
	        var inputBoxWidth:Number = inputBox.getExplicitOrMeasuredWidth();
	        //inputBox.setActualSize(unscaledWidth - w, unscaledHeight);
	        inputBox.setActualSize(inputBoxWidth,inputBoxHeight);
	    }

	     /**
		 *  @private
		 */
	    private function inputField_focusInHandler(event:FocusEvent):void
	    {

	    	inputField=event.currentTarget as TextInput;
	    	if(event.currentTarget as TextInput == hoursInputField){
	    		this.value=parseInt(inputField.text);
	    		this.minimum=0;
	    		this.maximum=23;
	    	}else{
	    		this.value=parseInt(inputField.text);
	    		this.minimum=0;
	    		this.maximum=59;
	    	}
	        focusInHandler(event);

			// Send out a new FocusEvent because the TextInput eats the event
	        // Make sure that it does not bubble.
	        dispatchEvent(new FocusEvent(event.type, false, false,
										 event.relatedObject,
										 event.shiftKey, event.keyCode));
	    }

	    /**
		 *  @private
		 */
	    private function inputField_focusOutHandler(event:FocusEvent):void
	    {
	        focusOutHandler(event);

			// Send out a new FocusEvent because the TextInput eats the event
	        // Make sure that it does not bubble
	        dispatchEvent(new FocusEvent(event.type, false, false,
										 event.relatedObject,
										 event.shiftKey,event.keyCode));
	    }

		/**
		 * @private
		 *
		 * do for format number to string
		 */
		private function valueCommandHandler(event:FlexEvent):void{
			//var v=this.value;
			inputField.text=StringToolkit.formatNumberWithChar(value,2,"0");
			if(inputField==hoursInputField){
	    		this.hours=value;
	    	}else{
	    		this.minutes=value;
	    	}
		}

	     /**
	     *  @private
	     *  Remove the focus from the text field.
	     */
	    override protected function focusInHandler(event:FocusEvent):void
	    {
	        super.focusInHandler(event);

	        var fm:IFocusManager = focusManager;
	        if (fm)
	            fm.defaultButtonEnabled = false;
	    }
		[Bindable]
		/**
		 * The hours (an integer from 0 to 23) of the day.
		 *
		 * @default 0
		 */
		public function get hours():Number
		{
			return _hours;
		}

		[Inspectable(defaultValue=0,category="Time",name="Hours")]
		public function set hours(val:Number):void
		{
			if (val >= 0 || val <= 24)
			{
				this._hours = val;
				if(inputField==hoursInputField && val!=value)
					value=val;
				else{
					hoursInputField.text=StringToolkit.formatNumberWithChar(val,2,"0");
				}
			}		

			dispatchEvent(new Event("hoursChange"));
			dispatchEvent(new Event("change"));
		}

		[Bindable]
		/**
		 * The minutes (an integer from 0 to 59) passed in the hours.
		 *
		 * @default 30
		 */
		public function get minutes():Number
		{
			return _minutes;
		}

		[Inspectable(defaultValue=30,category="Time",name="Minutes")]
		public function set minutes(val:Number):void
		{
			if (val >= 0 || val <= 59)
			{
				this._minutes = val;
				if(inputField==minutesInputField && val!=value)
					value=val;
				else{
					minutesInputField.text=StringToolkit.formatNumberWithChar(val,2,"0");
				}
			}

			dispatchEvent(new Event("minutesChange"));
			dispatchEvent(new Event("change"));
		}

		public function get Time():Date{
			var d:Date=new Date();
			d.hours=_hours;
			d.minutes=_minutes;
			return d;
		}
		public function set Time(time:Date):void{
			this._time=time;
			this.hours=time.hours;
			this.minutes=time.minutes;
		}

		 /**
	     *  @private
	     */
	    override public function set enabled(value:Boolean):void
	    {
	        _enabled = value;
	        if(hoursInputField){
	        	hoursInputField.enabled=value;
	       		minutesInputField.enabled=value;
	       		sText.enabled=value;
	       		nextButton.enabled=value;
	       		prevButton.enabled=value;
	        }

	    }

	    /**
	     *  @private
	     */

	    override public function get enabled():Boolean
	    {
	        return _enabled;
	    }

	}
}

用法如下:

首先从这里下载二进制SWC包,将其添加到您的Mxmlc编译组件路径中,然后加入如下命名空间声明:

[xml]
xmlns:eshangrao="http://www.eshangrao.com"

接着就可以使用TimeInput了:

[xml]
<eshangrao:TimeInput hours="3" minutes="45" id="timeInput"/>

完整的范例演示代码如下:

[xml]
<?xml version="1.0" encoding="utf-8"?>
<mx:Application creationComplete="init();" xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal" xmlns:eshangrao="com.eshangrao.controls.*">

	<mx:DateFormatter id="timeFormatter" formatString="J:NN A" />

	<mx:Panel layout="vertical" paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5" title="TimeInput">
		<eshangrao:TimeInput hours="3" minutes="45" id="timeInput"/>
	</mx:Panel>

	<mx:Panel layout="vertical" paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5" title="time now">
		<mx:Text text="hours:{timeInput.hours}"/>
		<mx:Text text="minutes:{timeInput.minutes}"/>
		<mx:Button label="setTime to 6:30" click="timeInput.hours=6; timeInput.minutes=30" />
		<mx:Button label="switchEnable" click="if(timeInput.enabled) timeInput.enabled=false; else timeInput.enabled=true;"/>
	</mx:Panel>
</mx:Application>

运行示例

本组件已经包含入Plum中,点击这里下载源码及二进制SWC包。详细的组件用法请查看文档

另外,为了项目的需要,顺手做了两个中文月份和星期的ChineseDateChooserChineseDateField,有需要的朋友可以直接使用。