I had an issue recently where there was a standard form item, with a textInput as the entry component. The problem was that a single line was not large enough for the client to enter in the details that they required. Using a text area would be my next logical choice however because of certain real estate restrictions I wanted to keep as many form elements visible on the screen at the one time.
Hence the Expandable Text area, a hybrid of the single lined TextInput and the TextArea. Where typing and removing text would re-size the input component accordingly, and setting a text value would expand the component to cater to the text size.
It’s a fairly simple component with the overridden measure method doing most of the work.
measure
Below in the measure command we utilize both the viewMetrics to determine the number or lines and then the measuredHeight of the TextArea
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
override protected function measure():void { super.measure(); // Calculate the default size of the control based on the // contents of the TextArea.text property. var lineMetrics:TextLineMetrics = measureText(text); // get the edge metrics of the textArea var vm:EdgeMetrics = viewMetrics; var w:Number = (explicitWidth - vm.left - vm.right); // calc the line count. If the textFields width has been // updated to the TextAreas explicit // height then we can use it's line count otherwise we use // the lineMetrics width divided // by the explicit width minus the edges var lineCount:int = Math.ceil(lineMetrics.width / w); // use the textFields linecount if it's the layout has been initialized if(useLineCount){ lineCount = textField.numLines; } // include the total for the line height and the // padding for top bottom and leading values measuredHeight = measuredMinHeight = (lineMetrics.height * lineCount) + vm.top + vm.bottom + lineMetrics.leading; } |
onTextChange
There is also a listener and handler for the “textChanged” event that is dispatched from the extended TextArea. This handler invalidates the size of the component if the number of lines in the textField has been modified. I also had to sets the variable called useLineCount to determine whether the textFields.numLines attribute should be used. As when the component is first initialized with text, the textFields layout is not yet completely set thus returning an incorrect numLines value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private function onTextChange(e:Event):void{ if(_prevLineCount != textField.numLines){ // because the lineCount of the textField is not // set as per the actual size during // the first phase of instantiation we know that // it's correct now so we can set a boolean to // indicate that. useLineCount = true; // invalidate the size this.invalidateSize(); _prevLineCount = textField.numLines; } |
updateDisplayList
And the last piece is the updateDisplayList which sets the actual size of the textField based on the number of lines and the components unscaledWidth. It also sets the explicitWidth of the component if it results in a NaN. This occurs if the component’s width is determined by a percentage or layout constraints.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
override protected function updateDisplayList(unscaledWidth:Number , unscaledHeight:Number):void{ // we just need to layout the chrome no need for scrollbars super.layoutChrome(unscaledWidth,unscaledHeight); // if the explicitWidth isn't set then set it. // This will force the invalidateSize() to be called. if(isNaN(explicitWidth)){ explicitWidth = unscaledWidth; } var vm:EdgeMetrics = viewMetrics; textField.move(vm.left, vm.top); var w:Number = unscaledWidth - vm.left - vm.right; var h:Number = unscaledHeight - vm.top - vm.bottom; // set the actual size of the textField based on the // number of lines and the default measured minimum // height of the component textField.setActualSize(w ,(textField.numLines) * (DEFAULT_MEASURED_MIN_HEIGHT)); } |
There it is, unfortunately it’s written with the Flex 3 SDK although I’ll eventually port it to 4. It’s a hybrid between the two text inputs which will enable the user to concentrate on data entry without having to worry about scroll bars and what they’d typed 50 characters ago.