Scroll inside an item renderer result in an empty item - apache-flex

I'm developing a mobile app. I have a view with an horizontal list, each item has a long description so I would need a vertical scroll for that independent information. I'm trying to add an scroll as a Child of an itemRenderer. And I dont get it. Anyone knows what I'm doing bad?
I have a class with inheritance of ItemRenderer (I tried BaseRenderer from AsFusion too, that it seems to have more performance for mobile apps, with the same result).
And here is the part of the scroll from my code:
override protected function createChildren():void
{
super.createChildren();
scroller = new VScrollBar();
scroller.percentHeight = 100;
scroller.setStyle('right', 0);
scrollGroup = new Group();
scrollGroup.percentHeight = 100;
scrollGroup.percentWidth = 100;
super.addElement(scroller);
super.addElement(scrollGroup);
scroller.viewport = scrollGroup;
}
I also tried
override protected function createChildren():void
{
super.createChildren();
scroller = new Scroller();
scroller.percentHeight = 100;
scroller.percentWidth = 100;
scrollGroup = new Group();
scrollGroup.percentHeight = 100;
scrollGroup.percentWidth = 100;
super.addElement(scroller);
super.addElement(scrollGroup);
scroller.viewport = scrollGroup;
}
And the result is the same. An empty item in the list. I can change page (pagesnapping of the list horizontal scroll) and the next item is also empty. If I delete addElement(scroller) I can see the items perfectly, but without the vertical scroll that I really need. So the problem is in the scroller. Any idea what I'm doing so bad?? Please? I need the solution in actionscript, I have more itemrenderers done and I will make inheritance, and the performance for the mobile is better in actionscript. Thank you in advance.

I've never used scroll bars in an item renderer... But you might check out the Scroller component? Something like this:
<s:ItemRenderer>
<s:Scroller width="100%" height="100%">
<s:Group>
<Your_components_here />
</s:Group>
</s:Scroller>
</s:ItemRenderer>
Not sure if it would behave any different though.

In order to work, scrolls need an actual width and height. It seems that the groups you're passing are actually empty, although they do have a set percentWidth. Add content into them.
If you're scrolling text, it might be more viable to use the built in scroll of the TextArea.

I solved, with the guidance of Grigorash Vasilij I noticed that the content of the scroller was not showing because in the group the content size was 0 and private visible variable was false. So the percent sizes of the scroller was not working, I updated it in the method updateDisplayList.
override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ):void
{
super.updateDisplayList( unscaledWidth, unscaledHeight );
scroller.width = unscaledWidth;
scroller.height = unscaledHeight;
...
}
After that, the scroller was horizontal, then the horizontal scroll wasn't work, I wanted a verticalScroll if needed in the item Renderer, so after the constructor of Scroller I set the horizontalPolicy of the scroll equal to off. The result is the next:
override protected function createChildren():void
{
super.createChildren();
scroller = new Scroller();
scroller.percentHeight = 100;
scroller.percentWidth = 100;
scroller.setStyle("horizontalScrollPolicy", "off");
scrollGroup = new Group();
scrollGroup.percentHeight = 100;
scrollGroup.percentWidth = 100;
addChild(scrollGroup);
scroller.viewport = scrollGroup;
addChild(scroller);
}
My class is inheriting of BaseRenderer from Asfusion If you inherit of itemrenderer use addElement instead of addChild.

Related

scroll beyond maximum vertical position in flex datagrid

I have a situation where I need to show several 1-column DataGrids one beside the other and control them with 1 vertical scroll bar. (the result looks like 1 DataGrid but since I use a variable row height, each "column" can have a different row height on each row.
In order to achieve the required result, I created several grids and stacked them in an HBox (I know I should use spark but I want to retain my styles and my entire application is in Halo). Outside of the HBox, I have a VerticalScrollBar and I'm able to control the grid's scroll position since I extended the DataGrid and added a vertical scroll bar getter.
For simplicity, I've stripped the code to the bare essentials:
<mx:Canvas width="100%" height="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:HBox id="viewPort" top="1" right="15" bottom="0" left="1" minWidth="0"
horizontalScrollPolicy="auto" verticalScrollPolicy="off">
</mx:HBox>
<mx:VScrollBar id="vScrollBar" top="24" right="0" bottom="0" scroll="onScroll(event)" />
</mx:Canvas>
private function populateGrids() : void
{
var item:Object;
var maxScrollPosition:Number = 0;
var dataGrid:CustomDataGrid;
var dataProvider:Object = {'1':[11,12,13,14,15,16], '2':[21,22,23,24,25,26,27,28], '3':[31]};
viewPort.removeAllChildren();
for (item in dataProvider) {
var col:DataGridColumn = new DataGridColumn();
col.headerText = "Grid" + item;
col.sortable = false;
dataGrid = new CustomDataGrid();
viewPort.addChild(dataGrid);
dataGrid.minWidth = 200;
dataGrid.percentHeight = 100;
dataGrid.percentWidth = 100;
dataGrid.headerHeight = 23;
dataGrid.selectable = false;
dataGrid.variableRowHeight = true;
dataGrid.columns = [col];
dataGrid.dataProvider = dataProvider[item];
}
viewPort.validateNow();
for (item in dataProvider) {
dataGrid = (viewPort.getChildAt(int(item)) as CustomDataGrid);
maxScrollPosition = Math.max(maxScrollPosition, dataGrid.vScrollBar.maxScrollPosition);
}
vScrollBar.maxScrollPosition = maxScrollPosition;
vScrollBar.scrollPosition = 0;
// need to shut-off the vertical scroller only after the maxScrollPosition is calculated
// otherwise, it doesn't calculate it ...
for (item in dataProvider) {
dataGrid = (viewPort.getChildAt(int(item)) as CustomDataGrid);
dataGrid.verticalScrollPolicy = "off";
}
}
private function onScroll(e:ScrollEvent) : void
{
var grids:Array = viewPort.getChildren();
for each (var grid:CustomDataGrid in grids) {
grid.verticalScrollPosition = e.currentTarget.scrollPosition;
}
}
While this does show the grids and scrolls them, the item renderers are not reused correctly (probably due to the verticalScrollPositon which might be larger than the actual maximum scroll position for each grid).
Am I going at it from a wrong position? Is there a better way to achieve what I want?
If not, is there a way to assign a higher-than-max verticalScrollPosition to a DataGrid and have it show a blank cell where no data exists?
I Hope this was coherent enough ...
BTW: I'm using Flex 4.5.1

Flex: Custom composite component in ActionScript -- problems with percentWidth/percentHeight

I've done tons of research and read the Adobe Live Docs till my eyes bled trying to figure this out.
I am building a composite custom component in ActionScript, and would like the sub controls to be laid out horizontally. This works fine by adding them to an HGroup and adding HGroup to the component, the problem comes with percentage based sizing.
I need _horizontalGroup:HGroup to size it self based on the size of its container.
stepping through the code shows that the parent properties of each UIComponent are...
_horizontalGroup.parent = ReportGridSelector
ReportGridSelector.parent = grpControls
If grpControls has an explicit size, shouldn't ReportGridSelector have its size as well?
The custom component is implemented like this...
NOTE: ReportControl extends UIComponent and contains no sizing logic
public class ReportGridSelector extends ReportControl{
/*other display objects*/
private var _horizontalGroup:HGroup;
public function ReportGridSelector(){
super();
percentHeight = 100;
percentWidth = 100;
}
override protected function createChildren():void{
super.createChildren();
if(!_horizontalGroup){
_horizontalGroup = new HGroup();
//I WANT SIZE BY PERCENTAGE, BUT THIS DOESN'T WORK
_horizontalGroup.percentWidth = 100;
_horizontalGroup.percentHeight = 100;
//EXPLICITLY SETTING THEM WORKS, BUT IS STATIC :-(
//_horizontalGroup.width = 200;
//_horizontalGroup.height = 200;
addChild(_horizontalGroup);
}
}
}
Consuming MXML code
<?xml version="1.0" encoding="utf-8"?>
<s:VGroup id="grpControls" width="200" height="200">
<ReportControls:ReportGridSelector width="100%" height="100%"/>
</s:VGroup>
If I explicitly define _horizontalGroup's width and height properties, everything displays fine. If I try _horizontalGroup.percentWidth or percentHeight, all the controls get scrunched together.
Any thoughts as to what is going on?
Perform layout when the display list is invalidated from updateDisplayList.
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
_horizontalGroup.width = unscaledWidth;
_horizontalGroup.height = unscaledHeight;
}
Understand the Flex component lifecycle:
http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_2.html
createChildren
Creates any child components of the component. For example, the
ComboBox control contains a TextInput control and a Button control as
child components.
For more information, see Implementing the createChildren() method.
updateDisplayList
Sizes and positions the children of the component on the screen based
on all previous property and style settings, and draws any skins or
graphic elements used by the component. The parent container for the
component determines the size of the component itself.

Trying to draw a Rectangle to a Custom Container in Flex4/AS3

So below is the code I have so far. For now I simply want to make it draw a square and have it show up. Right now when I click the area defined in MXML as <components:PaintArea width="100%" height="100%" id="paint-a"></PaintArea> it shows nothing; however, the actionlistener is getting triggered and an element is being added to the group. Not sure exactly what is going on... perhaps for some reason it doesn't think the element is drawable? Anyways thanks for the help!
public class PaintArea extends SkinnableContainer
{
private var canvas:Group;
public function PaintArea()
{
super();
canvas = new Group();
canvas.clipAndEnableScrolling = true;
canvas.percentHeight = 100;
canvas.percentWidth = 100;
canvas.addEventListener(MouseEvent.MOUSE_UP,drawRectangle);
this.addElement(canvas);
}
private function drawRectangle(e:MouseEvent):void{
var r:Rect = new Rect();
r.fill = new SolidColor(0x00ff00,.5);
canvas.addElement(r);
}
}
You should probably set the width and height of the rectangle r.
You could also use a BorderContainer (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/components/BorderContainer.html) - its a SkinnableContainer with a styleable border and fill

Is there a multiline text workaround for Flex

Is there a workaround for displaying multiline text in Flex 3? The two controls I have tried so far are mx:Text, and mx:TextArea. Each control has its own bug associated with it. For reference: mx:Text bug - http://bugs.adobe.com/jira/browse/SDK-9819 mx:TextArea bug - http://bugs.adobe.com/jira/browse/SDK-12616. Basically, neither control handles scrolling correctly if you do not specify a height and the text wraps onto the next line (height is determined dynamically by Flex, based on the wrapping). Does anybody have a workaround that might be helpful?
Thanks.
Update: One of the methods I have tried in the past has been to manually calculate the height of a mx:Text element. I can do this by using the following:
var textItem:Text = new Text();
var len:int = value.length;
var lines:int = int(len/115) + 1;
var height:int = lines * 20;
textItem.height = height;
While this seems to get around the problem in mx:Text, there is one big fault. The calculation relies heavily on font-size, letter-spacing, and the width of textItem. I can use this method, and move on with my project. However, maintenance on this is inevitable, and with code like this, it will a gigantic PITA.
I've had to deal with this a few times myself. The best way I've found to get dynamic height sizing of <mx:Text> is to leave the height out of the text and then specify a percent height of 100% on the enclosing VBox, HBox, etc. Something like the following should work for you:
<mx:VBox width="100%" height="100%">
<mx:Text text="Your really long text goes here." width="100%"/>
</mx:VBox>
As this is a bit of a hack itself, your milage may vary.
Edit
If you want to extend your above example so that maintenance on the code is easier, you should look into the TextLineMetrics class. This will allow you to measure the width and height of your text, taking into account font, size, etc. The docs for TextLineMetrics can be found here. To use your above example, you'd want to do something like the following:
var textItem:Text = new Text();
var metrics:TextLineMetrics = textItem.measureText( value );
var len:int = metrics.width;
var lines:int = int(len/textItem.width) + 1;
var height:int = lines * metrics.height;
textItem.height = height;
I use a variable height text area class that works very well for me:
package
{
import mx.controls.TextArea;
/**
* TextArea that xpands to the height of the content contained
* within.
* #author joel
*
*/
public class VariableHeightTextArea extends TextArea
{
public function VariableHeightTextArea()
{
super();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(this.height != int(this.textField.measuredHeight) + 5 )
{
this.height = this.textField.measuredHeight + 5;
}
}
}
}

Flex: Custom Item Renderer For Combobox controls truncates text

I've implemented a custom item renderer that I'm using with a combobox on a flex project I'm working on. It displays and icon and some text for each item. The only problem is that when the text is long the width of the menu is not being adjusted properly and the text is being truncated when displayed. I've tried tweaking all of the obvious properties to alleviate this problem but have not had any success. Does anyone know how to make the combobox menu width scale appropriately to whatever data it's rendering?
My custom item renderer implementation is:
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
styleName="plain" horizontalScrollPolicy="off">
<mx:Image source="{data.icon}" />
<mx:Label text="{data.label}" fontSize="11" fontWeight="bold" truncateToFit="false"/>
</mx:HBox>
And my combobox uses it like so:
<mx:ComboBox id="quicklinksMenu" change="quicklinkHandler(quicklinksMenu.selectedItem.data);" click="event.stopImmediatePropagation();" itemRenderer="renderers.QuickLinkItemRenderer" width="100%"/>
EDIT:
I should clarify on thing: I can set the dropdownWidth property on the combobox to some arbitrarily large value - this will make everything fit, but it will be too wide. Since the data being displayed in this combobox is generic, I want it to automatically size itself to the largest element in the dataprovider (the flex documentation says it will do this, but I have the feeling my custom item renderer is somehow breaking that behavior)
Just a random thought (no clue if this will help):
Try setting the parent HBox and the Label's widths to 100%. That's generally fixed any problems I've run into that were similar.
Have you tried using the calculatePreferredSizeFromData() method?
protected override function calculatePreferredSizeFromData(count:int):Object
This answer is probably too late, but I had a very similar problem with the DataGrid's column widths.
After much noodling, I decided to pre-render my text in a private TextField, get the width of the rendered text from that, and explicitly set the width of the column on all of the appropriate resize type events. A little hack-y but works well enough if you haven't got a lot of changing data.
You would need to do two things:
for the text, use mx.controls.Text (that supports text wrapping) instead of mx.controls.Label
set comboBox's dropdownFactory.variableRowHeight=true -- this dropdownFactory is normally a subclass of List, and the itemRenderer you are setting on ComboBox is what will be used to render each item in the list
And, do not explicitly set comboBox.dropdownWidth -- let the default value of comboBox.width be used as dropdown width.
If you look at the measure method of mx.controls.ComboBase, you'll see that the the comboBox calculates it's measuredMinWidth as a sum of the width of the text and the width of the comboBox button.
// Text fields have 4 pixels of white space added to each side
// by the player, so fudge this amount.
// If we don't have any data, measure a single space char for defaults
if (collection && collection.length > 0)
{
var prefSize:Object = calculatePreferredSizeFromData(collection.length);
var bm:EdgeMetrics = borderMetrics;
var textWidth:Number = prefSize.width + bm.left + bm.right + 8;
var textHeight:Number = prefSize.height + bm.top + bm.bottom
+ UITextField.TEXT_HEIGHT_PADDING;
measuredMinWidth = measuredWidth = textWidth + buttonWidth;
measuredMinHeight = measuredHeight = Math.max(textHeight, buttonHeight);
}
The calculatePreferredSizeFromData method mentioned by #defmeta (implemented in mx.controls.ComboBox) assumes that the data renderer is just a text field, and uses flash.text.lineMetrics to calculate the text width from label field in the data object. If you want to add an additional visual element to the item renderer and have the ComboBox take it's size into account when calculating it's own size, you will have to extend the mx.controls.ComboBox class and override the calculatePreferredSizeFromData method like so:
override protected function calculatePreferredSizeFromData(count:int):Object
{
var prefSize:Object = super.calculatePrefferedSizeFromData(count);
var maxW:Number = 0;
var maxH:Number = 0;
var bookmark:CursorBookmark = iterator ? iterator.bookmark : null;
var more:Boolean = iterator != null;
for ( var i:int = 0 ; i < count ; i++)
{
var data:Object;
if (more) data = iterator ? iterator.current : null;
else data = null;
if(data)
{
var imgH:Number;
var imgW:Number;
//calculate the image height and width using the data object here
maxH = Math.max(maxH, prefSize.height + imgH);
maxW = Math.max(maxW, prefSize.width + imgW);
}
if(iterator) iterator.moveNext();
}
if(iterator) iterator.seek(bookmark, 0);
return {width: maxW, height: maxH};
}
If possible store the image dimensions in the data object and use those values as imgH and imgW, that will make sizing much easier.
EDIT:
If you are adding elements to the render besides an image, like a label, you will also have to calculate their size as well when you iterate through the data elements and take those dimensions into account when calculating maxH and maxW.

Resources