AlBirdie
AlBirdie

Reputation: 2071

Selection on spark mobile list only works 3 out of 4 times

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

Answers (6)

maurice
maurice

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.

https://issues.apache.org/jira/browse/FLEX-33169?focusedCommentId=13557088&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13557088

Please let me know what you think.

Upvotes: 4

Andrea
Andrea

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

Ryan Frishberg
Ryan Frishberg

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

Andrea
Andrea

Reputation: 326

i have the same issue. I want to be clear:

  • the issue is: selecting the item from list with touch, sometimes the selection is not changed; when this occurs, you should touch 2 or 3 times before you can change the selection;
  • it occurs only on iPad and iPhone devices, NOT in simulator;
  • it's difficult to reproduce, cause it's not easy to understand the situations affected;
  • i can suppose that it happens specially after a scrolling action, and surely with a light touch (but the touch is happened, so the "lightness" is not the problem...)

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

JeffryHouser
JeffryHouser

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:

  1. There is that there is a bug in AIR 3.3 that does not exhibit itself in AIR 3.1
  2. There is something odd happening on an iPad 2 that is not happening on the other devices
  3. There is something wrong with your custom merge of Flex 4.6 and AIR 3.3.

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

AlBirdie
AlBirdie

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

Related Questions