Вот решение, которое я использую в производственных системах:
package com.npacemo.component
{
import flash.events.Event;
import flash.events.FocusEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.ui.Keyboard;
import mx.core.UIComponent;
import spark.components.Label;
import spark.components.PopUpAnchor;
import spark.components.TextInput;
import spark.components.supportClasses.SkinnableComponent;
[Event(name="change", type="flash.events.Event")]
public class EditableLabel extends SkinnableComponent
{
[SkinState("normal")]
[SkinState("selected")]
[SkinPart(required="true")]
public var labelComponent:Label;
[SkinPart(required="true")]
public var inputAnchor:PopUpAnchor;
[SkinPart(required="true")]
public var inputComponent:UIComponent;
[Bindable]
public var text:String;
public function EditableLabel()
{
addEventListener(MouseEvent.CLICK, handleDisplayLabelClick);
}
override protected function partAdded(partName:String, instance:Object):void
{
if (instance == labelComponent)
{
labelComponent.addEventListener(MouseEvent.CLICK, handleDisplayLabelClick);
}
else if (instance == inputComponent)
{
inputComponent.addEventListener(Event.CHANGE, handleInputComponentChange);
inputComponent.addEventListener(KeyboardEvent.KEY_DOWN, handleTextInputKeyDown);
inputComponent.addEventListener(FocusEvent.FOCUS_OUT, handleInputComponentFocusOut);
}
}
private function handleInputComponentChange(e:Event):void
{
text = (inputComponent as TextInput).text;
dispatchEvent(e.clone());
}
private function handleDisplayLabelClick(event:MouseEvent):void
{
skin.currentState = "selected";
stage.addEventListener(MouseEvent.CLICK, handleStageClick);
}
private function handleStageClick(e:MouseEvent):void
{
if (!inputComponent.hitTestPoint(stage.mouseX, stage.mouseY))
{
stage.removeEventListener(MouseEvent.CLICK, handleStageClick);
skin.currentState = "normal";
}
}
private function handleTextInputKeyDown(event:KeyboardEvent):void
{
if (event.charCode == Keyboard.ENTER)
{
stage.removeEventListener(MouseEvent.CLICK, handleStageClick);
skin.currentState = "normal";
}
}
private function handleInputComponentFocusOut(event:FocusEvent):void
{
stage.removeEventListener(MouseEvent.CLICK, handleStageClick);
skin.currentState = "normal";
}
}
}
И это примерный скин:
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<!-- host component -->
<fx:Metadata>
[HostComponent("com.npacemo.component.EditableLabel")]
</fx:Metadata>
<!-- SkinParts
name=inputComponent, type=mx.core.UIComponent, required=true
name=labelComponent, type=spark.components.Label, required=true
-->
<s:states>
<s:State name="normal"/>
<s:State name="selected" enterState="inputComponent.setFocus(); inputComponent.selectRange(inputComponent.text.length, inputComponent.text.length);"/>
</s:states>
<s:transitions>
<s:Transition fromState="*" toState="*" autoReverse="true">
<s:Fade targets="{[labelComponent, inputComponent]}" duration="100"/>
</s:Transition>
</s:transitions>
<s:PopUpAnchor id="inputAnchor" x="-1" y="-7" displayPopUp.normal="false" displayPopUp.selected="true">
<s:TextInput id="inputComponent" text="{hostComponent.text}"
alpha.normal="0" alpha.selected="1"
enabled.normal="false" enabled.selected="true"
width="{labelComponent.width + 20}" focusSkin="{null}"
contentBackgroundColor="0xFBFCA4" borderVisible="false"
fontFamily="Futura" fontSize="12" textAlign="left">
<s:filters>
<s:DropShadowFilter angle="135" alpha="0.5" blurX="10" blurY="10"/>
</s:filters>
</s:TextInput>
</s:PopUpAnchor>
<s:Label id="labelComponent" text="{hostComponent.text}"
alpha.normal="1" alpha.selected="0"
visible.normal="true" visible.selected="false"
verticalCenter="0" width="{this.width+20}" maxDisplayedLines="1"
textDecoration="underline" buttonMode="true"/>
</s:Skin>