2

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

AlBirdie
  • 2,071
  • 1
  • 25
  • 45
  • Show your code, along with an itemRenderer. IS the list being displayed in a popup? Are you testing in an emulator or a device? – JeffryHouser Jul 31 '12 at 13:27
  • Even though there really is no need for any code as this issue is SDK related and not specific to my application, I added the basic code required for people to reproduce the issue above. Naturally, I'm testing this on a device. The issue does NOT occur on the emulator, that's why I didn't notice it either, but my boss playing around with my app on the actual device immediately noticed. Almost forgot, currently testing on an iPad2 with Air 3.3. – AlBirdie Jul 31 '12 at 13:48
  • did you have debugged your application with your device? – Diego Urenia Jul 31 '12 at 14:09
  • @Vyccus, guess! :) "...testing this on a device...", "...issue does NOT occur on the emulator...", "...testing on an iPad2..." ... – AlBirdie Jul 31 '12 at 14:19
  • yeah, but you can debug it running in your ipad2, the output will appear on your computer screen and you can see whether or not something went wrong. – Diego Urenia Jul 31 '12 at 14:43
  • Yes, I'm well aware of that and that's exactly where I'm stuck right now. As I said, the touch events are fired just fine, the selection just doesn't get committed every time, I just can't figure out why. – AlBirdie Jul 31 '12 at 14:51
  • Are you are using a custom skin for the list or scrollbar? – JeffryHouser Jul 31 '12 at 15:05
  • @www.Flextras.com, nope. Just set up a new Flex-Mobile project, don't do anything but add the List (as is) to the HomeView and you'll be able to reproduce the issue. – AlBirdie Jul 31 '12 at 15:13
  • 1
    Al_Birdy - Is there any chance that when you press down you move your finger a little bit (causing a potential press event to turn into a scroll event)? If that's the case then it may be the mouse movement threshold / tolerance in the List classes (or air) has been changed or is too low for the device? HD devices have way more pixels per inch so if the threshold is too small then the slightest movement may be counting as a scroll rather than selection. The fact that the down state is set means the up or rollout event is never received or could be moving off the display objects in the renderer. – 1.21 gigawatts Aug 19 '12 at 06:52
  • @1.21gigawatts, that's my guess as well since it is in fact the default list selection behavior. You press an item and once you scroll the list, the down state of that item is removed. It might well be possible that a move event is received, if it is, the threshold is indeed way too low as I don't intentionally move my finger (same applies for other persons that tested the list behaviour). – AlBirdie Aug 20 '12 at 07:55
  • Update: AFAIK not an mouse move issue. You can move your finger on the device quite a lot before the scroller recognizes it as a scroll event, so everything's fine regarding that matter. Also, not an issue of trying to select items too fast as this happens when you select items very slowly (like pressing the item for a second or two) as well. The issue is that `commitProperties()` isn't fired when the bug occurs, resulting in the fact that the selection isn't changed. – AlBirdie Aug 20 '12 at 11:27
  • Is the fix you posted on the bug report holding up? Otherwise can you try it with 3.1 / 3.2 vs 3.3 and see if it still happens? – 1.21 gigawatts Aug 20 '12 at 14:46
  • What fix? You mean the dirty hack I posted as an answer here? That's not working as it immediately sets the selection on the item you touched, even though you don't want it to be selected (i.e. in case of scrolling). And yes, happens with 3.1/3.2 as well. Nasty bugger this is... ;) – AlBirdie Aug 20 '12 at 15:33
  • add this to the item renderer and see what states are getting set or ignored. override public function setCurrentState(stateName:String, playTransition:Boolean=true):void { labelDisplay.text = stateName; super.setCurrentState(stateName, playTransition); } – 1.21 gigawatts Sep 03 '12 at 21:54
  • Also, add these states: BTW Not sure where to post code than in the comments? – 1.21 gigawatts Sep 03 '12 at 21:56
  • You could just post an answer and put your code in there, makes formatting much easier. I'll test the states in the next few days (as well as the last suggestion Ryan Made in the Jira report). Kinda busy with some other stuff right now but I'll get to it asap. – AlBirdie Sep 04 '12 at 07:03

6 Answers6

4

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.

maurice
  • 41
  • 2
  • Hi, I just posted a very simple patch (one line to change) on Apache Flex , waiting for approval and also a workaround for Adobe Flex SDK 4.5+. I tested the fix on my iPad3: works like a charm. https://issues.apache.org/jira/browse/FLEX-33169 – maurice Jan 18 '13 at 11:01
  • That's great news for flex mobile! However, I've moved to Starling/Feathers in the meantime as I don't see a cross platform solution that doesn't leverage gpu rendering as a viable option for everything but the smallest phone apps any more. Also, using such a big framework kind of limits your options. – AlBirdie Jan 21 '13 at 12:16
1

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

Andrea
  • 326
  • 3
  • 9
  • +1! Thanks for summing up the issue! Nice to see that others are experiencing this issue as well. – AlBirdie Aug 13 '12 at 13:16
  • Just to clarify this, this also happens on Android devices. Really frustrating this is... – AlBirdie Aug 16 '12 at 11:40
  • I've just tried the Flex SDK 4.8. I found that that issue is solved (or, at least, it seems to happen less frequently). – Andrea Sep 04 '12 at 17:39
  • Sorry, I forgot to tell Flex 4.8 with AIR 3.4 SDK ( flash player version 11.4 ). – Andrea Sep 04 '12 at 18:06
  • I'll try that next week. Must be the new Flex SDK then, cause with the old 4.6 SDK the issue remains, even with AIR 3.4. – AlBirdie Sep 05 '12 at 06:03
  • With deeper tests i can confirm that the issue remains. Anyway, it seems it happens less frequently. Flex 4.8 SDK is simply a "legal" release (now it's distributed under Apache license), i think nothing is changed inside. – Andrea Sep 10 '12 at 18:26
1

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.

  • +1! I'll definitely look into the handlers to see what exactly is happening here. Thank you very much for your explanation of the scroller methods! – AlBirdie Aug 22 '12 at 07:02
  • Posted it on the jira as well, the scrolling handlers are not the root of this issue. The `touchInteractionStartHandler()` is not getting called when the bug occurs. – AlBirdie Aug 22 '12 at 09:10
1

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.

Andrea
  • 326
  • 3
  • 9
  • Unfortunately I've got too much other things on my plate right now, so I can't test it in the near future (might be a couple of weeks), that's why I didn't look into the suggestions that have been made on the official bug report, but I'll come back to this issue again once I get the time. For now, you might wanna submit your solution on the bug report as well so that more users see it and maybe test it. – AlBirdie Oct 08 '12 at 08:20
0

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!

AlBirdie
  • 2,071
  • 1
  • 25
  • 45
  • Did a little testing, and turns out that this hack interferes with the index change event(s). This does select an item in the list even if you don't want to, i.e. when you're scrolling. Currently trying to mitigate this issue by checking whether the list is scrolling (`scroller.verticalScrollInProgress`), but it doesn't look too promising to be honest. As usual, totally open for suggestions here. – AlBirdie Aug 01 '12 at 16:49
0

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>
JeffryHouser
  • 39,401
  • 4
  • 38
  • 59
  • Touching on the line between rows doesn't seem to be the issue as the line itself is part of the renderer, hence should be included in the selection area. The iPad doesn't seem to be the issue either, as I tested it on an iPad3 and a Xoom (3.2) as well, same issue. But I came to the same conclusion, that pressing the finger too quickly causes this. Still, I find it rather odd because the list definitely recognizes that an item has been pressed, the selection just doesn't commit. I'll have a look into this with AIR 3.1 and report back. – AlBirdie Aug 07 '12 at 11:47
  • So, tested with 3.1 again, same issue. The down color shortly shows but the selection isn't committed, leaving the existing item selected. Not really sure what to do at this point. Fact is, it shouldn't be like that as the touch is definitely registered by the list, and on the correct item. – AlBirdie Aug 10 '12 at 08:10
  • I don't have an answer for you; as I could not replicate what what you see with the above code. – JeffryHouser Aug 10 '12 at 12:39
  • Well, you said it yourself that sometimes you have the same issue on the iPads and @yannlieb faced it as well. Just rapidly select items in the list and the selection process stops working properly. Even though it might be a little tricky to reproduce and doesn't come up very often when you properly interact with the device, the issue is definitely there, hiding somewhere deep in the `List`/`ListBase` classes. Tested it with other devices, other workstations with different SDKs as well and could reproduce it every single time. Seems to an acceptable issue for most people... :-( – AlBirdie Aug 10 '12 at 13:20
  • I did not say I had the same issue. In situations where nothing was selected on the iPad, there was no visual indication that something was selected. This is different situation than what you're experiencing. – JeffryHouser Aug 10 '12 at 14:47