I've had two issues when working with TextArea and TextInput on mobile components I don't know how to solve. The first is that the TextArea text does not position correctly and the second is that it overlaps other components.
The issues occur when the TextArea is in a Scroller (or the soft keyboard activates and moves the TextArea position).
You can see this if you add the code below to a mobile Application:
<s:Scroller width="100%" height="100%" top="100" bottom="100">
<s:VGroup width="50%">
<s:Button label="" height="600" width="440"/>
<s:TextArea id="testing" />
<s:TextArea id="testing2" />
<s:Button label="" height="800" width="440"/>
</s:VGroup>
</s:Scroller>
The first image is the application with some text.
In the second image I've scrolled down some. As I've scrolled down notice that the text has remained in the same place (it's on top of the button that was above it)!
CAVEAT:
If I throw the view then the text is immediately positioned in the correct place (on the AIR Simulator) and is positioned again correctly when the content settles into it's final position (on mobile devices the text seems to disappear until it's final resting place). This is good news since something is happening on throw that is not happening when manually pressing, dragging and releasing (not throwing) or on soft keyboard activating.
Unfortunately, the text may still appears on top of all other content ignoring the scrollers mask but I can live with that if the other first problem is fixed.
UPDATE
I can get the text to reposition in the right position if I set the width to a width + 1. This is not going to work because I don't want to resize it obviously. I tried invalidating and nothing is working. Here is the code I tried in the softKeyboardActivating event. Uncomment out the width = width+1 to see it "work" :
<s:TextArea id="testing" softKeyboardActivate="testing_softKeyboardActivateHandler(event)"/>
<fx:Script>
<![CDATA[
import mx.core.IInvalidating;
protected function testing_softKeyboardActivateHandler(event:SoftKeyboardEvent):void {
trace("Activating");
/*testing.invalidateProperties();
testing.invalidateDisplayList();
testing.invalidateSize();
testing.validateNow();
parentGroup.validateNow();
scroller.validateNow();*/
// testing.invalidateParentSizeAndDisplayList();
//IInvalidating(testing.parent).invalidateSize();
//IInvalidating(testing.parent).invalidateDisplayList();
//testing.width = NaN;
//testing.width = testing.width+1;
}
]]>
</fx:Script>
UPDATE 2:
This code works but it's a hack and it's much slower than the code that the scroller is using when the view is thrown:
protected function testing_softKeyboardActivateHandler(event:SoftKeyboardEvent):void {
StageTextAreaSkin2(testing.skin).styleChanged("styleName");
}
UPDATE 3
I've added a workaround in the answers section below but it is a hack. Also, when it's in the right position it also is correctly masked. So that's good as well. However, I'm still looking for the right way to do this.
This has been tested with Flex 4.6, AIR 3.5 on Mac on the AIR Simulator, iPhone 5 and Android Nexus 7.
Here is a workaround. Create an MXML file (usually StageTextArea.mxml will do) and place this code in it.
<?xml version="1.0" encoding="utf-8"?>
<s:TextArea xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
softKeyboardActivate="softKeyboardActivateHandler(event)"
softKeyboardDeactivate="softKeyboardDeactivateHandler(event)"
added="addedHandler(event)"
removed="removedHandler(event)">
<!-- USAGE
<controls:StageTextArea id="myTextArea" parentScroller="{myScroller}"/>
-->
<fx:Script>
<![CDATA[
import mx.events.PropertyChangeEvent;
import spark.components.Scroller;
private var _parentScroller:Scroller;
public function get parentScroller():Scroller {
return _parentScroller;
}
/**
* Adds a listener to an ancestor scroller.
* */
[Bindable]
public function set parentScroller(value:Scroller):void {
if (value && value.viewport) {
value.viewport.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handle, false, 0, true);
}
if (value==null && _parentScroller) {
value.viewport.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handle);
}
_parentScroller = value;
}
/**
* Add listener to parent component when added to the display list
* */
protected function addedHandler(event:Event):void {
if (event.target==event.currentTarget) {
// we could "discover" the scroller here if we wanted
// or we could just use parentScroller property
owner.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handle, false, 0, true);
}
}
/**
* Remove listener to parent component when removed to the display list
* */
protected function removedHandler(event:Event):void {
if (event.target==event.currentTarget) {
owner.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handle);
}
}
/**
* Handle parent or ancestor scroll position changes
*/
private function handle(e:PropertyChangeEvent):void {
if (e.source == e.target && e.property == "verticalScrollPosition") {
//trace(e.property, "changed to", e.newValue);
updateTextFieldPosition();
}
if (e.source == e.target && e.property == "horizontalScrollPosition") {
//trace(e.property, "changed to", e.newValue);
updateTextFieldPosition();
}
}
/**
* Handles when keyboard activates
* */
protected function softKeyboardActivateHandler(event:SoftKeyboardEvent):void {
updateTextFieldPosition();
}
/**
* Handles when keyboard deactivates
* */
protected function softKeyboardDeactivateHandler(event:SoftKeyboardEvent):void {
updateTextFieldPosition();
}
/**
* Updates the native text fields position
* */
public function updateTextFieldPosition():void {
skin.styleChanged("anything"); // force skin class to revalidate
}
]]>
</fx:Script>
</s:TextArea>