lunes, 1 de septiembre de 2008

Selector de Hora - Componente Flex


En los últimos dias he empezado a desarrollar mis primeros componentes, aprendiendo las fantásticas funciones como commitProperties, updateDisplayList, merge, etc. Que te permiten realizar un Binding de manera mas acoplada al framework de Flex y mejorar el rendimiento de los componentes.

Como primer componente (y en vista de mis necesidades para futuros componentes) he decidido desarrollar un componente que por extrañas razones no viene incluido en el framework de desarrollo de flex: Selector de Horas (Time Picker).

Trate de hacer el componente lo mas parametrizable posible, permite cambiar los saltos de minutos de 5minutos, 10minutos, 15 minutos, etc. Permite cambiar el color de fondo y texto del componente, puede trabajar tanto en hora militar como meridiano, etc.

Para descargar el codigo del componente en formato RAR debes hacer click aqui, y cualquier duda que tengan pueden preguntame a mi mail aalejo@gmail.com, espero que les guste y ¡ que le saquen dinero!

Sin mas, el codigo y ejemplo del componente, los parametros estan hechos para cargar desde el inicio, no para modificarlos en tiempo de ejecucion, asi que perdonen los errores si los cambian, prometo dar una version mejorada en los proximos dias que acepte cambios en tiempo de ejecucion sin errores:

Codigo


<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" initialize="initApp()" horizontalGap="0">
<mx:Metadata>
[Event(name="onChange", type="flash.events.Event")]
</mx:Metadata>

<mx:Script>
<![CDATA[

/**
* Componenete que permite seleccionar una hora del dia, representada
* en formato militar o meridiano, el componente tambien permite ajustar
* el salto en minutos que debe dar al aumentar o disminuir la hora,
* si se quiere saltar de dos horas en dos horas se debe colocar 120 minutos
* @version 1.0, 01/9/2008
* @author Alejandro Sánchez
*/

import mx.containers.VBox;
import mx.binding.utils.BindingUtils;


[Bindable]
public var textColor : uint = 0x000000;

[Bindable]
public var backGroundColor : uint = 0xffffff;

[Bindable]
public var _twelveHour : Boolean = true;

[Bindable]
private var _hour : Number = 1;

[Bindable]
private var _minutes : Number=0;

[Bindable]
private var _meridian : String = "AM";

[Bindable]
private var _minutesJump : Number = 1;

[Bindable]
private var _hoursJump : Number = 1;

[Bindable]
[Inspectable(type="Boolean")]
public var editable : Boolean = true;


private var focused : TextInput = null;

/**
* Inicializa las variables por debecto y visializa el componenete
* por primera vez
*/

private function initApp():void {
focused=tHour;
if(!_twelveHour) {
tMeridian.width=0;
}
tHour.text = _hour.toString();
tMinute.text = _minutes.toString();
tMeridian.text = _meridian.toString();

BindingUtils.bindSetter(changeHourType,this,"twelveHour");
BindingUtils.bindSetter(resetMinutes,this,"minutesJump");

validateHour();
validateMinutes();
}

/**
* Cambia la visualizacion del comoponente de militar a meridian
* o visceversa
* @param value Falso si se quiere pasar a hora militar
*/

public function changeHourType(value : Boolean) : void
{
if(!value)
{
tMeridian.width=0;
hBox.width = tHour.width + tMinute.width + TextDosPuntos.width + 5;
vBox.x = hBox.width;
}
else
{
tMeridian.width=20;
hBox.width = tHour.width + tMinute.width + TextDosPuntos.width + tMeridian.width + 5;
vBox.x = hBox.width;
}
}

[Inspectable(type="Boolean")]
public function set twelveHour(twHour:Boolean):void {
this._twelveHour = twHour;
}

/**
* Vuelve a empezar el conteo de minutos para evitar errores
*/

public function resetMinutes(value : int) : void
{
_minutes = 0;
}

/**
*
* @return true o false
*/

public function get twelveHour() : Boolean
{
return this._twelveHour;
}

public function set hour(hr:Number):void {
this._hour = hr;
this.tHour.text = hr > 9 ? ""+hr : "0"+hr;
}

public function set minutes(mn:Number):void {
this._minutes = mn;
this.tMinute.text = mn > 9 ? ""+mn : "0"+mn;
}

/**
*
* @return Los minutos actuales que marca el componente
*/

public function get minutes():Number {
return this._minutes;
}

/**
*
* @return La hora actual que marca el componente
*/

public function get hour():Number {
return this._hour;
}

/**
* El salto en minutos que se espera que haga
* @return El salto en minutos que se espera que haga.
*/

public function get minutesJump() : int {
return this._minutesJump;
}

/**
* Calcula el salto en horas y en minutos que se debe saltar
* @param value el salto en minutos que se quiere saltar (Ej. 120min)
*/
[Inspectable(defaultValue=5, enumeration="1,5,10,15,30,60,120,180,240,300,360,420,480,540,600", type="int")]
public function set minutesJump(value : int):void
{
if(value>=60)
{
_minutesJump= 0;
_hoursJump = Math.floor(value/60);
}
else
{
this._minutesJump = value;
}
}

/**
*
* @return AM o FM
*/

public function get meridian():String {
return this._meridian;
}

public function set meridian(ampm:String):void {
this._meridian = ampm;
}

/**
* Si el usuario presiona el boton para subir la hora (o minuto)
*/

private function arrowUp():void {
if(editable)
{
if(focused == tHour && _twelveHour) {
adjustHour12(true);
} else if (focused == tHour && !_twelveHour) {
adjustHour24(true);
} else if (focused == tMinute) {
if(_minutesJump>0) adjustMinute(true);
} else if (focused == tMeridian) {
switchAm();
} else {
return;
}
dispatchEvent(new Event("onChange",true));
}
}

/**
* Si el usuario presiona el boton para bajar la hora (o minuto)
*/

private function arrowDown():void {
if(editable)
{
if(focused == tHour && _twelveHour) {
adjustHour12(false);
} else if (focused == tHour && !_twelveHour) {
adjustHour24(false);
} else if (focused == tMinute) {
if(_minutesJump>0) adjustMinute(false);
} else if (focused == tMeridian) {
switchAm();
} else {
return;
}
dispatchEvent(new Event("onChange",true));
}
}

/**
* Si se esta trabajando en Meridian y se quiere subir o bajar una hora
* @param up Un booleano "verdadero" si se esta subiendo en la hora
*/

private function adjustHour12(up:Boolean):void {
var n:Number = new Number(tHour.text);
if ((n<=12) && ((n+_hoursJump) > 12) && up) {
n = (n+_hoursJump)%12;
} else if ((n>=1) && ((n-_hoursJump) < 1) && !up) {
n = 12 + (n-_hoursJump);
} else if (up) {
n+=_hoursJump;
} else if (!up) {
n-=_hoursJump;
}
_hour = n;
if (n<10) {
tHour.text = "0"+n.toString();
} else {
tHour.text = n.toString();
}
}

/**
* Actualiza los valores en minutos del componenete
* @param up Un booleano "verdadero" si se esta subiendo en la hora
*/

public function adjustMinute(up:Boolean):void {
var n:Number = new Number(tMinute.text);
if (n == (60-minutesJump) && up)
{
if(_twelveHour) adjustHour12(up);
else adjustHour24(up);
n = 0;
} else if (n == 0 && !up) {
if(_twelveHour) adjustHour12(up);
else adjustHour24(up);
n = 60 - minutesJump;
} else if (up) {
n+=minutesJump;
} else if (!up) {
n-=minutesJump;
}
if (n<10) {
tMinute.text = "0"+n.toString();
} else {
tMinute.text = n.toString();
}
_minutes = n;
}

/**
* Actualiza los valores en horas militares del componenete
* @param up Un booleano "verdadero" si se esta subiendo en la hora
*/

private function adjustHour24(up:Boolean):void {
var n:Number = new Number(tHour.text);
if ((n+_hoursJump) > 23 && up) {
n = (n+_hoursJump)%24;
} else if ((n-_hoursJump) <= 0 && !up) {
n = 24 + (n -_hoursJump);
} else if (up) {
n+=_hoursJump;
} else if (!up) {
n-=_hoursJump;
}
if (n<10) {
tHour.text = "0"+n.toString();
} else {
tHour.text = n.toString();
}
_hour = n;

}

/**
* Cambia de AM a FM o visceversa
*/

public function switchAm():void {
if (tMeridian.text == "AM") {
tMeridian.text = "PM";
} else {
tMeridian.text = "AM";
}
_meridian = tMeridian.text;
dispatchEvent(new Event("onChange",true));

}

/**
* Valida que los valores del campo para AM y PM sean coherentes (o AM o FM)
*/

public function validateAm():void {
if (tMeridian.text.toUpperCase() != "AM" && tMeridian.text.toUpperCase() != "PM") {
if (tMeridian.text.length > 0 && tMeridian.text.toUpperCase().charAt(0) == 'P') {
tMeridian.text="PM";
} else {
tMeridian.text="AM";
}
}
tMeridian.text = tMeridian.text.toUpperCase();
_meridian = tMeridian.text;
dispatchEvent(new Event("onChange",true));
}

/**
* Se encarga de llamar a las funciones que actualizan las horas segun
* el tipo de visualizacion
* @param up Un booleano "verdadero" si se esta subiendo en la hora
*/

public function validateHour():void {
if (_twelveHour) {
validateHour12();
} else {
validateHour24();
}
dispatchEvent(new Event("onChange",true));
}

public function validateHour12():void {
var n:Number = new Number(tHour.text);
if (n >12) {
n= n % 12;
} else if (n < 1) {
n = 12 + n;
}
if (n < 10) {
tHour.text = "0"+n.toString();
} else {
tHour.text = n.toString();
}
_hour = n;
}

public function validateHour24():void {
var n:Number = new Number(tHour.text);
if (n >23) {
n= n % 24;
} else if (n < 0) {
n = 24 + n;
}
if (n < 10) {
tHour.text = "0"+n.toString();
} else {
tHour.text = n.toString();
}
_hour = n;
}

public function validateMinutes():void {
var n:Number = new Number(tMinute.text);
if (n >59) {
n=0;
} else if (n < 0) {
n = 60 - _minutesJump;
}
if (n < 10) {
tMinute.text = "0"+n.toString();
} else {
tMinute.text = n.toString();
}
_minutes = n;
dispatchEvent(new Event("onChange",true));
}

public function changeFocus(focusBox : TextInput):void {
focused=focusBox;
}

]]>
</mx:Script>

<mx:HBox id="hBox" horizontalGap="0" borderStyle="inset" verticalAlign="middle" height="22" horizontalScrollPolicy="off" backgroundColor="{backGroundColor}">
<mx:TextInput color="{textColor}" maxChars="2" restrict="0-9" borderStyle="none" textAlign="right" id="tHour" width="17" focusIn="changeFocus(tHour)" focusOut="validateHour()" backgroundAlpha="0.0"/>
<mx:TextInput color="{textColor}" id="TextDosPuntos" borderStyle="none" textAlign="right" editable="false" text=":" width="7" focusEnabled="false" backgroundAlpha="0.0"/>
<mx:TextInput color="{textColor}" maxChars="2" restrict="0-9" borderStyle="none" textAlign="right" id="tMinute" width="17" focusIn="changeFocus(tMinute)" focusOut="validateMinutes()" backgroundAlpha="0.0"/>
<mx:TextInput color="{textColor}" visible="{_twelveHour}" maxChars="2" restrict="APM" borderStyle="none" textAlign="right" id="tMeridian" click="switchAm()" width="20" focusIn="changeFocus(tMeridian)" focusOut="validateAm()" backgroundAlpha="0.0"/>
</mx:HBox>

<mx:VBox id="vBox" verticalAlign="bottom" verticalGap="0" horizontalGap="0">
<mx:Button verticalGap="0" cornerRadius="0" icon="@Embed(source='assets//uparrow.png')" click="arrowUp()" width="16" height="10"/>
<mx:Button verticalGap="0" cornerRadius="0" icon="@Embed(source='assets//downarrow.png')" click="arrowDown()" width="16" height="11"/>
</mx:VBox>

</mx:HBox>
Ejemplo

No hay comentarios:

Publicar un comentario en la entrada