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()" />