在Flex 4 List中处理鼠标点击以查找所选项目(因为itemClick已经消失)

3
我为我的问题准备了一个简化的测试案例。如果您将以下2个文件放入项目中,它将立即在您的Flash Builder中运行。
我试图在弹出窗口中显示字符串列表和确认复选框。

screenshot

在真实的应用中,我会使用自定义事件来分发选定的字符串,但在下面的测试代码中,我只是调用trace(str); 我的问题是:如果我使用click事件,那么窗口会关闭,即使我点击滚动条(当前面有项目被选中时,!str检查无法帮助)。而如果我使用change事件,则在我第二次点击相同的项目时,窗口不会关闭。而spark.components.List中似乎没有itemClick事件。
请问如何处理这个经常出现的问题?
为每个项目编写一个自定义项渲染器,并具有单独的click事件处理程序,似乎对于这种情况过于繁琐,因为列表中有字符串。
Test.mxml:(请点击myBtn几次 - 查看我在clickchange方面遇到的问题)
<?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" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    minWidth="400" minHeight="300">

    <fx:Script>
        <![CDATA[
            import mx.managers.PopUpManager;

            private var _popup:Popup = new Popup();

            private function showPopup(event:MouseEvent):void {
                PopUpManager.addPopUp(_popup, this, true);
                PopUpManager.centerPopUp(_popup);
            }
        ]]>
    </fx:Script>

    <s:Button id="myBtn" right="5" bottom="5" 
        label="Open window" click="showPopup(event)" />

</s:Application>

Popup.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx"
    width="240" height="240"
    creationComplete="init(event)"
    close="close()">    

    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayList;
            import mx.controls.Alert;
            import mx.events.FlexEvent;
            import mx.events.CloseEvent;
            import mx.events.ItemClickEvent;
            import mx.managers.PopUpManager;

            private var myData:ArrayList = new ArrayList();

            private function init(event:FlexEvent):void {
                // XXX in the real app data is updated from server
                myData.removeAll();
                for (var i:uint = 1; i <= 10; i++)
                    myData.addItem('Item #' + i);
            }

            public function close(event:TimerEvent=null):void {
                PopUpManager.removePopUp(this);
            }

            private function handleClick(event:MouseEvent):void {
                var str:String = myList.selectedItem as String;
                if (!str)
                    return;

                if (myBox.selected) {
                    Alert.show(
                        'Select ' + str + '?', 
                        null, 
                        mx.controls.Alert.YES | mx.controls.Alert.NO,
                        null,
                        handleConfirm,
                        null,
                        mx.controls.Alert.NO
                    );
                } else {
                    sendEvent();
                }
            }

            private function handleConfirm(event:CloseEvent):void {
                if (event.detail == mx.controls.Alert.YES)
                    sendEvent();
            }

            private function sendEvent():void {
                close();
                // XXX in the real app dispatchEvent() is called
                trace('selected: ' + (myList.selectedItem as String));
            }
        ]]>
    </fx:Script>

    <s:VGroup paddingLeft="20" paddingTop="20" 
            paddingRight="20" paddingBottom="20" gap="20" 
            width="100%" height="100%">
        <s:List id="myList" dataProvider="{myData}" 
            click="handleClick(event)"
            width="100%" height="100%" fontSize="24" />
        <s:CheckBox id="myBox" label="Confirm" />
    </s:VGroup>
</s:TitleWindow>

还有一个问题,为什么会收到上面的警告:

Data binding will not be able to detect assignments to "myData".

2
数据绑定无法检测到赋值,这意味着您需要使用[Bindable]标记声明变量myData。 - Angelo
3个回答

5

Spark List 触发 'IndexChangeEvent.CHANGE' 事件。您可以监听此事件以了解列表中的选择何时更改。

    <s:List id="myList" dataProvider="{myData}" 
            change="handleIndexChange()"
            width="100%" height="100%" fontSize="24" />

只有当所选索引实际更改时,才会触发该事件,这意味着当您第二次重新打开窗口并选择某个项时,可能仍会保留上一次选中的项,而这时如果您再次点击该项,则不会触发CHANGE事件。为了解决这个问题,在关闭窗口之前请先取消所选内容:

        public function close():void {
            myList.selectedIndex = -1;
            PopUpManager.removePopUp(this);
        }

同时确保在关闭窗口(并取消选择)之前使用所选项目分派事件。

至于您关于绑定警告的问题:您收到该消息是因为未将“myData”标记为可绑定。要解决此问题,只需使用[Bindable]标签:

[Bindable]
private var myData:ArrayList = new ArrayList();

如果您不需要绑定,可以完全跳过它并只需在ActionScript中将数据提供者分配给列表:
myList.dataProvider = myData;

谢谢,但正如我在原问题中所写的那样:如果您多次使用myBtn并尝试再次选择相同的项目,则它将无法工作。弹出窗口不会关闭,并且跟踪不会打印(即在实际应用程序中未分派自定义事件)。 - Alexander Farber

2
如果您绝对想要显示选择的项,我建议两种解决方案。否则,RIAStar 提供的解决方案就可以解决问题。
在您的弹出窗口中监听 rendererAdd 和 rendererRemove 事件,如此处所述。这样可以轻松访问列表的渲染器,而不会影响其虚拟布局。
使用自定义渲染器。我知道这听起来比较繁琐,但只要保持代码整洁,itemRenderers 就不会使应用程序的内存暴增。它们是为了渲染大量项目而设计的,而不会造成内存泄漏。

实际上,我在我的真实应用程序中有一个项目渲染器,所以我可以使用它。但是,在其单击事件处理程序内部时,我如何知道所选字符串? - Alexander Farber

-2
在你的Test.mxml文件中,修改代码如下:
  <fx:Script>
            <![CDATA[
                import mx.managers.PopUpManager;

                private var _popup:Popup;

                private function showPopup(event:MouseEvent):void {
                    _popup = new Popup();
                    PopUpManager.addPopUp(_popup, this, true);
                    PopUpManager.centerPopUp(_popup);
                }
            ]]>
        </fx:Script>

在你的Popup.mxml文件中,我不确定为什么将TimerEvent放在close函数中。
此外,由于在点击警告框的“是”按钮后立即调用close()函数,因此trace不会显示。

那不是 Alexander Farber 所问的。在这里,你只是重新初始化了弹出窗口。你失去了关于所选项目的信息。 - LoremIpsum
抱歉,TimerEvent是从真正的应用程序中遗留下来的,我在TitleWindow的标题中显示倒计时。这里并不重要。 - Alexander Farber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接