I am using Flex 4 with Spark components to build a mobile application and I have a HGroup that I am using to contain all of my elements. When the screen loads it pulls in a small amount of text that will be displayed and loops through all the words to see if any of them are a keyword. While it is looping I am putting each word into its own label element and if the word is a keyword it changes a few styles and adds a click event to show a description about the word.
Everything runs fine but when everything is appended to the HGroup, there ends up being only one line and most of the text completely cut off because it will not wrap the content.
My Question is - Is there a way to set or extend the HGroup to allow content wrapping on its child elements?
Below are some code snippets of what I have:
MXML containers:
<s:VGroup id="answerData" width="580" height="700" horizontalAlign="center" paddingTop="5">
<s:HGroup id="theLabel" color="white" width="580" fontSize="25" paddingBottom="20" />
<s:HGroup id="theText" color="white" width="580" fontSize="25" maxWidth="580" />
</s:VGroup>
AS to create labels:
public static function setKeyWords(someText:String, theGroup:Group, theDictionary:Array, theView:Object):void {
theGroup.removeAllElements();
var textArray:Array = someText.split(' ');
for(var i:int = 0, l:int = textArray.length; i < l; i++) {
if(checkForWord(theDictionary, textArray[i].toString())) {
var theLink:Label = new Label();
theLink.text = textArray[i].toString();
theLink.setStyle("color", "0xFFFF00");
theLink.setStyle("fontWeight", "bold");
theLink.maxWidth = 580;
var tmpDescrip:String = theDescription;
theLink.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void {
showToolTip(tmpDescrip, theView);
});
theGroup.addElement(theLink);
} else {
var someLabel:Label = new Label();
someLabel.maxWidth = 580;
someLabel.text = textArray[i].toString();
theGroup.addElement(someLabel);
}
}
}
The issue that I was having is, I had multiple lables in a VGroup and needed them to wrap instead of extending past the containers set width. I was trying to integrate keywords into a dynamic paragraph of text. I could not use mx:Text because I needed each word to be its own component that allowed custom styling plus a mouse click even if the word was a keyword. Also the label max lines solution would not work because I am dealing with multiple lables in a VGroup and the VGroup needed to wrap its children not the label tags. I also could not use a TileGroup because it does not look right breaking a paragraph into a table looking component where each word is in its own column/row.
The solution I used was to count each character in the label being generated and add it to a variable to determine when I need to create a new HGroup that holds the labels and sits in a VGroup. I had to do this because I cannot determine the labels width until it renders because it is generated dynamically. This could not be done because as its render point is too late for me to move everything because the user can see all of this happening which is definitely not the desired effect.
Below is the code I used to solve this issue incase anyone else runs into this issue:
public static function setKeyWords(someText:String, theGroup:Group, theDictionary:Array, theView:Object):void {
theGroup.removeAllElements();
var textArray:Array = someText.split(' ');
var theCount:int = 0;
var theHGroup:HGroup = new HGroup();
var breakNum:int = 40;
theHGroup.percentWidth = 100;
for(var i:int = 0, l:int = textArray.length; i < l; i++) {
theCount += textArray[i].toString().length;
if(theCount >= breakNum) {
theGroup.addElement(theHGroup);
theHGroup = new HGroup();
theHGroup.percentWidth = 100;
theCount = 0;
}
if(checkForWord(theDictionary, textArray[i].toString())) {
theCount += 1;
var theLink:Label = new Label();
theLink.text = textArray[i].toString();
theLink.setStyle("color", "0xFFFF00");
theLink.setStyle("fontWeight", "bold");
theLink.maxWidth = 580;
//theLink.includeInLayout = false;
var tmpDescrip:String = theDescription;
theLink.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void {
showToolTip(tmpDescrip, theView, 'keywords');
});
theHGroup.addElement(theLink);
} else {
theCount += 1;
var someLabel:Label = new Label();
someLabel.maxWidth = 580;
someLabel.text = textArray[i].toString();
//someLabel.includeInLayout = false;
theHGroup.addElement(someLabel);
}
}
if(theCount > 0)
theGroup.addElement(theHGroup);
}
This may not be the most effecient way to do this but it does work and takes little time to execute on the Iphone which is what I was aiming for.
Not fully sure I understand your question.
If you mean the Label's are truncated, you might want to set a percentWidth and wrap Labels with maxDisplayedLines:
someLabel.maxDisplayedLines = 10;
If you mean you want columns and rows to your Group layout, use TileGroup / TileLayout.
If the children of your group contain composite content that must float, some kind of includeInLayout=false might help.
If you want text to show in block of several lines, use mx:Text with width set.
To display something above HGroup, easiest way is to leave HGroup alone and just make transparent container (Canvas) above it. There you'll be free to display anything (just do the math to position it correctly.)
Related
I wonder how to set the text "Highlight" of a part of text inside tlfTextField with the code?
I tried "tf.backgroundColor = 0x990000" property, but did not help.
For instance, I can change the Font Color of any contents inside Parenthesis, by this code:
private function decorate():void {
var tf:TextFormat = new TextFormat();
tf.color = 0x990000;
var startPoint:int = 0;
while (startPoint != -1) {
var n1:int = textMc.tlfText.text.indexOf("(", startPoint);
var n2:int = textMc.tlfText.text.indexOf(")", n1 + 1);
if (n1 == -1 || n2 == -1) {
return;
}
textMc.tlfText.setTextFormat(tf, n1 + 1, n2);
startPoint = n2 + 1;
}
}
So I know "tf.color = 0x990000;" will change the Font color, however, don't know how to "highlight" some text, with code, as I do inside Flash manually.
You should have probably used tlfMarkup property to set the required format to the specific part of text. The attributes you seek are backgroundColor and backgroundAlpha of the span XML element that you should wrap your selection, however it should be much more difficult should there already be spans around words when you retrieve the property from your text field.
The problem with your solution is that you don't check if the two characters are located on a single line before drawing your rectangle, also you would need to redraw such rectangles each time something happens with the textfield. The proposed approach makes use of Flash HTML renderer's capabilities to preserve the formatting, however it will require a lot of work to handle this task properly.
In looking at this image you can see in an ideal world each box would have the same height of content in each box. However in the real world we can't control how many characters the client uses for a heading. Wondering thoughts on how to deal with a situation like this? Is it ok to just let it be as is?
This will create an array of heights of an element by class, then find the tallest, and then make them all that height.
<script>
var headerHeights = [];
var mclength = document.getElementsByClassName("myClass").length;
for (i = 0; i < mclength; i++) {
headerHeights[i] = document.getElementsByClassName("myClass")[i].getBoundingClientRect().height;
}
var headerMaxHeight = Math.max(...headerHeights);
for (i = 0; i < mclength; i++) {
document.getElementsByClassName("myClass")[i].style.height = headerMaxHeight+"px";
}
</script>
You will likely want to make this a function which replaces "myClass" with a function parameter so that you can call it for each class you add. You will also want to add a listener for when a person resizes their window to rerun the function.
I have a responsive app for desktop and mobile.
In the app i have a div which randomly shows texts of all kinds of lengths.
I want to do the following:
If the line breaks because the length of the text is too wide for the width of that div, i want the font-size to reduce itself (I am using em's in my app).
Is it something i need to build directive for it? is it something that was built and used wildly?
Writing a robust solution for this problem is going to be non-trivial. As far as I know, there's no way to tell whether a line of text breaks. However, we do know the criteria for line breaking is the width of the text being wider than the element, accounting for padding.
The Canvas API has a method called measureText which can be used to measure a string, using a given context with a font and size set. If you spoof the settings of the element with a canvas, then you can measure the text with the canvas and adjust the size until it fits without overflowing.
I've written up a rough implementation of the way I would tackle this.
function TextScaler(element) {
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
var scaler = {};
scaler.copyProps = function() {
var style = element.style.fontStyle,
family = element.style.fontFamily,
size = element.style.fontSize,
weight = element.style.fontWeight,
variant = element.style.fontVariant;
context.font = [style, variant, weight, size, family].join(' ');
};
scaler.measure = function(text) {
text = text || element.innerText;
return context.measureText(text);
};
scaler.overflows = function() {
var style = window.getComputedStyle(element),
paddingLeft = style['padding-left'],
paddingRight = style['padding-right'],
width = style.width - paddingLeft - paddingRight;
return scaler.measure() > width;
};
scaler.decrease = function() {
// decrease font size by however much
};
scaler.auto = function(retries) {
retries = retries || 10;
if(retries <= 0) {
scaler.apply();
console.log('used all retries');
}
if(scaler.overflows()) {
scaler.decrease();
scaler.auto(retries - 1);
} else {
console.log('text fits');
scaler.apply();
}
};
scaler.apply = function() {
// copy the properties from the context
// back to the element
};
return scaler;
}
After you've sorted out some of the blank details there, you'd be able to use the function something like this:
var element = document.getElementById('');
var scaler = TextScaler(element);
scaler.auto();
If it doesn't manage to decrease it within 10 retries, it will stop there. You could also do this manually.
while(scaler.overflows()) {
scaler.decrease();
}
scaler.apply();
You'd probably want some fairly fine tuned logic for handling the decrease function. It might be easiest to convert the ems to pixels, then work purely with integers.
This API could quite trivially be wrapped up as a directive, if you want to use this with Angular. I'd probably tackle this with two attribute directives.
<div text-scale retries="10">Hello world</div>
Of course, if it's not important that all the text is there onscreen, then you can just use the text-overflow: ellipsis CSS property.
I want to get radioButton's label height in as3.
I have 5 radio buttons in my radioButton group. One of those radioButtons has a three line label, another has a single line etc...
When I try to access the radioButton.height, I always get same value regardless of the label height.
I need to know the height of the label, so I can set the radioButton y coordinate accordingly.
can I change the radioButton's label height?
spacing = 30;
rb.label = tempQQString //from xml. long or short string anyone
rb.group = myGroup;
rb.value = i + 1;
rb.x = answerX;
rb.y = questionField.height + (questionY+20) + (i * spacing); // here use rb.height or rb.textField.height
trace("rb height**" + rb.height) // always get velue 22;
addChild(rb);
RadioButton and any fl control that extends labelButton (all the ones that have a label), expose the actual text field with the .textField property.
So for your example, instead of using rb.height, you would use rb.textField.height to get the height of just the label portion of the control.
You can also set properties on the textField as well.
rb.textField.background = true;
rb.textField.backgroundColor = 0xDDDDDD;
rb.textField.multiline = true;
rb.textField.wordWrap = true;
rb.textField.autoSize = TextFieldAutoSize.LEFT;
now, for your scenario, you may be better off just using the radio button's bounds instead, as it will be the REAL height of the object.
rb.getBounds(rb).height; //this will be the true height of the component.
If your label was one line and your icon was taller than your label, you could potentially be getting a value from rb.textField.height that is smaller than the real height of the radio button.
Try this.
for(var i=0;i<rb.numChildren;i++){
if(rb.getChildAt(i) is TextField){
var txt:TextField = rb.getChildAt(i) as TextField;
trace(txt.height+":"+rb.height);//traces the height of text field and radio button
}
}
I need to realize textbox autoresizing in actionscript3(IDE - adobe flash pro cs3). For example my textarea is in width 100 px, user has been wrote in it something, that is bigger than 100 px, then my textbox should become increasingly. any ideas?
Also I can't realize multiline option: when the text goes beyond the textbox, it starts to scroll. In line type I've chosen 'multiline'.
thanks
try this:
textfield.autoSize = "left";
textfield.multiline = true;
textfield.wordWrap = true;
Hope it helps,
Rob
If you want to resize textfield automaticaly you can use textfield.autoSize property.
Wnen you are using multiline textfield, then setting
textfield.autoSize = TextFieldAutoSize.LEFT;
will align text to left and resize field vertically. If you use single line text field, it will resize to the right.
The TextField's .autosize property is great for sizing dynamic text fields when you already know the text string (but mind the .multiline and .wordwrap properties), but won't be helpful for input text fields.
For input text, I'd suggest listening for the Event.CHANGE event, then updating the width/height based on the number of lines, the .textWidth, or TextLineMetrics info (e.g. myTextField.getLineMetrics).
Here's a quick example:
var myField:TextField = new TextField();
myField.x = 10;
myField.y = 10;
myField.width = 100;
myField.height = 20;
myField.border = true;
myField.type = TextFieldType.INPUT;
myField.addEventListener(Event.CHANGE, textChangeHandler);
addChild(myField);
function textChangeHandler(evt:Event) {
var buffer:Number = 10;
myField.width = Math.max(100, (myField.textWidth + buffer));
myField.scrollH = 0;
}
Edit: Oh, and if you want that to work with .multiline, then just add:
myField.multiline = true;
and in the textChangeHandler function add:
myField.height = myField.textHeight + buffer;