Drag Scroll - User gesture of touching down and dragging the finger in order to scroll the content
Throw Scroll - User gesture of touching down, swiping the finger and releasing in order to quickly scroll the content
Date Spinner - Mobile component for selecting a date and/or time
List Item Snapping - Interaction behavior in a List where the scroll position always snaps to an item boundary
Selection Overlay - Visual indicator of which item is selected. Usually found in center of component
The SpinnerList component is a specialized List that is typically used for data selection in mobile applications. By default, as the user scrolls through the list items, the items will wrap once the user reaches the end. SpinnerList is commonly used as a numeric stepper component. The DateSpinner component will use a set of SpinnerLists.
The SpinnerList behaves like a spinning cylindrical drum. Users can spin the list using upward or downward throws, dragging it upward or downward and by clicking on an item.
Edge is adding a shopping cart feature to his band's mobile application. He is certain that the band's fans will want more than one copy of their hit song, "One". He adds a SpinnerList to choose the quantity of the cart item. He uses a NumericDataProvider as the dataProvider to more easily specify the allowed quantities.
Bono is upset that wrong versions of his lyrics have been published online. He adds a lyrics viewing feature to the band's mobile application. He uses a SpinnerList filled with the songs of a particular album. Selecting a song in the SpinnerList displays the proper lyrics for that particular song.
The SpinnerList component itself subclasses ListBase. It support single selection. The selected item is always in the center of the component and by default, is displayed under the selection overlay. Selection is required. SpinnerList only supports its specialized layout. It does not support using other layouts. It only supports fixed row heights.
SpinnerList itemRenderers do not show over, down, caret or selected states. They only show normal and disabled states. If an item is disabled, that means it is not selectable. The selected item is always under the selection overlay, which is how selection is conveyed. The ItemRenderers inherit basic font style and horizontal alignment styles from the component. SpinnerListItemRenderer subclasses LabelItemRenderer. It overrides the drawing logic to not show any background in different states.
The VerticalSpinnerLayout is a custom layout that arranges items vertically. It is similar to a vertical layout except that it will wrap the beginning items below the last items. For example, if there are 10 items, numbered from 1 to 10, item 10 will be directly above item 1 as you scroll the layout.
VerticalSpinnerLayout subclasses VerticalLayout but does not support all of the functionality in the base class. In the future, some of this functionality might be supported.
SpinnerList supports three types of interactions. Note that when an item snaps, it animates to that new position.
SpinnerList supports a wrap and non-wrap mode. The developer should set the SpinnerList to non-wrap mode when the number of items is fewer than the number of visible rows. The meridiem (AM/PM) spinner of the DateSpinner component is an example of a SpinnerList that is set to the non-wrap mode. In non-wrap mode, when the user scrolls to the end of the list, the component will allow the user to "pull" past the scrolling boundaries. When they release the finger, the component scrolls back to the scrolling boundary. This is the same bounce and pull behavior found in a List that uses touch interaction. If there are not enough items to wrap, SpinnerList will automatically use non-wrap mode.
SpinnerList will use a Scroller to implement both the wrap and non-wrap interaction modes. Scroller will be modified to optionally support a wrap layout like SpinnerList. Scroller will also support snapping an item to a particular location.
DateSpinner requires setting some items to be disabled. For example, the 31st day should be disabled for certain months. A disabled item can not be the selectedItem. It will ignore an item click. If the disabled item is under the selection overlay when the drag scroll or throw scroll is finished, SpinnerList will continue to scroll itself in the same direction until it reaches an enabled item.
SpinnerList will not expose any public way to set an item as disabled. This type of change will require modifications to the IItemRenderer interface, which we will do at a later time. Under the covers, SpinnerItemRenderer will have an "enabled" property. If false, then the item is disabled. DateSpinner will specify an item is disabled via data. A SpinnerItemRenderer is disabled if the "enabled" property of the data object is set to false. Otherwise, it is enabled.
If no items are enabled, this is considered an error condition.
Visually, a SpinnerList is comprised of item renderers and side borders, which denote the boundaries of the drum.
The SpinnerListContainer provides a way to visually group a set of SpinnerLists together. It contains a set of SpinnerList children which it lays out horizontally. The SpinnerListContainer skin is comprised of multiple parts. It contains a cylindrical drum which provides the surface for the item renderers. The drum has top and bottom shadow overlays which provides the illusion of depth. The drum also has a semi-transparent selection overlay located in its vertical center. The final part is a border which frames the drums.
The selection overlay skin part, called selectionIndicator, displays graphics that indicate which row is currently selected.
It is common to see a SpinnerList either by itself or adjacent to other SpinnerLists. The DateSpinner contains a set of SpinnerLists laid out horizontally. In both cases, there is a single frame border. In the case of a DateSpinner, the frame border frames the set of SpinnerLists.
One of the use cases for a SpinnerList is a numeric stepper. A numericStepper component uses minimum, maximum and stepSize to define the set of valid values. The NumericDataProvider class is a convenience class that subclasses EventDispatcher and implements the IList and IPropertyChangeNotifier interfaces. It adds minimum, maximum, and stepSize properties. Each data item is generated on request, it is not stored internally. The NumericDataProvider does not support adding or removing individual items. The stepSize can not have a value of 0.
package spark.components { import spark.components.supportClasses.ListBase; <a href="Exclude%28name%3D%26quot%3BaccentColor%26quot%3B%2C%20kind%3D%26quot%3Bstyle%26quot%3B%29">Exclude(name="accentColor", kind="style")</a> <a href="Exclude%28name%3D%26quot%3BchromeColor%26quot%3B%2C%20kind%3D%26quot%3Bstyle%26quot%3B%29">Exclude(name="chromeColor", kind="style")</a> <a href="Exclude%28name%3D%26quot%3Blayout%26quot%3B%2C%20kind%3D%26quot%3Bproperty%26quot%3B%29">Exclude(name="layout", kind="property")</a> <a href="Exclude%28name%3D%26quot%3BrequireSelection%26quot%3B%2C%20kind%3D%26quot%3Bproperty%26quot%3B%29">Exclude(name="requireSelection", kind="property")</a> <a href="Exclude%28name%3D%26quot%3Bchanging%26quot%3B%2C%20kind%3D%26quot%3Bevent%26quot%3B%29">Exclude(name="changing", kind="event")</a> <a href="Exclude%28name%3D%26quot%3BitemRollOut%26quot%3B%2C%20kind%3D%26quot%3Bevent%26quot%3B%29">Exclude(name="itemRollOut", kind="event")</a> <a href="Exclude%28name%3D%26quot%3BitemRollOver%26quot%3B%2C%20kind%3D%26quot%3Bevent%26quot%3B%29">Exclude(name="itemRollOver", kind="event")</a> /** * The SpinnerList component displays a list of items. The item in the center of the * list is always the selectedItem. By default, the list wraps around. */ public class SpinnerList extends ListBase { public function SpinnerList() { } <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%29">SkinPart(required="false")</a> /** * The optional Scroller used to scroll the SpinnerList. */ public var scroller:Scroller; /** * When true, scrolling past the last element will scroll to the first element. * * @default true */ public function get wrapElements():Boolean; public function set wrapElements(value:Boolean):void; } } package spark.components { <a href="Exclude%28name%3D%26quot%3BbackgroundAlpha%26quot%3B%2C%20kind%3D%26quot%3Bstyle%26quot%3B%29">Exclude(name="backgroundAlpha", kind="style")</a> <a href="Exclude%28name%3D%26quot%3BbackgroundColor%26quot%3B%2C%20kind%3D%26quot%3Bstyle%26quot%3B%29">Exclude(name="backgroundColor", kind="style")</a> /** * Container for one or more SpinnerLists. The SpinnerLists are laid out horizontally and a * frame is placed around the set of lists. */ public class SpinnerListContainer extends SkinnableContainer { public function SpinnerListContainer() { super(); } } } package spark.components { import spark.components.supportClasses.TextBase; /** * Simple item renderer class for SpinnerList. It does not show a down, hovered, caret or selected state */ public class SpinnerListItemRenderer extends LabelItemRenderer { public function SpinnerListItemRenderer() { } } } package spark.skins.mobile { /** * ActionScript-based skin for the SpinnerList in mobile applications. * * @langversion 3.0 * @playerversion AIR 3 * @productversion Flex 4.5.2 */ public class SpinnerListSkin extends MobileSkin { public function SpinnerListSkin() { } /** * Scroller skin part. */ public var scroller:Scroller; /** * DataGroup skin part. */ public var dataGroup:DataGroup; /** * @copy spark.skins.spark.ApplicationSkin#hostComponent */ public var hostComponent:SpinnerList; /** * Pixel size of the border */ protected var borderThickness:int; } } package spark.skins.mobile { /** * ActionScript-based skin for the SpinnerListContainer in mobile applications. */ public class SpinnerListContainerSkin extends MobileSkin { public function SpinnerListContainerSkin() { } /** * Pixel thickness of the border */ protected var borderThickness:Number; /** * Radius of the border corners */ protected var cornerRadius:Number; /** * Height of the selection indicator */ protected var selectionIndicatorHeight:Number; /** * Class for the border part */ protected var borderClass:Class; /** * Class for the selection indicator part */ protected var selectionIndicatorClass:Class; /** * Class for the shadow part */ protected var shadowClass:Class; /** * Border part which includes the background */ protected var border:InteractiveObject; /** * Selection indicator part */ protected var selectionIndicator:InteractiveObject; /** * Shadow part */ protected var shadow:InteractiveObject; /** * Mask for the content group */ protected var contentGroupMask:Sprite; /** * An optional skin part that defines the Group where the content * children get pushed into and laid out. */ public var contentGroup:Group; /** * @copy spark.skins.spark.ApplicationSkin#hostComponent */ public var hostComponent:SpinnerListContainer; } } package spark.collections { /** * Dynamically generates numeric data based on the minimum, maximum and snapInterval properties. */ public class NumericDataProvider extends OnDemandEventDispatcher implements IList { public function NumericDataProvider(target:IEventDispatcher=null) { super(target); } /** * The minimum allowed value */ public function get minimum():Number; public function set minimum(value:Number):void; /** * The maximum allowed value */ public function get maximum():Number; public function set maximum(value:Number):void; /** * The stepSize property controls the valid dynamically generated values. * * If nonzero, valid values are the sum of the <code>minimum</code> and integer multiples * of this property, for all sums that are less than or equal to the <code>maximum</code>. * * <p>For example, if <code>minimum</code> is 10, <code>maximum</code> is 20, and this property is 3, then the * valid values of this RangeList are 10, 13, 16, 19, and 20.</p> * * <p>If the value of this property is zero, then valid values are only constrained * to be between minimum and maximum inclusive.</p> */ public function get stepSize():Number; public function set stepSize(value:Number):void; } }
<?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" initialize="application1_initializeHandler(event)"> <fx:Script> <![CDATA[ import mx.collections.ArrayCollection; import mx.events.FlexEvent; import spark.events.IndexChangeEvent; <a href="Bindable">Bindable</a> public var minutesCollection:ArrayCollection; <a href="Bindable">Bindable</a> public var meridianCollection:ArrayCollection; <a href="Bindable">Bindable</a> public var hoursCollection:ArrayCollection; <a href="Bindable">Bindable</a> public var daysOfWeekCollection:ArrayCollection; protected function application1_initializeHandler(event:FlexEvent):void { daysOfWeekCollection = new ArrayCollection(<a href="%26quot%3BMon%26quot%3B%2C%26quot%3BTue%26quot%3B%2C%26quot%3BWed%26quot%3B%2C%26quot%3BThu%26quot%3B%2C%26quot%3BFri%26quot%3B%2C%26quot%3BSat%26quot%3B%2C%26quot%3BSun%26quot%3B">"Mon","Tue","Wed","Thu","Fri","Sat","Sun"</a>); meridianCollection = new ArrayCollection(<a href="%26quot%3BAM%26quot%3B%2C%20%26quot%3BPM%26quot%3B">"AM", "PM"</a>); } private function minuteToString(value:int):String { if (value < 10) return "0" + String(value); else return String(value); } protected function timeList_changeHandler(event:FlexEvent):void { var day:String = dayOfWeekList.selectedItem; var hour:String = hourList.selectedItem; var min:String = minList.selectedItem; timeDisplay.text = "Selected Time: " + day + " " + hour + ":" + min; } ]]> </fx:Script> <s:SpinnerListContainer left="50" top="50"> <s:SpinnerList id="dayOfWeekList" dataProvider="{daysOfWeekCollection}" change="timeList_changeHandler(event)"/> <s:SpinnerList id="hourList" width="100" change="timeList_changeHandler(event)"> <s:dataProvider> <s:NumericDataProvider minimum="0" maximum="23" stepSize="1"/> </s:dataProvider> </s:SpinnerList> <s:SpinnerList id="minList" width="100" dataProvider="{minutesCollection}" change="timeList_changeHandler(event)"> <s:dataProvider> <s:NumericDataProvider minimum="0" maximum="59" stepSize="1"/> </s:dataProvider> </s:SpinnerList> </s:SpinnerListContainer> <s:Label y="400" x="100" id="timeDisplay" text="Selected Time: "/> </s:Application>
Enter implementation/design details for the feature here. This section may be updated after the spec signs off.
none
This feature will not be skinned for the desktop use case. The desktop use case is limited because the user interaction is not ideal when using a mouse.
Does this feature apply differently to (or require special work for) Windows versus Mac platforms, iOS versus Android versus [...], or require different testing depending on platform?
none
none
none
Describe any accessibility considerations.
Describe any performance considerations or requirements.
None
None
Wiki: DateSpinner
Wiki: Flex 4.6
Wiki: Item Snapping
Wiki: SpinnerList