Display a message that a Spark List is empty
Sorry, this is probably a really easy to answer question, but how in Flex can I display a message within a Spark List component that states that the List is empty. For example if I have a List showing number of jobs outstanding, if my List is empty then I w开发者_开发百科ant a message displayed across the List stating "there are no jobs to perform".
I'd rather not use an Item Renderer because then it's an Item (the list is not empty) and the item can be selected.
This seems like it should be very trivial and I hope it is.
Thanks in advance, Phil
You could work with an overlay that will only be shown if the dataprovider has no items, like so:
<s:List dataProvider="{dp}" left="0" right="0" top="0" bottom="0" />
<s:Group id="emptyMsgBox" left="0" right="0" top="0" bottom="0" alpha=".7"
visible="{!dp.length}" includeInLayout="{!dp.length}">
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor color="0x000000" />
</s:fill>
</s:Rect>
<s:Label text="no items in list" color="0xffffff"
verticalCenter="0" horizontalCenter="0" />
</s:Group>
You could also use states instead of the binding between 'visible', 'includeInLayout' and 'dp.length'
Edit: if you would like this behavior in all your List components, you can create a custom skin for List, like this:
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Metadata>
[HostComponent("spark.components.List")]
</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<s:Rect left="0" right="0" top="0" bottom="0" id="border">
<s:stroke>
<s:SolidColorStroke id="borderStroke" weight="1"/>
</s:stroke>
</s:Rect>
<s:Rect id="background" left="1" right="1" top="1" bottom="1" >
<s:fill>
<s:SolidColor id="bgFill" color="0xFFFFFF" />
</s:fill>
</s:Rect>
<s:Scroller id="scroller" left="0" top="0" right="0" bottom="0"
minViewportInset="1" hasFocusableChildren="false">
<s:DataGroup id="dataGroup"
itemRenderer="spark.skins.spark.DefaultItemRenderer">
<s:layout>
<s:VerticalLayout gap="0" horizontalAlign="contentJustify"
requestedMinRowCount="5" />
</s:layout>
</s:DataGroup>
</s:Scroller>
<s:Group id="emptyMsgBox" left="0" right="0" top="0" bottom="0" alpha=".7"
visible="{!dataGroup.dataProvider.length}"
includeInLayout="{!dataGroup.dataProvider.length}">
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<s:SolidColor color="0x000000" />
</s:fill>
</s:Rect>
<s:Label text="no items in list" color="0xffffff"
verticalCenter="0" horizontalCenter="0" />
</s:Group>
</s:Skin>
This is a trimmed down version of the skin class, but to summarize: all you do is add that overlay Group to the original Spark ListSkin in exactly the same way as in the previous example. Apply it to all List components with CSS:
s|List {
skinClass: ClassReference("my.custom.skin.ListSkin");
}
You can use the list I've created with required functionality:
package
{
import flash.events.Event;
import mx.collections.IList;
import spark.components.List;
import spark.components.supportClasses.TextBase;
[SkinState("disabledEmpty")]
[SkinState("normalEmpty")]
public class ListWithMessage extends List
{
[SkinPart(required = "false")]
public var emptyMessageLabel:TextBase;
/**
* @inheritDoc
*/
override public function set dataProvider(value:IList):void
{
super.dataProvider = value;
invalidateSkinState();
}
//--------------------------------------
// emptyMessage
//--------------------------------------
private var _emptyMessage:String = "Empty";
public function get emptyMessage():String
{
return _emptyMessage;
}
public function set emptyMessage(value:String):void
{
if (_emptyMessage == value)
return;
_emptyMessage = value;
invalidateProperties();
}
/**
* @inheritDoc
*/
override protected function commitProperties():void
{
super.commitProperties();
if (emptyMessageLabel)
emptyMessageLabel.text = _emptyMessage;
}
/**
* @inheritDoc
*/
override protected function getCurrentSkinState():String
{
var state:String = super.getCurrentSkinState();
if (isListEmpty())
state += "Empty";
return state;
}
/**
* @inheritDoc
*/
override protected function partAdded(partName:String, instance:Object):void
{
super.partAdded(partName, instance);
if (instance == emptyMessageLabel)
emptyMessageLabel.text = _emptyMessage;
}
private function isListEmpty():Boolean
{
return !dataProvider || dataProvider.length == 0;
}
/**
* @inheritDoc
*/
override protected function dataProvider_collectionChangeHandler(event:Event):void
{
super.dataProvider_collectionChangeHandler(event);
invalidateSkinState();
}
}
}
The sample skin for this list:
<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009" minWidth="112"
alpha.disabledGroup="0.5" blendMode="normal">
<fx:Metadata>[HostComponent("ListWithMessage")]</fx:Metadata>
<fx:Script fb:purpose="styling">
/* Define the skin elements that should not be colorized.
For list, the skin itself is colorized but the individual parts are not. */
static private const exclusions:Array = ["scroller", "background"];
/**
* @private
*/
override public function get colorizeExclusions():Array {return exclusions;}
/* Define the content fill items that should be colored by the "contentBackgroundColor" style. */
static private const contentFill:Array = ["bgFill"];
/**
* @private
*/
override public function get contentItems():Array {return contentFill};
/**
* @private
*/
override protected function initializationComplete():void
{
useChromeColor = true;
super.initializationComplete();
}
/**
* @private
*/
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
if (getStyle("borderVisible") == true)
{
border.visible = true;
background.left = background.top = background.right = background.bottom = 1;
scroller.minViewportInset = 1;
}
else
{
border.visible = false;
background.left = background.top = background.right = background.bottom = 0;
scroller.minViewportInset = 0;
}
borderStroke.color = getStyle("borderColor");
borderStroke.alpha = getStyle("borderAlpha");
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
</fx:Script>
<s:states>
<s:State name="normal" stateGroups="normalGroup" />
<s:State name="disabled" stateGroups="disabledGroup" />
<s:State name="normalEmpty" stateGroups="emptyGroup,normalGroup" />
<s:State name="disabledEmpty" stateGroups="emptyGroup,disabledGroup" />
</s:states>
<fx:Declarations>
<!---
Defines the appearance of the the List's drop indicator.
To customize the drop indicator appearance, create a custom ListSkin class.
The List's layout takes care to size and position the dropIndicator.
The size of the <code>dropIndicator</code> is typically set to the size of the gaps between the items.
The minimum and maximum settings are typically respected only in the direction
along the major axis (the gap axis). For example a VerticalLayout ignores the
<code>minWidth</code> and <code>maxWidth</code> settings,
but respect <code>minHeight</code> and <code>maxHeight</code>.
@copy spark.components.List#dropIndicator
-->
<fx:Component id="dropIndicator">
<s:Group minWidth="3" minHeight="3" maxWidth="3" maxHeight="3">
<s:Rect left="0" right="0" top="0" bottom="0">
<s:fill>
<!--- Defines the color of the background. -->
<s:SolidColor color="0xBBBBBB" />
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0x868686" weight="1"/>
</s:stroke>
</s:Rect>
</s:Group>
</fx:Component>
</fx:Declarations>
<!-- border -->
<!--- @private -->
<s:Rect left="0" right="0" top="0" bottom="0" id="border">
<s:stroke>
<!--- @private -->
<s:SolidColorStroke id="borderStroke" weight="1"/>
</s:stroke>
</s:Rect>
<!-- fill -->
<!--- Defines the background appearance of the list-based component. -->
<s:Rect id="background" left="1" right="1" top="1" bottom="1" >
<s:fill>
<!--- Defines the color of the background. The default color is 0xFFFFFF. -->
<s:SolidColor id="bgFill" color="0xFFFFFF" />
</s:fill>
</s:Rect>
<!--- The Scroller component to add scroll bars to the list. -->
<s:Scroller left="0" top="0" right="0" bottom="0" id="scroller" minViewportInset="1" hasFocusableChildren="false">
<!--- @copy spark.components.SkinnableDataContainer#dataGroup -->
<s:DataGroup id="dataGroup" itemRenderer="spark.skins.spark.DefaultItemRenderer">
<s:layout>
<!--- The default layout is vertical and measures at least for 5 rows.
When switching to a different layout, HorizontalLayout for example,
make sure to adjust the minWidth, minHeihgt sizes of the skin -->
<s:VerticalLayout gap="0" horizontalAlign="contentJustify" requestedMinRowCount="5" />
</s:layout>
</s:DataGroup>
</s:Scroller>
<s:Label id="emptyMessageLabel" fontSize="16" fontWeight="bold" color="#333333" verticalCenter="0"
horizontalCenter="0" width="100%" textAlign="center" includeIn="emptyGroup" />
</s:SparkSkin>
Usage sample:
<?xml version="1.0" encoding="utf-8"?>
<s:Application minHeight="600" minWidth="955" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:local="*"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
protected function button1_clickHandler(event:MouseEvent):void
{
var item:Object = { label: Math.random() };
if (list.dataProvider)
list.dataProvider.addItem(item);
else
{
var dp:ArrayCollection = new ArrayCollection();
dp.addItem(item);
list.dataProvider = dp;
}
}
]]>
</fx:Script>
<s:VGroup horizontalCenter="0" verticalCenter="0">
<local:ListWithMessage allowMultipleSelection="false" emptyMessage="Empty List" enabled="{listEnabled.selected}"
height="400" id="list" skinClass="ListWithMessageSkin" width="400" />
<s:CheckBox id="listEnabled" selected="true" label="List enabled" />
<s:Button click="button1_clickHandler(event)" label="Add Item" />
<s:Button click="list.dataProvider.removeItemAt(list.selectedIndex)" enabled="{list.selectedItem}"
label="Remove Item" />
</s:VGroup>
</s:Application>
Hope this helps!
精彩评论