问题
I am trying to create an editable label in flex4.
To do it I am extending the textfield class, so it contains a label control element. But I am failing to make a label visible, once the text field gets invisible.
The code looks this way:
package unimap.components
{
import spark.components.Label;
import spark.components.TextInput;
public class SmartTextInput extends TextInput
{
private var _label:Label;
public function SmartTextInput()
{
super();
}
public override function set editable(value:Boolean):void
{
super.editable = value;
if (value == false)
{
_label = new Label();
_label.x = this.x;
_label.y = this.y;
_label.width = this.width;
_label.height = this.height;
_label.text = "Home";
addChild( _label ); // This lines fail the code with error
// Error: addChild() is not available in this class. Instead, use addElement() or modify //the skin, if you have one.
}
super.visible = false;
trace("Editable")
}
}
}
But if I would change addChild to addElement I will see the following error: 1180: Call to a possibly undefined method addElement.
Can somebody advice what I am doing incorrectly?
回答1:
Here's a solution I use in production systems:
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";
}
}
}
And this is a sample skin component:
<?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>
回答2:
There are a few issues here. One, if I call enabled = false a few times I'm going to have multiple label children. Not what you are going for. You could build a composite control based on a Group control and create a custom skin, but that might be overkill. I would recommend just skinning a TextInput, and changing the appearance to look like a label when not enabled (e.g. visible.disabled=false on the backgrounds and borders and alpha.disabled=1.0 on the main skin element) The benefit of this is that you can apply it via CSS and use a default control.
回答3:
Basically, TextInput is not a Container, it's a text control component, so you cannot add children neither elements to it (with addElement or addChild methods).
You can create a skin for your TextInput that makes your TextInput "behave" as a Label.
Tell us what you want to do with your "editable label", that would make us able to give you a better answer.
来源:https://stackoverflow.com/questions/7494736/flex-editable-label-component