方法其实很简单,就是自己写代码判断用户选的开始的位置和结束的位置,然后把中间的都选中就OK了。但是做起来,确实还是需要小心些的。
简单来说,这个DEMO中有3个List,但是用户会感觉在操作一个List,尤其是在多选的时候。
支持SHIFT选一段,CTRL一个一个选,以及CTRL+A选全部。
不废话,上代码和图~
SelectItemVO.as
================
package
{
// this class is used to set the data of first selection and second selection
public class SelectItemVO
{
public function SelectItemVO()
{
}
private var _listID:String="";
private var _itemIndex:int=-1;
// used to get/set item index
public function get itemIndex():int
{
return _itemIndex;
}
public function set itemIndex(value:int):void
{
_itemIndex=value;
}
// used to get/set list id
public function get listID():String
{
return _listID;
}
public function set listID(value:String):void
{
_listID=value;
}
// used to set value
public function setValue(id:String, index:int):void
{
_listID=id;
_itemIndex=index;
}
public function clean():void
{
_listID="";
_itemIndex=-1;
}
}
}
ListDemo.mxml
===========
<?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="955"
creationComplete="init(event)"
fontFamily="Verdana"
minHeight="600">
<fx:Script>
<![CDATA[
import mx.core.FlexGlobals;
import mx.events.FlexEvent;
import spark.events.IndexChangeEvent;
// avoid hard code, always use const value is a good habit
private const LIST:String="list";
private const LIST1:String="list1";
private const LIST2:String="list2";
private const LIST3:String="list3";
private var isCtrlDown:Boolean=false; // used to save the status of Ctrl key
private var isShiftDown:Boolean=false; // used to save the status of Shift key
private var valueSetFlag:Boolean=false; // used to mark wether key down or key up value set to isCtrlDown and isShiftDown already.
private var startItem:SelectItemVO=new SelectItemVO(); // used to save the start selected item, for SHIFT - select
private var endItem:SelectItemVO=new SelectItemVO(); // used to save the end selected item, for SHIFT - select
// initialization
protected function init(event:FlexEvent):void
{
// add global listener to handle key down / key up for CTRL and SHIFT
FlexGlobals.topLevelApplication.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
FlexGlobals.topLevelApplication.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
// add listener to lists for CTRL+A event handling
list1.addEventListener(Event.SELECT_ALL, selectAll);
list2.addEventListener(Event.SELECT_ALL, selectAll);
list3.addEventListener(Event.SELECT_ALL, selectAll);
}
// method to set list selection from start to end
private function selectItemFromTo(list:List, start:int, end:int):void
{
var selectList:Vector.<int>=new Vector.<int>();
for (var i:int=start; i < end; i++)
{
selectList.push(i);
}
list.selectedIndices=selectList;
}
// method to set list selection from start to a specified index
private function selectFromStart(list:List, index:int):void
{
// here, add one because the array is from 0
// we need to add one to make sure the for-loop will run enough times to select the item you clicked
selectItemFromTo(list, 0, index + 1);
}
// method to set list selection from a specified index to end
private function selectToEnd(list:List, index:int):void
{
selectItemFromTo(list, index, list.dataProvider.length);
}
// method used to handle CTRL+A
private function selectAll(event:Event):void
{
selectAllItem(list1);
selectAllItem(list2);
selectAllItem(list3);
}
// method set all items selected for a list
private function selectAllItem(list:List):void
{
selectItemFromTo(list, 0, list.dataProvider.length);
}
// 1. when single click, clean other lists
// 2. when SHIFT - select, clean other lists which will not in the selection range
private function cleanSelection(list:List, doAll:Boolean=false):void
{
switch (list.id)
{
case LIST1:
if (doAll)
{
list2.selectedIndex=-1;
list3.selectedIndex=-1;
}
if (doAll || (startItem.listID != LIST2 && endItem.listID != LIST2))
list2.selectedIndices=new Vector.<int>();
if (doAll || (startItem.listID != LIST3 && endItem.listID != LIST3))
list3.selectedIndices=new Vector.<int>();
break;
case LIST2:
if (doAll)
{
list1.selectedIndex=-1;
list3.selectedIndex=-1;
}
if (doAll || (startItem.listID != LIST1 && endItem.listID != LIST1))
list1.selectedIndices=new Vector.<int>();
if (doAll || (startItem.listID != LIST3 && endItem.listID != LIST3))
list3.selectedIndices=new Vector.<int>();
break;
case LIST3:
if (doAll)
{
list1.selectedIndex=-1;
list2.selectedIndex=-1;
}
if (doAll || (startItem.listID != LIST1 && endItem.listID != LIST1))
list1.selectedIndices=new Vector.<int>();
if (doAll || (startItem.listID != LIST2 && endItem.listID != LIST2))
list2.selectedIndices=new Vector.<int>();
break;
default:
break;
}
}
// handle user's clicking on list item (when list's select item is changing)
protected function changingHandler(event:IndexChangeEvent):void
{
var list:List=event.target as List;
if (isShiftDown) // handle SHIFT - select first
{
handleSHIFTSelect(list);
}
else if (isCtrlDown) // handle CTRL - select second
{
handleCTRLSelect(list);
}
else // handle single clicking, it's normal way in using
{
handleSingleClick(list);
}
}
// method to handle SHIFT - select
private function handleSHIFTSelect(list:List):void
{
// when handle the SHIFT - select, get end item ( second selection of range ) first
endItem.setValue(list.id, list.selectedIndex);
cleanSelection(list);
// No matter select the range in UP-DOWN way or DOWN-UP way, here, change the select style to UP-DOWN
var usedInToEndMethod:int=parseNameToID(startItem.listID) <= parseNameToID(endItem.listID) ? startItem.itemIndex : endItem.itemIndex;
var usedInFromStartMethod:int=parseNameToID(startItem.listID) > parseNameToID(endItem.listID) ? startItem.itemIndex : endItem.itemIndex;
// when user selects a range in same list
if (startItem.listID == endItem.listID)
{
var start:int=usedInToEndMethod < usedInFromStartMethod ? usedInToEndMethod : usedInFromStartMethod;
var end:int=usedInToEndMethod > usedInFromStartMethod ? usedInToEndMethod : usedInFromStartMethod;
selectItemFromTo(list, start, end);
}
// when user selects a range between list 1 and list 2
else if ((startItem.listID == LIST1 && endItem.listID == LIST2) || (startItem.listID == LIST2 && endItem.listID == LIST1))
{
selectFromStart(list2, usedInFromStartMethod);
selectToEnd(list1, usedInToEndMethod);
}
// when user selects a range between list 1 and list 2
else if ((startItem.listID == LIST1 && endItem.listID == LIST3) || (startItem.listID == LIST3 && endItem.listID == LIST1))
{
selectFromStart(list3, usedInFromStartMethod);
selectAllItem(list2);
selectToEnd(list1, usedInToEndMethod);
}
// when user selects a range between list 2 and list 3
else if ((startItem.listID == LIST2 && endItem.listID == LIST3) || (startItem.listID == LIST3 && endItem.listID == LIST2))
{
selectFromStart(list3, usedInFromStartMethod);
selectToEnd(list2, usedInToEndMethod);
}
else
{
// If you have other lists, terrible, try to handle all cases user selects a range from one list to another one... *_*
// So, this is not a good Demo, but anyway, it works~ ^_^
}
}
// used to get list index from a string, actually, if your list is not named in a indexable (XXX1, XXX2 ...) style
// you may need to find a way to make sure which list is first, which one is second and so on.
private function parseNameToID(value:String):Number
{
return Number(value.replace(LIST, ""));
}
// method to handle CTRL - select
private function handleCTRLSelect(list:List):void
{
// do nothing, List will support CTRL - select by default
// but need to set the start item ( first selection of range ), for the using in SHIFT - select
startItem.setValue(list.id, list.selectedIndex);
}
// method to handle single click on list
private function handleSingleClick(list:List):void
{
startItem.clean();
endItem.clean(); // clean end item to make sure all other lists will be cleaned in following method
cleanSelection(list, true);
// set the start item ( first selection of range ), for the using in SHIFT - select
startItem.setValue(list.id, list.selectedIndex);
}
// handle key down
protected override function keyDownHandler(event:KeyboardEvent):void
{
if (!valueSetFlag) // just set the value once when key down, actually, could be removed, this flag
{
isCtrlDown=event.ctrlKey;
isShiftDown=event.shiftKey;
valueSetFlag=true;
}
}
// handle key up
protected override function keyUpHandler(event:KeyboardEvent):void
{
if (valueSetFlag)
{
isCtrlDown=event.ctrlKey;
isShiftDown=event.shiftKey;
valueSetFlag=false;
}
}
]]>
</fx:Script>
<s:VGroup width="80%"
height="100%"
horizontalCenter="0"
verticalCenter="0"
horizontalAlign="center"
verticalAlign="middle">
<s:Label text="List 1"
fontSize="16"
fontWeight="bold"
width="70%"/>
<s:List id="list1"
width="70%"
allowMultipleSelection="true"
contentBackgroundColor="#72D9F7"
contentBackgroundAlpha="0.75"
changing="changingHandler(event)"
rollOverColor="#F5F184"
selectionColor="#F75854">
<s:dataProvider>
<s:ArrayCollection>
<fx:String>List 1 - Item 1</fx:String>
<fx:String>List 1 - Item 2</fx:String>
<fx:String>List 1 - Item 3</fx:String>
<fx:String>List 1 - Item 4</fx:String>
<fx:String>List 1 - Item 5</fx:String>
<fx:String>List 1 - Item 6</fx:String>
<fx:String>List 1 - Item 7</fx:String>
</s:ArrayCollection>
</s:dataProvider>
</s:List>
<s:Label text="List 2"
fontSize="16"
fontWeight="bold"
width="70%"/>
<s:List id="list2"
width="70%"
allowMultipleSelection="true"
contentBackgroundColor="#72D9F7"
contentBackgroundAlpha="0.75"
changing="changingHandler(event)"
rollOverColor="#F5F184"
selectionColor="#F75854">
<s:dataProvider>
<s:ArrayCollection>
<fx:String>List 2 - Item 1</fx:String>
<fx:String>List 2 - Item 2</fx:String>
<fx:String>List 2 - Item 3</fx:String>
<fx:String>List 2 - Item 4</fx:String>
<fx:String>List 2 - Item 5</fx:String>
<fx:String>List 2 - Item 6</fx:String>
<fx:String>List 2 - Item 7</fx:String>
</s:ArrayCollection>
</s:dataProvider>
</s:List>
<s:Label text="List 3"
fontSize="16"
fontWeight="bold"
width="70%"/>
<s:List id="list3"
width="70%"
allowMultipleSelection="true"
contentBackgroundColor="#72D9F7"
contentBackgroundAlpha="0.75"
changing="changingHandler(event)"
rollOverColor="#F5F184"
selectionColor="#F75854">
<s:dataProvider>
<s:ArrayCollection>
<fx:String>List 3 - Item 1</fx:String>
<fx:String>List 3 - Item 2</fx:String>
<fx:String>List 3 - Item 3</fx:String>
<fx:String>List 3 - Item 4</fx:String>
<fx:String>List 3 - Item 5</fx:String>
<fx:String>List 3 - Item 6</fx:String>
<fx:String>List 3 - Item 7</fx:String>
</s:ArrayCollection>
</s:dataProvider>
</s:List>
</s:VGroup>
</s:Application>