Native Text Input with StageText

One of the major new features of AIR 3 (release candidate available on Adobe Labs) is the introduction of the StageText API. StageText allows developers to place native text inputs in their mobile AIR applications rather than using the standard flash.text.TextField API. StageText is essentially a wrapper around native text fields, so the resulting input is identical to the text input fields used in native applications. (Note that StageText is only for the AIR mobile profile; the StageText APIs are simulated on the desktop, but they do not result in native text inputs on Mac or Windows.)

Advantages of Using StageText

There are several advantages to using StageText over flash.text.TextField in mobile applications:

  • Both iOS and Android have sophisticated auto-correct functionality that StageText allows AIR applications to take advantage of.
  • The autoCapitalize property of StageText allows you to configure how auto-capitalization is applied to your text field. Options are specified as properties of the AutoCapitalize class and include ALL, NONE, SENTENCE, and WORD.
  • StageText allows you to control the type of virtual keyboard that is displayed when the text field gets focus. For example, if your text field is requesting a phone number, you can specify that you want a numeric keypad, or if you’re asking for an email address, you can request a soft keyboard with an "@" symbol. Options are specified as properties of the SoftKeyboardType class, and include CONTACT, DEFAULT, EMAIL, NUMBER, PUNCTUATION, and URL.
  • In addition to controlling the type of virtual keyboard, you can also configure the label used for the return key. Options are specified as properties of the ReturnKeyLabel class, and include DEFAULT, DONE, GO, NEXT, and SEARCH.

The Challenges of Using StageText

On mobile devices, StageText provides much more powerful text input capabilities than flash.text.TextField, however there are some challenges to adding native visual elements to AIR applications.

StageText is Not on the Display List

StageText is not the first time we’ve exposed native functionality directly in AIR. The StageVideo class (mobile and Adobe AIR for TV) allows video to be rendered in hardware at the OS level, and StageWebView (mobile only) allows HTML to be rendered with the device’s native HTML engine. All three of these features have similar benefits (native performance and functionality), but they also share the same unusual characteristic: although they are visual elements, they are not part of the Flash display list. In other words, like StageVideo and StageWebView, StageText does not inherit from DisplayObject, and therefore cannot be added to the display list by calling the addChild() function. Instead, StageText is positioned using its viewPort property which accepts a Rectangle object specifying the absolute (that is, relative to the stage) coordinates, and the size of the native text field to draw. The text field is then rendered on top of all other Flash content; even if Flash content is added to the display list after a StageText instance, the StageText instance will always appear to have the highest z-order, and will therefore always be rendered on top.

In order to help address this behavior, StageText has has a function called drawViewPortToBitmapData. The drawViewPortToBitmapData function provides similar functionality to the IBitmapDrawable interface when used in conjunction with the BitmapData.draw function. Specifically, you can pass a BitmapData instance into the drawViewPortToBitmapData function to create a bitmap identical to the StageText instance. You can then toggle the visible property of your StageText instance, add the bitmap copy to the display list at the same coordinates as the viewPort of your StageText, and then you can place other Flash content directly on top without your StageText instance showing through. It seems like a lot of work, but it’s actually very easy to implement (especially when nicely encapsulated), and provides a good work-around for the fact that native visual elements do not render through the Flash pipeline. (Note that StageWebView also has the drawViewPortToBitmapData function which allows for the same work-around.)

StageText Does Not Have Borders

Something else that sets StageText apart from flash.text.TextField is the fact that it is not rendered with a border. There were two primary reasons for this decision:

  1. Developers will frequently want to customize their StageText borders.
  2. Native text inputs in native applications are sometimes rendered without any borders, especially on iOS. (For an example, compose a new email in the Mail application and note how the "To" and "Subject" fields have dividers between them, but no borders around them.)

Unfortunately, drawing borders around native text fields isn’t as easy as it sounds. In order to perfectly vertically center text between horizontal borders, and to constrain the text perfectly within vertical borders, developers need access to sophisticated font metrics APIs which are available for the two different text engines in Flash (flash.text and flash.text.engine), but they are not available for StageText. Drawing a perfectly sized and positioned border around a StageText instance so that it will work with different fonts and font properties, and work consistently across platforms, is somewhat challenging.

Solutions

Now that I’ve pointed out the challenges of working with StageText, it’s only fair that I provide some solutions.

For Flex Developers

Flex will address the two challenges of working with StageText described above (the fact that it’s not part of the display list, and the drawing of borders) by encapsulating StageText inside of spark.components.TextInput. The TextInput component will worry about drawing a border around the StageText instance, and as long as you use the Flex APIs for manipulating the display list (as opposed to manipulating the display list at the Flash API level), Flex will also manage the viewPort property for you. In other words, Flex will give you get all the advantages of StageText without you having to manage it yourself.

For ActionScript Developers

Developers who are writing in ActionScript directly rather than using Flex have a few options:

  1. Use the StageText APIs directly. This is probably the least practical approach since you will likely find that trying to manage the display list, and trying to manage visual elements not on the display list in a completely different way, will be complex and error-prone.
  2. Encapsulate StageText. You can always do what the Flex team did, and encapsulate StageText to make it easier to work with. This is a fair bit of work, but in the end, it will make it much easier to manage, and will probably prove to be a good time investment.
  3. Use the NativeText class. While validating the encapsulation of StageText, I wrote a pretty comprehensive wrapper which makes working with StageText much easier (details below).

Introducing NativeText

NativeText is a wrapper around StageText which makes adding native text input functionality to your ActionScript application much easier. NativeText has the following advantages:

  • It makes StageText feels like it’s part of the display list. You can create an instance of NativeText, give it x and y coordinates, and add it to the display list just like you would any other display object. All the management of the viewPort is completely encapsulated.
  • It’s easy to add borders. NativeText does the work of drawing borders such that the StageText instance is vertically centered and horizontally bound. You can even change the border’s thickness, color, and corner radius.
  • It’s easy to add content on top because it manages the bitmap swapping for you. NativeText has a function called freeze which does all the work of drawing the StageText to a bitmap (including the border), hiding the StageText instance, and positioning the bitmap exactly where the StageText was. Therefore, if you want to place content on top of a NativeText instance (for instance, an alert box), just call the freeze function first, and everything will work perfectly. When you’re done, call unfreeze, and the bitmap is replaced with the StageText instance and disposed of.
  • Support for multi-line text inputs. NativeText supports text fields of one or more lines. Just pass the number of lines of text you want into the constructor (this is the one property that cannot be changed dynamically), and both the border and viewPort will be properly managed for you.
  • Cross platform. The trickiest part of writing NativeText was making sure it rendered properly on all our reference platforms (Mac, Windows, iOS, and Android) which use different fonts, have different sized cursors, etc. NativeText lets you use any font, font size, and border thickness you want, and your text input should render properly and consistently on all supported platforms.

NativeText is included in a project called StageTextExample. It is open-source, and the code is available on GitHub (or you can download the ZIP file for importing into Flash Builder). This is something I worked on independently, and is not an officially supported Adobe solution, so although I’ve found that it works pretty well, it’s certainly possible that you could discover bugs or use cases it does not take into account. If that’s the case, let me know and/or feel free to hack away at the code yourself.