Is there a way to disable the scrollbar / list bounce effect in flex 4.5 (mobile)? - apache-flex

I'm trying to make my own component that behaves like a list and supports infinite scrolling (in 1-dimension : vertical or horizontal) - both directions. For eg, a vertically laid out list which the user can scroll up or down forever - without ever hitting the 'last' or 'first' item. A good use for this: a calendar that displays each month as a list item.
Anyway, there are a bunch of things to overcome. The first of which, I think, is to disable the scrollbar's bounce effects (introduced in the latest Flex 4.5 (mobile) SDK).
If I can disable the bounce effects, I'm guessing I can then add/remove items as needed to the list and it would scroll infinitely.
Any ideas?
Krishna

Personally, an infinite list would mean a lot of rework of the core List component. It's a lot of work to reverse engineer and you'll probably hit a wall. I think what you want to do is create a component from scratch and extends SkinnableContainer.
From here on out, you need to decide how to implement and what's the user interaction for an infinite list, then need to implement proper practices and reuse your item renderers.

From my experience, you can simply implement the lazy loading on the List component by adding property change event to the viewport of the list's dataGroup
list.dataGroup.addEventListener( PropertyChangeEvent.PROPERTY_CHANGE, onScrollPropertyChangeHandler );
Then in the event, listen to the vertical scroll position
if ( event.property == "verticalScrollPosition" ){
var listHeight:Number = itemList.height;
var curAnchorPoint:Number = event.newValue + listHeight;
var bottomPositionToLoad:Number = 200; // Start loading when the list nearly reach the bottom minus 200
var anchorToLoadNextPage:Number = itemList.dataGroup.contentHeight - bottomPositionToLoad;
if(curAnchorPoint >= anchorToLoadNextPage){
loadNextPage();
}
}
When loadNextPage() is running, remember to remove the property change event so the loadNextPage will not be called multiple times.

Related

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
}
}

Jerky mouse movement on BorderContainer

It seem when I trying to drag the bordercontainer to move along x-axis position on mouse down. It appear to move stable but became jerky when I drag a little faster.
Any way to get bordercontainer move with smooth motion?
private function mDownHandler(event:MouseEvent):void {
gMouseX = event.localX;
borderCntr.addEventListener(MouseEvent.MOUSE_MOVE, mMoveHandler);
}
private function mMoveHandler(event:MouseEvent):void {
borderCntr.x = int(event.localX)-gMouseX;
}
are you targeting mobile devices? MouseEvent.MOUSE_MOVE is quite intensive for mobile devices since it calls many more times faster than the framerate. it's not optimized nor recommended to use MouseEvent.MOUSE_MOVE for projects with mobile deployment targets.
instead, create your own moving logic by employing either the stageX and stageY properties or startDrag() and stopDrag() functions with an Event.ENTER_FRAME event.
1) You shouldn't use local coordinates in your mMoveHandler, because it's coordinates of mouse inside borderCntr - it simply couldn't work as you wish to. Use parent's or top level application's mouseX or simply event.stageX.
2) Don't add MouseEvent.MOUSE_MOVE listener to borderCntr. When you will make very quick movement and mouse will walk outside borderCntr, you simply won't recieve move events anymore. Instead listen to parent or stage or top level application (again), the one that would not loose mouse.

Flex : best way to avoid slide transition quirks

I have a custom viewstack that applies automatically a slide in/slide out effect when the view changes. Everything goes smoothly when the views are lightweight but becomes jumpy when the next view requires heavy rendering.
Here are the relevant parts of the code :
_showEffect = new Parallel();
_contentMove = new Move();
_imageMove = new Move();
_showEffect.addChild(_contentMove);
_showEffect.addChild(_imageMove);
_showEffect.addEventListener(EffectEvent.EFFECT_END, effectEndHandler);
I apply the parallel effect to the showEffect of every view :
vis.setStyle("showEffect", _showEffect);
I define the property of the moves depending on the direction of the animation, here to the left (where _views is my resident viewstack in the custom component) :
_contentMove.xFrom = _views.width;
_contentMove.xTo = 0;
_contentMove.yFrom = 0;
_contentMove.yTo = 0;
_imageMove.xFrom = 0;
_imageMove.xTo = -_views.width;
_imageMove.yFrom = 0;
_imageMove.yTo = 0;
_imageMove moves a screen image of the previous view which is set to visible when the animation starts and hidden when it ends.
I have tried some hacks like lengthening the duration of the animation when the next page has never been shown before but this is not quite generic as the required duration to have a smooth transition changes depending on the rendering requirements.
Isn't there any way like in other programming languages to force the layout/rendering process of the next page while it is still out of view ?
Thanks
Viewstack has a creationPolicy property. By default this is set to ContainerCreationPolicy.AUTO, which means the views are rendered as-needed. This explains why the view appears jumpy when switching to it. You can set the creationPolicy property to ContainerCreationPolicy.ALL and it will force the ViewStack to prerender all of its views before they are loaded.
This will solve your problem but note that it will increase overall initialization time of your application because it will now be forced to render everything ahead of time.

Making a Flex DataGrid scroll smoothly

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);
}
}

In Flex, how can I detect when a component moves?

When a Flex component moves directly, so that its x and y properties change, then a "move" event is dispatched on the component. That's all fine.
However, a component may also move as a result of its parent component moving — or its parent moving, and so on. Now when a parent component moves, its children just "move along" without any properties changing or move events being dispatched on the children. Still, the children are moving on the screen. So how can you detect this kind of generalized movement?
One workaround is to capture all move events in the application:
Application.application.addEventListener
(MoveEvent.MOVE, handleMove, true, 0, true);
The third argument is required because move events do not bubble, and so instead have to be captured. The fourth argument is unimportant, and the fifth argument turns on weak references, which is a good idea in this case because we are creating a global reference from Application.application to handleMove — a recipe for memory leaks.
Of course, this will fire too often (once each time anything whatsoever in the application moves), and in a large application could lead to performance problems. If you know that there is some component higher up in the hierarchy that’s sure to stay still, you can put the listener at that point instead of globally, which could reduce the problem.
Still, it would be nice to have a cleaner way to solve this.
Well, you've already suggested the most general solution, but I think it's possible for the child to go through parents/grandparents until it reaches one that is stationary (set by some dynamic property you set), at least this would save you some trouble in figuring out which parent handles which child.
private function addHandlerToStationaryParent(handler:function):void
{
var currentParent:DisplayObjectContainer = parent;
while(currentParent != null)
{
if(currentParent["stationary"] == true)
{
currentParent.addEventListener(MoveEvent.MOVE, handler);
return;
}
}
}
I guess it would be your preference as to whether or not this would be a better solution.

Resources