I'm implementing an image gallery which presents assets as equally sized boxes that are forming a grid. I thought that I could easily achieve that by using the spark.layouts.TileLayout but unfortunately I have some additional requirements that I'm unable to implement with it.
The general principal should to be to present as many boxes as possible within given space. The entire layout of the application is liquid and depends on the user's screen resolution.
I've started from a basic DataGroup with a TileLayout:
<s:DataGroup id="dgpImages" width="100%" height="100%" top="12" dataProvider="{ list }"
minHeight="0" minWidth="0" clipAndEnableScrolling="true" itemRenderer="LibraryImageRenderer">
<s:layout>
<s:TileLayout orientation="rows" clipAndEnableScrolling="true"
requestedColumnCount="-1" requestedRowCount="-1"
verticalGap="12" horizontalGap="12" useVirtualLayout="true" />
</s:layout>
</s:DataGroup>
I don't know the RequestedColumnCount or RequestedRowCount in advance as they depend on the available space, so the above code layouts all elements from left-to-right and then from top-to-bottom - which is as close as you can get from what I really want to achieve.
The problem
This list of boxes should be cable of rendering fake paging. In reality it means that if the last visible row does not entirely fit the available space it should be moved to the next page.
To give you an example let's imagine that we have a list of 10 images. Each one is 10x10 px but my screen resolution only allows me to fit a grid 35x35 px. This means that one page is only capable of presenting 9 images in form of a 3x3 grid (as 5 px is not enough to present a full image). The 10th image should be then transferred to the second page.
The question
This is obviously not happening automatically with the code that I've pasted above as the TileLayout allows for displaying partially visible rows (in a form of a vertically scrolled list). I was wondering how I could achieve the behavior described above.
If the above description does not sound logical please let me know so that I can adapt it (or include more details).
Any help on this will be highly appreciated!
You need to create a custom layout for this kind of functionality. Good news is I found a tutorial doing almost the exact thing you want to do for ya here:
http://www.adobe.com/devnet/flex/articles/spark_layouts.html
So implement that first. in the updateDisplayList() method, look for this line :
if (x + elementWidth > containerWidth)
when checking for bumping it to the next line, youll check the total height versus the current row height and stop adding elements if it exceeds the boundries. (just break out of the for loop)
Then, all you have to do is page the dataProvider for the group based on the current page.
Related
In general, I haven't yet found a definitive way to get Flex and Spark containers to size themselves relative to their children. I have searched for good documentation for this concept many times but have yet to find anything that sufficiently explains the concepts.
A simple example of something I often want to do is to draw a border uniformly around a set of controls.
The goal here would be to have all UI elements to size themselves automatically.
<s:BorderContainer>
<s:HGroup>
<s:Label text="l1" />
<s:Label text="very long label 2" />
</s:HGroup>
</s:BorderContainer>
I would want the Labels to size themselves based on the text property, and the HGroup should be as big as needed to contain labels and BorderContainer should only just contain that HGroup.
Now this simple example may or may not work as desired, I haven't yet figured out how to determine what the output will look like without trying the code. Does anyone know of a good way to understand this process so I can know deterministically how things will look?
Now the more complicated example that I am working on now.
I have a class that extends the BorderContainer. Within the class I create an HGroup with 5 Labels as children of the HGroup. Then the HGroup is added as a child of the ExtendedBorderContainer (using addElement() method).
Then the new class is used in the application as:
<mx:ViewStack>
<s:NavigatorContent>
<my:ExtendedBorderContainer />
</s:NavigatorContent>
<!-- more NavigatorContents here -->
</mx:ViewStack>
I have tried various [max|min]height/width combinations and I can not get the desired layout, which is very tight borders around elements without extraneous space starting from the deepest Label children all the way up to the NavigatorContent.
Look here:
http://livedocs.adobe.com/flex/3/html/help.html?content=size_position_3.html
It is not clear what your problem is.
Regarding first part of your question: what do you mean saying "may or may not work as desired". This example should work and have layout that you described. Label sizes itself based on text property and so on. What behaviour do you get?
Second part:
If you are hinting that your layout is ok in general but have extra spacing between items then look at the padding and gap properties of containers. Some containers have non zero default padding and gap and you have to override them manualy.
I am using mx:Tree (in Flex 4), and assigned a customised MXTreeItemRenderer for every items. As the TreeItemRenderer contains a list with tileLayout, which means the height of every row is variable. So I have to set the tree's "variableRowHeight to true. When I was testing the tree, everything went well. But when I was using the vertical scroller, I met some problems:
The scroll bar did not move to the position I want. When I was scrolling the content, the scroll bar sometimes scrolled to a unwanted position (e.g. the head of the tree). When there are more rows, the problem is more obvious.
When I was scrolling the tree, the images were flicking all the time. That means, the images are reloading I guess. any help?
Is there anyone who can help me solve the problems? many thx :)
One way that I found helpful was to just wrap the tree in a canvas. I was having the same problem with the tree's scrolling because it would do all sorts of funky things, but when you allow the canvas to handle the scrolling instead of the tree everything works out. I chose this over replacing it with the spark tree just because it was a nice quick fix.
<mx:Canvas height="100%" width="100%" verticalScrollPolicy="on">
<mx:Tree width="100%" height="100%"
click="click_handler(event);"
verticalScrollPolicy="off"
itemRenderer="com.fti.view.itemRenderers.defaultRenderer"
itemClose="{treeSample.height = treeSample.measureHeightOfItems();}"
itemOpen="{treeSample.height = treeSample.measureHeightOfItems();}"
id="treeSample"
variableRowHeight="true"/>
</mx:Canvas>
Be sure and include those itemClose and itemOpen properties so the canvas is able to accurately set the height and scroll properly. One warning is that if you're working with larger trees, this can be a bit heavy and impact performance.
use SparkTree
http://kachurovskiy.com/2010/spark-tree/
there is too much bugs with the current tree.
( whating for Flex 5 to clean out thoose things finally ) :)
I want that the DataGridColumn or AdvancedDataGridColumn would automatically resize it's width so as to fit the content within..
I'm new to flex. I want to implement something like HTML tables.
The Data Rendered is simple Text. Some Rows have little longer Text, in that case I would like to automatically extend the width of DataGridColumn.
Any help to solve this.
Thanks in advance
You've hit upon one of the biggest problems of the DataGrid and AdvancedDataGrid. I absolutely hate how hard it is to get cell content to appear comfortably. For reasons not immediately apparent, narrow field values will appear in very wide cells while wide content and headers will get scrunched.
This is especially true for the first and last columns for some reason.
The only solution I have found is to set the minWidth property on columns, and I have to go through the data first to find the widest outliers in those columns and make sure they fit comfortably. Another solution that helps is to have dummy columns on the left and right that are given widths and minWidths and maxWidths of some very small size, say 5, which seems to allow the real columns in the middle to "breathe" a little better.
<mx:columns>
<mx:DataGridColumn id="leftDummy" width="5" minWidth="5" maxWidth="5"/>
<!-- Your "real" columns here, with minWidth assignments -->
<mx:DataGridColumn id="rightDummy" width="5" minWidth="5" maxWidth="5"/>
</mxcolumns>
Be careful, though. If you set a width on a column it gets interpreted not as a literal value or an actual percentage but as some kind of half-assed proportion. I can only assume that the column-sizing procedures get tired of calculating and settle on some kind of "reasonable" interpretation of column width — which, of course, turns out to be utterly unreasonable much of the time.
At this moment I am so frustrated I am considering going with a 3rd party product, ElfGrid, which solves these issues and more. Look at the documentation, especially the ElfColumnUtils, which have some very handy methods for dealing with these issues. It's also quite fast in the testing I've done.
Here's how I do it, and it works quite well...
Create a bindable variable and give it a starting number (let's say 100), ex:
[Bindable] private var colWidth:int = 100;
Use data binding to size the columns, ex:
mx:DataGridColumn width="{colWidth}"
Add a resize event handler to your data grid, ex:
myDataGrid.addEventListener(ResizeEvent.RESIZE , onDataGridResize);
In the handler listen for the new width of the data grid, take that number and divide it by the amount of columns (if all columns are to be equal), or do some math to create the 'percentage' for the width of the columns, ex:
private function onDataGridResize(event:ResizeEvent):void {
colWidth = myDataGrid.width / numberOfColumns;
}
You can set the width to some fixed percentage if you can guess the possible width.
But i would suggest another optiong :
set the variableRowHeight property of the datagrid to true
set the wordWrap property of the column which will have longer text to true.
By this way your long text will be put in multiple lines and only that row height will be increased. just give it a try. if you dont want this, then i think you may need to write a new class extending datagridColumn.
<mx:DataGrid dataProvider="{testAC}" width="80%" height="100%" variableRowHeight="true">
<mx:columns>
<mx:DataGridColumn dataField="company" wordWrap="true"/> <mx:DataGridColumn dataField="share"/>
<mx:DataGridColumn dataField="expense"/>
</mx:columns>
</mx:DataGrid>
Cheers,
PK
I had the same problem, you should know beforehand the size of your longer string, then assigns the MinWidth that size and have the column resizeable = 'false'.
It worked for me
Say I have this AdvancedDataGrid:
<mx:AdvancedDataGrid id="grid"
height="384"
width="100%"
styleName="aStyleName"
displayItemsExpanded="false"
groupItemRenderer="SomeRenderer"
draggableColumns="false"
defaultLeafIcon="{null}"
folderClosedIcon="{null}"
folderOpenIcon="{null}">
<mx:columns>
<!-- a bunch of AdvancedDatagridColumns -->
</mx:columns>
</mx:AdvancedDataGrid>
which works like a tree, i.e., certain nodes are "collections" that can be collapsed and expanded.
The style for this grid has an alternating-item-color array specified. The array length is 2, meaning rows alternate between light and dark.
The problem is, even with a custom groupItemRenderer, the alternating rows very obnoxiously alternate :) no matter whether the row is an expandable node or a data row. The row immediately beneath any expandable row right now could be either color, and the expandable row gets the color too. My preference is for the light-dark alternation to skip the expandable rows and restart light-dark sequence after each row, but skip the expandable row entirely.
I made the groupItemRenderer take up however many columns are displayed and gave it a gray background, but the row color still displays behind the item renderer, even though the height of the render is set to 100%.
If I haven't lost everybody at this point, does anyone have any ideas about how to do this? Do I have to extend AdvancedDataGrid and override the drawRowBackgrounds method, or is there a simpler solution?
My solution, unsatisfactory as it may be, is to use no alternating rows colors. I would like to have kept this open, because it still doesn't accomplish what I want to do, but someone responded to this question with a question of his own, not an answer to my question, thereby dragging down my accept rate. If someone comes along with a good answer, I will uncheck this response and check that one. Sorry, but I'm not sure what else to do here.
I want my custom button to look like the button sample below.
More specifically, I want the width of the button to be determined by the width of the largest word in the label (i.e. width of "Elongated" in my example). The label must wrap, not truncate.
And I want only one pixel between the top edge of the icon and my button's top edge.
I have tried lots of things but to no avail so far. I have reduced the horizontalGap and verticalGap to zero and padding to zero. But still nothing. Please help.
Here's what I have right now:
<mx:Button top="0" cornerRadius="2" paddingLeft="0" paddingRight="0" leading="0" letterSpacing="0" paddingTop="0" paddingBottom="0" verticalGap="0" horizontalGap="0" textAlign="center" labelPlacement="bottom" icon="{MyIcon}" height="60" width="75" label="Elongated Label" fontSize="10" />
That's not at all simple.
You will need to create your own button,
public class Mybutton extends Button {...}
override createChildren and set the word wrap of the IUITextField used for the label to true.
override measure and use your own line metrics to determine the width that the button should be. If you get the measure right, the button will lay itself out properly.
I don't have a dev environment in front of me at the moment, but something along these lines should work:
Set truncateToFit property of the Label to false (OR use a TextField with wordWrap set to true - I think this should keep the words together as much as possible).
I haven't had the need to use any of the above yet, and hopefully you haven't tried them yet because it would be an easy solution to a part of your problem. Without any code, I'm not sure why padding didn't work, but maybe it's something to do with the word truncation.
As an alternative, why not use a Button, embed or specify a source for your icon and decide where to place the text by specifying the object's labelPlacement property?
EDIT: Since there's no property in Button about wordWrap, as they would recommend in the Adobe Flex forums for such questions regarding sizing based on content where there is no automatic feature to do that, you have to find the longest word and adjust the width of the button (i.e.: in the creationComplete event). Experimenting to find the font to pixel ratio would be my best bet (or you can use a Monospace font where all the characters are given the same space thereby simplifying the estimation):
creationComplete="event.target.width=returnMyWidth();"
As for the padding, it may be related to the custom width that you had set or it may be from embedding the image automatically setting a padding by being included - I'm not really sure, so it would be good if someone can offer a comment based on experience with this.