Reputation: 2071
I'm facing a very serious issue with my implementation of the Spark list in a Flex mobile application (deployed on iOS and Android).
The thing is that the list selection doesn't always work when an item is touched. In fact sometimes the down
state is set, but the item never gets selected, instead the previously selected item remains selected.
Going through the List
and ListBase
classes, I figured out that my lists do receive the mouse/touch event. The List
's item_mouseDownHandler
does get fired and in fact behaves identical for selections that do get committed properly and those that doesn't.
The difference I found so far is that the commitProperties
method (belongs to ListBase
) is never called for non-successful selections, therefore, the List
's commitSelection
isn't called either, resulting in the fact that the selection won't change.
Does anyone have a deeper insight into why this doesn't work? I'd be more than thankful for any help as I don't see me figuring this out myself.
For anyone who wants to reproduce the issue, set up a Flex mobile project and add a list to view with a simply inline renderer and some generic objects, much like the following:
<s:List id="myList" width="100%" height="100%">
<s:dataProvider>
<s:ArrayCollection>
<fx:Object label="Item1" />
.....
<fx:Object label="Item30" />
</s:ArrayCollection>
</s:dataProvider>
</s:List>
Then randomly select the items with your finger on a device and see what happens, items are not selected even though you touched the screen, sometimes the down color shows up.
Edit: Oh and don't get fixated on the 3 outta 4, you've gotta try a little more often than just 4 times to see the issue, but roughly 75% is a good estimate I guess.
Edit2: Since other are obviously experiencing the same issue, I filed a bug report in the Flex Jira: https://issues.apache.org/jira/browse/FLEX-33169
Upvotes: 2
Views: 801
Reputation: 41
I posted what I believe is the real cause of this random problems on Apache Flex JIRA, and thought you might be interested.
Please let me know what you think.
Upvotes: 4
Reputation: 326
This issue is causing me too much frustration: it's serious and it has a very bad effect on the user experience. (for the ones that can't replicate that issue, please, test on iPhone or Android smartphones, because it seems it doesn't happens on iPad). So after tweaking a little bit... I HOPE I have found a solution, but I invite you all to test deeper. You have to use a list extended in this way:
package com.panurge.mobile
{
import flash.events.MouseEvent;
import mx.core.IVisualElement;
import mx.core.mx_internal;
import mx.events.TouchInteractionEvent;
import spark.components.List;
import spark.events.IndexChangeEvent;
import spark.events.RendererExistenceEvent;
public class ListSelectionFix extends List
{
protected var itemMouseDown:Object;
protected var changeEventDispatched:Boolean = false;
use namespace mx_internal;
public function ListSelectionFix()
{
super();
// we need this to cancel previous selection if a scroll action occurs
this.addEventListener(TouchInteractionEvent.TOUCH_INTERACTION_START, onInteractionStart);
// we need this to preventing dispatch change event twice
this.addEventListener(IndexChangeEvent.CHANGE, onIndexChange);
}
protected function onIndexChange(event:IndexChangeEvent):void
{
//trace("onIndexChanging", itemMouseDown, changeEventDispatched);
if (itemMouseDown != null){
if (changeEventDispatched){
changeEventDispatched = false;
event.preventDefault();
}
}
}
override protected function dataGroup_rendererAddHandler(event:RendererExistenceEvent):void
{
super.dataGroup_rendererAddHandler(event);
var renderer:IVisualElement = event.renderer;
if (!renderer)
return;
renderer.addEventListener(MouseEvent.MOUSE_UP, item_mouseUpHandler);
}
override protected function dataGroup_rendererRemoveHandler(event:RendererExistenceEvent):void
{
super.dataGroup_rendererRemoveHandler(event);
var renderer:IVisualElement = event.renderer;
if (!renderer)
return;
renderer.removeEventListener(MouseEvent.MOUSE_UP, item_mouseUpHandler);
}
override protected function item_mouseDownHandler(event:MouseEvent):void
{
itemMouseDown = event.currentTarget;
//trace("item_mouseDownHandler");
super.item_mouseDownHandler(event);
}
protected function onInteractionStart(event:TouchInteractionEvent):void
{
//trace("onInteractionStart");
// set this to null when the scroll is starting again to avoid dispatch on item_mouseUpHandler
itemMouseDown = null;
}
protected function item_mouseUpHandler(event:MouseEvent):void
{
if (itemMouseDown == null || event.isDefaultPrevented())
return;
//trace("item_mouseUpHandler");
// if we are in the same item renderer then we can safely select the item touched
if (event.currentTarget == itemMouseDown){
var _selectedIndex:int = this.dataProvider.getItemIndex(event.currentTarget.data);
setSelectedIndex(_selectedIndex, true);
changeEventDispatched = true;
itemMouseDown.selected = true;
//event.stopImmediatePropagation();
event.preventDefault();
// is dispatch by setSelectedIndex()
//dispatchEvent(new IndexChangeEvent(IndexChangeEvent.CHANGE));
}
}
}
I hope it solves. Let me know.
Upvotes: 1
Reputation: 86
I posted this on the bug that was filed (https://issues.apache.org/jira/browse/FLEX-33169), but I figured I would post this info here as well:
I'm not so sure this is a bug.
On a desktop with a mouse, the list selects the item on mouse down. However, on a device in touch interaction mode, the list selects the item on mouse up. The main reason for this is the user may want to scroll and not actually select the item (for instance, selecting the item could transition to a new screen). Down is different than selected, and an item may be in the down state and then go into the downAndSelected state (and then the selected state) or it may go back into the normal state. The difference between down and selected is subtle, but down is to show the user that they are on top of something that they might select, but selected means they've actually selected it.
On mouse down, List keeps track of what item was clicked on, and if a scroll hasn't been started, then on mouse up, that item will become selected. You can see what's going on by putting breakpoints in List.removeMouseHandlersForDragStart() and List.touchInteractionStartHandler(). If the scrolling is getting triggered, you'll see List.touchInteractionStartHandler() being called.
If that is indeed the issue, there are ways to stop the scroll from happening. A scroll doesn't start immediately, but it waits to make sure the user has moved their finger a certain amount before deciding it is actually indeed a scroll. That logic is in TouchScrollHelper.sbRoot_mouseMoveHandler(). The amount needed to scroll can be controlled with Scroller.mx_internal::minSlopInches. You could also stop all scrolls by calling event.preventDefault() on the "touchInteractionStarting" (TouchInteractionEvent.TOUCH_INTERACTION_STARTING) event.
If that's not the issue, I hope that this will give you some starting points to debug into what's going on.
There used to be a document on the old Adobe wiki describing some of these behaviors (though not in as much code detail), but unfortunately, that seems to be lost. Hopefully someone at Adobe can find the time to resurrect it.
Upvotes: 1
Reputation: 326
i have the same issue. I want to be clear:
I have no solution, but i want to share with you, to emphasize that it's a real serious bug affecting the user experience ( i had also negative reviews on app store for this...).
Ciao
Upvotes: 1
Reputation: 39408
I used the code, shown below, with the 'default' Flex 4.6. I believe Flex 4.6 ships w/ AIR 3.1 natively, not 3.3 as you stated you are using
I could not replicate the described problem. I tested on a Motorola Xoom (Android 3), an HTC Evo 3D (Android 4), an iPad 1st Generation, and an iPad 3rd Generation.
I did notice that sometimes on the iPads, an item would not select if I pressed my finger too quickly or if I touched on the line between list rows; however it was difficult to reproduce this. In these situations the down state was never set; so it is as if the press did not register. I assume this was because my touch was too light in such situations.
My conclusions are one of these things:
The code I used in my testing:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:List id="myList" width="100%" height="100%">
<s:dataProvider>
<s:ArrayCollection>
<fx:Object label="Item1" />
<fx:Object label="Item2" />
<fx:Object label="Item3" />
<fx:Object label="Item4" />
<fx:Object label="Item5" />
<fx:Object label="Item6" />
<fx:Object label="Item7" />
<fx:Object label="Item8" />
<fx:Object label="Item9" />
<fx:Object label="Item10" />
<fx:Object label="Item11" />
<fx:Object label="Item12" />
<fx:Object label="Item13" />
<fx:Object label="Item14" />
<fx:Object label="Item15" />
<fx:Object label="Item16" />
<fx:Object label="Item17" />
<fx:Object label="Item18" />
<fx:Object label="Item19" />
<fx:Object label="Item20" />
<fx:Object label="Item21" />
<fx:Object label="Item22" />
<fx:Object label="Item23" />
<fx:Object label="Item24" />
<fx:Object label="Item25" />
<fx:Object label="Item26" />
<fx:Object label="Item27" />
<fx:Object label="Item28" />
<fx:Object label="Item29" />
<fx:Object label="Item30" />
</s:ArrayCollection>
</s:dataProvider>
</s:List>
</s:Application>
Upvotes: 0
Reputation: 2071
Alright, preliminary solution (although I consider this more a hack since I don't know yet if it doesn't cause other issues) is as follows;
Subclass the spark list, override the item_mouseDownHandler
and specifically set the selected index.
override protected function item_mouseDownHandler(event:MouseEvent):void {
setSelectedIndex((event.currentTarget as IItemRenderer).itemIndex);
}
Again, that does work on first sight, but requires testing of course. Will keep you guys updated!
Upvotes: 0