flex AutoComplete 重写,继承combobox控件的重写
最终效果如下图:

代码如下:
所有文档结果如下:

AutoComplete.as代码如下:
package org.flashcommander.components
{
?import flash.events.FocusEvent;
?import flash.events.KeyboardEvent;
?import flash.events.MouseEvent;
?import flash.ui.Keyboard;
?import flash.ui.Mouse;
?import flash.ui.MouseCursor;
?
?import mx.collections.ArrayCollection;
?import mx.collections.ListCollectionView;
?import mx.collections.Sort;
?import mx.events.CollectionEvent;
?import mx.events.CollectionEventKind;
?import mx.events.FlexEvent;
?import mx.events.FlexMouseEvent;
?import mx.events.SandboxMouseEvent;
?
?import org.flashcommander.event.CustomEvent;
?
?import spark.components.Group;
?import spark.components.List;
?import spark.components.PopUpAnchor;
?import spark.components.TextInput;
?import spark.components.supportClasses.SkinnableComponent;
?import spark.events.TextOperationEvent;
?
?[Event (name="select",type="org.flashcommander.event.CustomEvent")]
?
?[Event (name="enter",type="mx.events.FlexEvent")]
?
?[Event (name="change",type="spark.events.TextOperationEvent")]
?public class AutoComplete extends SkinnableComponent
?{
??
??public function AutoComplete()
??{
???super();
???this.mouseEnabled = true;
???this.setStyle("skinClass",Class(AutoCompleteSkin));
???this.addEventListener(MouseEvent.MOUSE_OUT,onMouSEOut)
???collection.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChange)
???
??}
??
??public var maxRows:Number = 6;
??public var minChars:Number = 1;
??public var prefixOnly:Boolean = true;
??public var requireSelection:Boolean = false;
??
??[SkinPart(required="true",type="spark.components.Group")]
??public var dropDown:Group;
??[SkinPart(required="true",type="spark.components.PopUpAnchor")]
??public var popUp:PopUpAnchor;
??[SkinPart(required="true",type="spark.components.List")]
??public var list:List;
??[SkinPart(required="true",type="spark.components.TextInput")]
??public var inputTxt:TextInput;
??
??override protected function partAdded(partName:String,instance:Object) : void{
???super.partAdded(partName,instance)
???
???if (instance==inputTxt){
????inputTxt.addEventListener(FocusEvent.FOCUS_OUT,_focusOutHandler)
????inputTxt.addEventListener(FocusEvent.FOCUS_IN,_focusInHandler)
????inputTxt.addEventListener(TextOperationEvent.CHANGE,onChange);
????inputTxt.addEventListener("keyDown",onKeyDown);
????inputTxt.addEventListener(FlexEvent.ENTER,enter)
????inputTxt.text = _text;
???}
???if (instance==list){
????list.dataProvider = collection;
????list.labelField = labelField;
????list.labelFunction = labelFunction
????list.addEventListener(FlexEvent.CREATION_COMPLETE,addClickListener)
????list.focusEnabled = false;
????list.requireSelection = requireSelection
???}
???if (instance==dropDown){
????dropDown.addEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE,mouSEOutsideHandler);?
????dropDown.addEventListener(FlexMouseEvent.MOUSE_WHEEL_OUTSIDE,mouSEOutsideHandler);????
????dropDown.addEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE,mouSEOutsideHandler);
????dropDown.addEventListener(SandboxMouseEvent.MOUSE_WHEEL_SOMEWHERE,mouSEOutsideHandler);
???}
??}
??
??private var collection:ListCollectionView = new ArrayCollection();
??
??public function set dataProvider(value:Object){
???if (value is Array)
????collection = new ArrayCollection(value as Array);
???else if (value is ListCollectionView){
????collection = value as ListCollectionView;
????collection.addEventListener(CollectionEvent.COLLECTION_CHANGE,collectionChange)
???}
???if (list) list.dataProvider = collection;
???
???filterData();
??}
??public function get dataProvider():Object { return collection; }
??
??private function collectionChange(event:CollectionEvent){
???if (event.kind == CollectionEventKind.RESET || event.kind == CollectionEventKind.ADD)
????filterData();
??}
??
??private var _text:String = "";
??
??public function set text(t:String){
???_text = t;
???if (inputTxt) inputTxt.text = t;
??}
??public function get text():String{
???return _text;
??}
??
??private var _labelField : String;
??public function set labelField(field:String) : void?{
???_labelField = field;
???if (list) list.labelField = field
??}
??public function get labelField():String { return _labelField };
??
??public function set labelFunction(func:Function) : void?{
???_labelFunction = func;
???if (list) list.labelFunction = func
??}
??public function get labelFunction() : Function?{ return _labelFunction; }
??
??private var _labelFunction:Function;
??
??public var returnField:String;
??
??public function get selectedItem() : Object?{ return _selectedItem; }
??
??public function set selectedItem(item:Object) {
???_selectedItem = item;
???//inputTxt.text = returnFunction(item);
???text = returnFunction(item)
??}
??
??private var _selectedItem:Object;
??
??public function get selectedIndex() : int { return _selectedIndex; }
??private var _selectedIndex : int = -1;
??
??private function onChange(event:TextOperationEvent){
???_text = inputTxt.text;
???
???filterData()
???
???if (text.length>=minChars) filterData();
???
???dispatchEvent(event);
??}
??
??public function filterData(){
???if (!this.focusManager || this.focusManager.getFocus()!=inputTxt) return;
???
???if (!popUp) return;
???
???collection.filterFunction = filterFunction;
???var customSort:Sort = new Sort();
???customSort.compareFunction = sortFunction
???collection.sort = customSort?
???collection.refresh()
???
???if ((text=="" || collection.length==0) && !forceOpen ){
????popUp.displayPopUp = false
???}
???else {
????popUp.displayPopUp = true
????if (requireSelection)
?????list.selectedIndex = 0;
????else
?????list.selectedIndex = -1;
????list.dataGroup.verticalScrollPosition = 0
????list.dataGroup.horizontalScrollPosition = 0
????list.height = Math.min(maxRows,collection.length) * 22 + 20 ;
????list.validateNow()
????popUp.width = inputTxt.width
???}
??}
??
??// default filter function
??
??public function filterFunction(item:Object):Boolean{
???var label:String = itemToLabel(item).toLowerCase();
???// prefix mode
???if (prefixOnly){
????if (label.search(text.toLowerCase()) == 0)
?????return true;
????else
?????return false
???}
???// infix mode
???else {
????if (label.search(text.toLowerCase()) != -1) return true;
???}
???return false;
??}
??
??public function itemToLabel(item:Object):String
??{
???if (item == null) return "";
???
???if (labelFunction)
????return labelFunction(item);
???else if (labelField && item[labelField])
????return item[labelField];
???else
????return item.toString();
??}
??
??private function returnFunction(item:Object):String{
???if (item == null) return "";
???
???if (returnField)
????return item[returnField];
???else
????return itemToLabel(item);
??}
??
??// default sorting - alphabetically ascending
??
??public var sortFunction:Function = defaultSortFunction;
???
??public function defaultSortFunction(item1:Object,item2:Object,fields:Array=null):int{
???var label1:String = itemToLabel(item1);
???var label2:String = itemToLabel(item2);
???if (label1<label2)
????return -1;
???else if (label1==label2)
????return 0;
???else
????return 1;
???
??}
??
??private function onKeyDown(event: KeyboardEvent) : void{
???
???if (popUp.displayPopUp){
????switch (event.keyCode){
?????case Keyboard.UP:
?????case Keyboard.DOWN:
?????case Keyboard.END:
?????case Keyboard.HOME:
?????case Keyboard.PAGE_UP:
?????case Keyboard.PAGE_DOWN:
??????inputTxt.selectRange(text.length,text.length)
??????list.dispatchEvent(event)
??????break;
?????case Keyboard.ENTER:
??????acceptCompletion();
??????break;
?????case Keyboard.TAB:
??????if (requireSelection)
???????acceptCompletion();
??????else
???????popUp.displayPopUp = false
??????break;
?????case Keyboard.ESCAPE:
??????popUp.displayPopUp = false
??????break;
????}
???}
??}
??
??private function enter(event:FlexEvent){
???if (popUp.displayPopUp && list.selectedIndex>-1) return;
???dispatchEvent(event)
??}
??
??// this is a workaround to reset the Mouse cursor
??
??private function onMouSEOut(event:MouseEvent){
???Mouse.cursor = MouseCursor.AUTO;
??}
??
??public function acceptCompletion() : void
??{
???if (list.selectedIndex >= 0 && collection.length>0)
???{
????
????_selectedIndex = list.selectedIndex
????_selectedItem = collection.getItemAt(_selectedIndex)
????
????text = returnFunction(_selectedItem)
????
????inputTxt.selectRange(inputTxt.text.length,inputTxt.text.length)
????
????var e:CustomEvent = new CustomEvent("select",_selectedItem)
????dispatchEvent(e)
????
???}
???else {
????_selectedIndex = list.selectedIndex = -1
????_selectedItem = null
???}
???
???popUp.displayPopUp = false
???
??}?
??
??public var forceOpen:Boolean = false;
??
??private function _focusInHandler(event:FocusEvent){
???if (forceOpen){
????filterData();
???}
??}
??
??private function _focusOutHandler(event:FocusEvent){
???close(event)
???
???if (collection.length==0){
????_selectedIndex = -1;
????selectedItem = null;
???}
??}
??
??private function mouSEOutsideHandler(event){
???if (event is FlexMouseEvent){
????var e:FlexMouseEvent = event as FlexMouseEvent;
????if (inputTxt.hitTestPoint(e.stageX,e.stageY)) return;
???}
???
???close(event);
??}
??
??private function close(event){
???popUp.displayPopUp = false;
??}
??
??private function addClickListener(event){
???list.dataGroup.addEventListener(MouseEvent.CLICK,listItemClick)
??}
??
??private function listItemClick(event: MouseEvent) : void
??{
???acceptCompletion();
???event.stopPropagation();
??}
??
??override public function set enabled(value:Boolean) : void{
???super.enabled = value;
???if (inputTxt) inputTxt.enabled = value;
??}
??
?}
??
}
AutoCompleteSkin.mxml代码如下:
<?xml version="1.0" encoding="utf-8"?>
<!--- AutoComplete Skin: contains a List wrapped in a PopUpAnchor and a TextInput
-->
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
?xmlns:controls="controls.*"
??? alpha.disabled=".5" xmlns:components="org.flashcommander.components.*" >
??? <!-- host component -->
??? <fx:Metadata>
??? <![CDATA[
??? /**
???? * @copy spark.skins.spark.ApplicationSkin#hostComponent
???? */
??????? [HostComponent("org.flashcommander.components.AutoComplete")]
??? ]]>
??? </fx:Metadata>
??? <s:states>
??????? <s:State name="normal" />
??????? <s:State name="open" />
??????? <s:State name="disabled" />
??? </s:states>
???
??? <!---
??????? The PopUpAnchor control that opens the drop-down list.
??? -->
?<s:PopUpAnchor id="popUp"? displayPopUp="false"
??????? top="0" bottom="0" left="0" right="0"
??popUpWidthMatchesAnchorWidth="true"
??????? popUpPosition="below" >
???????
??????? <s:Group id="dropDown" minHeight="22" width="100%">
???????
???<components:ListAutoComplete id="list" width="100%" minWidth="150"
?????????? borderColor="#b4b4b4" focusAlpha="0" contentBackgroundColor="#FFFFFF" />
???
??????? </s:Group>
??
??? </s:PopUpAnchor>
?
?<s:TextInput id="inputTxt" left="0" right="0" top="0" bottom="0"
???? borderColor="#b4b4b4" focusAlpha="0" contentBackgroundColor="#FFFFFF" />
</s:Skin>
ListAutoComplete.as的代码如下:
package org.flashcommander.components
{
?import flash.events.KeyboardEvent;
?import flash.ui.Keyboard;
?
?import spark.components.List;
?
?/**
? * This list is used in AutoCompleteSkin.
? * keyDownHandler is overridden so that the list can handle keyboard events for navigation.?
? */?
?public class ListAutoComplete extends List
?{
??
??override protected function keyDownHandler(event:KeyboardEvent):void {
???
???super.keyDownHandler(event);
???
???if (!dataProvider || !layout || event.isDefaultPrevented())
????return;
???
???adjustSelectionAndCaretUponNavigation(event);
???
??}
?}
}
CustomEvent的代码如下:
package org.flashcommander.event{
?
?import flash.events.Event;
?/**
? * <P>Custom event class.</P>
? * stores custom data in the <code>data</code> variable.
? */?
?public class CustomEvent extends Event{
??
??public var data:Object;
??public function CustomEvent(type:String,mydata:Object,bubbles:Boolean = false,cancelable:Boolean = false){
???
???super(type,bubbles,cancelable);
???
???data = mydata;
??}
?}
}
SearchItem.mxml的代码如下
<?xml version="1.0" encoding="utf-8"?>
<!--
ADOBE SYSTEMS INCORPORATED
Copyright 2008 Adobe Systems Incorporated
All Rights Reserved.
NOTICE: Adobe permits you to use,modify,and distribute this file
in accordance with the terms of the license agreement accompanying it.
-->
<!--- The default skin class for a Spark DefaultItemRenderer class.?
@langversion 3.0
@playerversion Flash 10
@playerversion AIR 1.5
@productversion Flex 4
-->
<s:ItemRenderer?xmlns:fx="http://ns.adobe.com/mxml/2009"
????xmlns:s="library://ns.adobe.com/flex/spark"
????focusEnabled="false" mouseEnabled="false" mouseChildren="true"
????autoDrawBackground="true">
?
?<s:states>
??<s:State name="normal" />???????????
??<s:State name="hovered" />
??<s:State name="selected" />
??<s:State name="normalAndShowsCaret"/>
??<s:State name="hoveredAndShowsCaret"/>
??<s:State name="selectedAndShowsCaret"/>
?</s:states>
?
?<s:VGroup left="0" right="0"
???? paddingLeft="2" paddingRight="2" paddingTop="2" paddingBottom="2">
??
??<s:Label text="{data.titleNoFormatting}" fontWeight="bold" fontSize="13" width="100%"? />
??
??<s:Label text="{data.url}" textDecoration="underline" color="blue" width="100%" buttonMode="true"
???? click="openPage()"/>
??
?</s:VGroup>
?
?<fx:Script>
??<![CDATA[
???import flash.net.navigateToURL;
???
???function openPage(){
????navigateToURL(new URLRequest(data.url),"blank")
???}
??]]>
?</fx:Script>
</s:ItemRenderer>
?
在主程序中引用的代码如下:
<components:AutoComplete id="geoComplete" width="100" ?????????? forceOpen="false" prefixOnly="false" requireSelection="true" ?????????? change="geoTextChange()" enter="this.geoComplete_selectFun.call()" ?????????? select="this.geoComplete_selectFun.call()" />