Making a Flex DataGrid scroll smoothly - apache-flex

I've noticed that the default behaviour for a DataGrid's vertical scroll bar is to scroll one row at a time. This is all well and good when the rows are all uniform and small (e.g. displaying a single line of text), but gets really ugly as soon as you have rows with variable heights.
I'm curious, is there a way to make DataGrid scrolling "smooth"? For instance, is there a way to have the DataGrid scroll by a set number of pixels, lines of text, etc. rather than scrolling one row at a time?
So far, the only solution I've managed to come up with is to place the DataGrid in a Canvas and have the Canvas do the scrolling instead of the DataGrid. The issue with this approach, though, is that as soon as the Canvas scrolls far enough, the DataGrid headers scroll off-screen. Ideally, I'd like to get the smooth-scrolling nature of the Canvas, but also keep the DataGrid headers visible. Is that possible?

The way that ItemRenderer's work in Flex 3 makes smooth scrolling difficult to achieve. Basically Flex recycles item renderers scrolled off of the top of the list as the display objects used for new data at the bottom of the list. Adobe's implementation of most list components in Flex 3 creates and adds these items as they come on to the screen rather than just off the screen, so they "pop in" and smooth scrolling isn't available. I'm not sure why they couldn't have done it in a similar manner for items +/- one position above or below the current scroll pane, but they didn't, and we're stuck with sticky scrolling by default.
Work-arounds do exist, though the one you've noted (dropping the datagrid into a canvas) negates the display-object saving intention of item renderers and incurs a performance cost. This will be fixed for most list-based Flex components in Flex 4, though it won't be fixed immediately for DataGrid. The DataGrid / AdvancedDataGrid component is maintained by a separate team based in India, last time I heard, and so it tends to be a bit behind the rest of the SDK.
I'd recommend trying something similar to this implementation of a smooth-scrolling list by Alex Harui. I'm not sure exactly how well it'd work for DataGrid or AdvancedDataGrid, but this is the most intuitive technique I can think of for making the list scroll correctly.

Try this... It's still based on Alex's code that was mentioned above. His should still be a great start for removing the snap-to-row behavior. Original source:
http://blogs.adobe.com/aharui/2008/03/smooth_scrolling_list.html
Alex's original some code for smooth vertical scrolling but that was not an issue I had with the DataGrid. It was smooth scrolling horizontally that I needed. I am using the DataGrid in an unorthodox manner for analyzing plain text reports output by our database (great way of providing visual feedback on a document). The code below allows content to go off screen and the user can scroll without that snap-to-column behavior.
You can adapt this to use the same math routines for vertical scrolling and then it will make scrolling possible and ignore the snap to row behavior. In particular switch the usage of the listContent.move method to move the contents vertically and use a inverse of the rounded pixel value you calculate from the vertical scroll bar (as opposed to my using the horizontal).
This method is bit simpler than Alex's method from the link above - a lot less code so try adapting and see how it works.
override protected function scrollHandler(event:Event):void
{
// Override the default scroll behavior to provide smooth horizontal scrolling and not the usual "snap-to-column" behavior
var scrEvt:ScrollEvent = event as ScrollEvent;
if(scrEvt.direction == ScrollEventDirection.HORIZONTAL) {
// Get individual components of a scroll bar for measuring and get a horizontal position to use
var scrDownArrow:DisplayObject = horizontalScrollBar.getChildAt(3);
var sctThumb:DisplayObject = horizontalScrollBar.getChildAt(2);
// I replaced maxHorizontalScrollPosition in Alex's code with "1300" to fix my exact application. In other situations you may finding using some property or different value is more appropriate. Don't rely on my choice.
var hPos:Number = Math.round((sctThumb.y - scrDownArrow.height) / (scrDownArrow.y - sctThumb.height - scrDownArrow.height) * 1300);
// Inverse the position to scroll the content to the left for large reports
listContent.move(hPos * -1, listContent.y);
}
// Go ahead and use the default handler for vertical scrolling
else {
super.scrollHandler(event);
}
}

Related

GWT - PopupPanel, change layout when flipped

I'm creating a context menu for certain elements using a PopupPanel; the menu itself is going to be fairly large and complex. What I'm looking to do is to have a list of buttons, plus an image and some text, related to the element clicked.
My problem is that I'd like the buttons to always display directly under the clicked element, because that's convenient for the user; the issue is that when PopupPanel is near the edges of the screen, it automatically changes position to be fully visible, not aligning its left side to the element as usual. I like this behavior, but it moves the position of the buttons away.
So what I'd like to happen is: normally the buttons are on the left of the panel, the other stuff is to the right. When the panel is close to the right of the screen, I'd like the buttons to instead be on the right (and thus under the clicked element) and the other stuff on the left.
Is there a clever way to do this, either in GWT or better yet, using only CSS? PopupPanel itself doesn't seem to tell you when it's going to get flipped, sadly. The only solution I currently have is to manually check the position and width of the popup before showing it and adjust accordingly, but I'm hoping there's a better solution.
Here is what I suggest (based on my own implementation of a similar use case):
Have the position callback implementation accept references (in constructor) on:
PopupPanel element
element on which user right cliked
the content you put in the PopupPanel
Replicate (I know this not DRY but implementation is package private) the code from default position callback
When opening to the right invoke a method that changes the layout of your content (CSS based or otherwise)
I hope it helps. If you find something better let me know.

Flex: preventing tree's vertical scroll bar overlapping view

I have an mx:Tree, but when the vertical scroll bar appears, it overlaps the content of the tree (odd that the horizontal bar does not). That might be acceptable for the text, but the stripe that I create using the item renderer, for certain items, seems to make it an anathema to the QA guys. How can I keep this from happening?
I have an idea for a workaround: I could make use the item renderer to stop the drawing a little bit short of the right side of the view (not that I can reliably get the width of a scroll bar) but I can't even figure out how wide the displayable part of a tree is--all the properties of a tree seem to be about its entire width, which includes the entire area coverable using the horizontal scroll bar. However, the blue stripe signifying a selected item doesn't seem to have that problem--it stops short of the scroll bar. In any case, when trying to find the displayable region, I don't know if I could handle the added complication of when the horizontal scroll bar is moved. Much better if someone could tell me how to put the veritcal bar outside the displayable tree area (or shrink the displayable area, of course). Thanks.
I'm using the Flex 3.5 SDK
I was able to find the solution when researching horizontal scrollbar issue on list and tree component. The blog to which it links eventually shows a kind of hacky solution (in the readers' comments) that shows how to make sure that none of the drawing is done beyond a certain boundary.

dynamic resizing of UICollectionView using intrinsic size within an autolayout

I am trying to implement a calendar using UICollectionView which has a similar scrolling behaviour to the built in calendar app when changing months. At the moment the view is flickering as the resize occurs between months.
I am using a UICollectionViewScrollLayout with a UICollectionView with 10000 indexes in 1 section. The cells are filled by calculating the day for an index. The problem I am having is when trying to resize the view to fit the correct number of weeks for the month, the collectionview doesn't appear to draw in the cells quickly enough as the scrolling and view size change happens. Having slowed the animation down, as the view size changes, it appears that cells are being removed too early from the view. This happens both with a reducing and enlarging the view. Ie, as they are about to scroll off the view they are removed before scrolling out of the view.
The layout is all done using autolayout and there is a fixed view above and then a resizable view below. As the size of the collection view changes the view below changes to fill the space. The only way I seem to have managed to achieve this behavior it is by changing the intrinsic size as per the code below. I have tried changing the frame/bounds but it doesn't seem to work with autolayout. Am I going about the resizing of the view in the right way?
Should I be moving the behaviour to the viewcontroller and change the constraints pinning the height instead.
// Scroll the view to the date given.
-(void) moveViewToDate:(NSDate*) date
{
NSIndexPath *indexPath=[self indexPathForFirstDayInMonth:date];
[self scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionTop animated:YES];
[UIView animateWithDuration:0.25f animations:^{
self.isize=[self weeksInViewForDate:date];
[self invalidateIntrinsicContentSize];
[self.superview layoutIfNeeded];
}];
}
-(CGSize)intrinsicContentSize
{
return CGSizeMake(322,self.isize*46);
}
I finally worked out what was going on. Since I was committing an animation which was changing the size of the view and doing a scroll, the two seperate animations where conflicting with each other.
I eventually rewrote the collection using UIScrollView but had the same problem. I got round it by placing the UIScrollView in a UIView. The UIView was resized using a constraint on the height which was animated.

Flex DataGrid columns won't resize

I am having serious problems trying to resize columns in a DataGrid. I've been at it for over a day now and am at my wit's end with a headache to boot.
Essentially, I have a TabNavigator component with NavigatorContent children inside. In each one of the NavigatorContent children, I have a DataGrid to which I'm setting the width to 100% (this is needed to be able to handle resizing of the browser window). I am using the excellent filterable DataGrid from Iwo Banas as the DataGrid in each tab.
Now, I am making visible/invisible some columns in each of the DataGrids and this is working fine. However, I find that the column widths are not being set correctly. Whenever I set the column widths (using this code), all of the columns seem to be set to the correct width except for the ones that I have recently made visible and the column immediately to the left of these. The ones recently made visible are very small (though I set their width to 30) and the one to the left of these columns is very large (though I've also set its width to 30).
I think it's something to do with the life cycle of the DataGrids because the first DataGrid behaves fine. It's when I click on the other tabs that I find that the widths of the other DataGrids have not been set correctly.
However, if I "see" one of those problem DataGrids (i.e. it appears on the screen) and the code which resizes the columns runs again, the columns are correctly sized.
I have tried a number of things recommended all over the internet including the questions listed below but to no avail.
This is the code I'm using to resize the columns (taken from this answer)
public static function resizeColumn(col:DataGridColumn, size:int):void
{
var owner:* = col.mx_internal::owner
col.mx_internal::owner = null;
col.width = size;
col.mx_internal::owner = owner;
}
Any and all help would be greatly appreciated.
I have already looked at the following answers.
Flex DataGrid column width: one column to rule them all?
Flex 3 DataGrid Column Width Problem
Flex DataGrid Column Width (<-- this answer got me closest)
Unable to change the column width dynamically in flex datagrid
So, after a huge amount of head wrecking, I finally found a solution (at least to my problem, not sure if it would help anyone else but thought I'd post it here anyway as you never know what could help someone else).
I decided that since calling the resize code when the DataGrid is on screen works then I needed to just do that. So I thought of putting the resize code for the relevant DataGrid depending on the tab clicked. Not that straightforward as there didn't seem to be a straightforward way to implement a click handler for the tabs. I did a quick Google and found this solution.
Essentially, you add a click handler for each tab button by cycling through the children of the tab navigator, getting the button and adding an event listener.
Then, you do what you need to do in the handler.
Code example (slightly different to one from website):
protected function tbGridArea_creationCompleteHandler(event:FlexEvent):void
{
for ( var i:int=0; i < tbGridArea.getChildren().length; i++ )
{
var tab:Object = tbGridArea.getTabAt(i);
tab.addEventListener( FlexEvent.BUTTON_DOWN,myTabClickHandler );
}
}
private function myTabClickHandler(event:Event):void {
switch(event.currentTarget.label) {
// do whatever you need to do here
}
}

Qt QHboxLayout cell size ssues

For those of you who haven't been reading my Qt questoins, I am learning Qt for a project. I have only limited experience with GUI design at all, and not in Qt.
I've got a horizontal layout that I want to populate with some buttons. I can feed these buttons in just fine, but my formerly-square buttons are stretched horizontally to take up more space.
I want to let the layout manager determine the best way to size these buttons, but I also want their original proportions to remain intact. For instance, if I start would with 32X32 buttons that need to shrink to fit all of them in the layout, I want them to shrink proportionally so that the width to height scale is maintained. 20X20, 16X16, 12X12 would all be just fine, but 24X16 would be an example of dimensions that are unacceptable.
I've tinkered with size policies on the buttons and stretch options. I'm not seeing, even after reading the QPushButton and QHboxLayout classes how to do this. How is it accomplished?
Thanks.
As long as I understand the question correctly, I think what you want is QBoxLayout::addStretch(). This will add a spacer object that fills the unused space. So the buttons will have their ideal size and the spacer will fill the rest. You can try experimenting with this in Designer, it's easier than the write/compile/run cycle.
You should take a look at the answers to this question. This is a recap of my answer there.
You need to create a custom derivative of QLayoutItem, which overrides bool hasHeightForWidth() and int heightForWidth( int width ) to preserve the aspect ratio. You could either pass the button in and query it, or you could just set the ratio directly. You'll also need to make sure the widget() function returns a pointer to the proper button.
Once that is done, you can add a layout item to a layout in the same manner you would a widget. So when your button gets added, change it to use your custom layout item class.
I haven't actually tested any of this, so it is a theoretical solution at this point. I don't know of any way to do this solution through designer, if that was desired.

Resources