0
votes

I'm working on a simple auto complete function for my Flex mobile app. For that I've got a CalloutButton that triggers a Callout. The Callout holds some lists from which the user can select items. On item select, the callout gets closed (calloutButton.closeDropDown()).

The very same behavior is done for a TextInput. The user inputs text, the callout opens and according to the entered text, the lists change. Works fine so far. Now, when the user selects an item from any of the lists, the callout closes. Also fine. Now the issue, after the callout is closed, the TextInput automatically regains focus.

On a mobile device this is more than disturbing.

I narrowed this behavior down to the mobile TextInput skin (spark.skins.mobile.TextInputSkin) since a TextInput without this skin class doesn't show this behavior.

Now you might say just use the default skin instead but unfortunately I can't. The default skin has a bug with Android devices that doesn't pass though the enter event. That I could live with since I'n not necessarily dependent on the enter event, however, the spark mobile skin allows be to continue entering text in the TextInput even after the callout has been opened, which is desperately needed as the lists change according to the entered text.

I can't provide any code as the problem has been narrowed down to the skinClass, thus should not be in my own code. Believe me, I tried every nice and not so nice method to prevent the TextInput from getting focus again, but nothing worked.

So, totally stuck here! Hopefully you guys have some ideas on how to solve this.

Edit: Below the steps of my application's behaviour. (to be fair, the workflow inside the callout is a little more complex, I'm working with several lists and a SplitViewNavigator here (thus can't use the Flextras autocomplete), but that doesn't affect the TextInput focus issue I'm facing).

  • Enter text in TextInput
  • On key change a Callout with two Lists is opened.
  • The first List receives results according to the entered text from a webservice
  • User selects item from list
  • Second list receives results according to selection from first list from webservice
  • User selects item on second list
  • Callout closes

This all works fine, except that the TextInput receives focus after the Callout closes. I really don't know what triggers this behavior.

Edit2: Code to further illustrate the issue. Naturally, this is stripped down to the very basics, but it mirrors the behavior of the component and focus.

First the CalloutButton and TextInput that can both control the Callout:

<ui:SearchCallout id="detailSearch"/>

<s:TextInput id="searchInput" skinClass="spark.skins.mobile.TextInputSkin" 
                 enter="historySearch(searchInput.text)" 
                 focusOut="searchFocusOutEvent(event)" 
                 focusIn="searchFocusInEvent(event)"/>

The TextInput's focus handlers don't do anything related to the callout, they just set current states of another component, so I'll leave them out here.

Function historySearch closes the CallOut (I forced it to close as it wouldn't close with the usual closeDropDown()), formats the search text, handles a searchHistory and eventually triggers the search function that passes the formatted search text to the selected component. Here are the parts that matter for this case:

private function historySearch(val:String):void {

            detailSearch.forceClose=true;
            detailSearch.closeDropDown();

            searchEvent(_searchSyms, true);
}

Note: 'val' (the TextInput's text) is trimmed and whatnot, eventually it will result in an array of strings, represented by '_searchSyms'.

Further eventListeners are as follows:

   searchInput.addEventListener(KeyboardEvent.KEY_DOWN, onKeyEvent);
   searchInput.addEventListener(FlexEvent.VALUE_COMMIT, onKeyEvent);
   searchInput.addEventListener(TextOperationEvent.CHANGE, onTextChange); 

KEY_DOWN and VALUE_COMMIT don't have anything to do with the Callout, they are used to handle stuff for the searchHistory, hence I'll leave the onKeyEvent function out here.

onTextChange triggers the string search on the server and closes the Callout in case the TextInput's text is an empty string:

private function onTextChange(event:Event):void {
    if(searchInput.text=="") {
    if(detailSearch.isDropDownOpen) {
         (detailSearch.rightView.activeView as RightView).clearDetailList();
            detailSearch.isCloseable=true;

        }
    }
    _searchManager.getRicsByChar(searchInput.text);
}

Eventually the server will respond and passes an array of responses. The Callout gets opened and it's lists are filled with the responses.

I won't paste all the Callout content's code here as it would be way too much. Basically, the user selects an item from any of the lists, the Callout is forced to close and the search function that passes the value to the component (not yet pasted here, be patient ;), is given the item's value. That basically looks like this (never mind the FlexGlobals stuff, this gets all refactored once the focus issue has been resolved):

var search:String = String(event.currentTarget.selectedItem);
FlexGlobals.topLevelApplication.detailSearch.forceClose=true;
FlexGlobals.topLevelApplication.detailSearch.closeDropDown();
FlexGlobals.topLevelApplication.searchEvent(new Array(search), true);

Allright, now the final step of the whole functionality, the searchEvent. As already said, this function basically only passes the formatted search value to the selected component. This either happened on 'Enter' of the TextInput (as the code above shows), or on selection of an item from one of the Callout's lists.

public function searchEvent(_searchSymbols:Array, setText:Boolean):void {

if(setText) {
    var _searchString:String="";
    for each (var _sym:String in _searchSymbols) {
        _searchString += _sym + ", ";
    }
    searchInput.text = _searchString.substring(0, _searchString.length-2);
 }
 stage.focus=null;
 if(selectedWindowContainer) { 

      // set the array of search items to the selected component here

 selectedWindowContainer.setFocus();

} else 
    trace("[MAIN] no component selected");          
}

And that is basically it. This function is the last step of my search routine, and the selected component (that will get the search items), is getting the focus. Yet, it automatically loses focus again and the TextInput will receive it. I have no idea where and why this happens, and I need to get rid of this behavior asap!

Wow, what a post, anyone still reading this? ;) Well, I hope so.

1
I don't completely understand. Some code, or possibly a screencast would help here. [I can selfishly say to check out the Flextras Mobile AutoComplete, too.. if it does what you need; it'll save you a ton of time compared to building from scratch] - JeffryHouser
I already took a look at your AutoComplete component, however it doesn't provide quite the functionality I need. I'll edit the first post to illustrate the issue a little further. - AlBirdie
I still think a screenshot would help me (and possibly others) understand. l It sounds like you have cascading lists somehow; where one list is populated based on the data selected in the first. What would you like to have Focus after the Callout closes? - JeffryHouser
That is exactly what I have. I've got custom components in my application (multiple ones on one view), that have to be selected after the Callout closes. I already set their focus when my routine (selection of items in the callout) is done, but eventually these components (well, one of them) lose their focus again as the TextInput automatically gets focus somehow. - AlBirdie
I feel like a broken record asking for more information. But, honestly I think code will help here. Without reviewing your code, I can't possible guess why focus is going to one place over the other. - JeffryHouser

1 Answers

0
votes

Well, after hours of testing several ways to prevent the TextInput from automatically gaining focus, I (kinda) solved the issue, although I think it's more a dirty hack than anything else.

Setting the TextInput's focusEnabled to false works fine, even though the focus indication (border around the TextInput) won't work with this any more (an issue I can live with for now).

Still, I'd really like to know what exactly is going on here, especially in the mobile skin class.