Improved HSLider component for Flex (part 2)

An improved HSlider (slider) component for Flex (part 2)
I will show you how to code a slider component in a way that is usable. Here is an example of a HSlider out of the box:

Here is an example of my modified HSlider.



Compare the way the thumbs interact when they meet. In the first example, the two thumbs cannot meet or both pick the same value when the thumboverlap value is false. When the thumbOverlap value is true, then both thumbs can select a single value, however the upper slider can now move below the lower slider giving an undesirable effect. In this article, I will show how to create a two thumb slider where the two thumbs can meet at a single point, yet not overlap any further.

Look at the SliderEvent Class for Public Properties. This will describe the properties of the SliderEvent class. We will use these docs to determine what values are available when the slider is moved and how to reference them. For Flex 2 here is the link to the SliderEvent Class:
http://livedocs.adobe.com/flex/201/langref/mx/events/SliderEvent.html

The properties that look interesting are ‘thumbIndex’, (0 or 1) indicating which slider is moving, and ‘value’ – the new value of the thumb that is moving. Notice that only one of the values is available here – but that’s ok, only one thumb slider is changing at a time. We’ll be using dollar amounts for values and I’ll show how to format the amounts to include dollar signs and proper formatting with commas and two decimal places. Let’s import the CurrencyFormatter so we can use it, declare variables for the PriceMin and PriceMax, and declare a priceFormatter. We’ll use the default
Settings (currencySymbol=”$”, UseThousandsSeparator=”true”, etc.) of the CurrencyFormatter, so no parameters are needed. It’s a good idea to look at the help documentation to see what options are available for CurrencyFormatter.

import mx.formatters.CurrencyFormatter;
[Bindable] public var PriceMin:uint = 0;
[Bindable] public var PriceMax:uint = 25000;
public var priceFormatter:CurrencyFormatter = new CurrencyFormatter();

Next, let’s create the HSlider and some labels. Setting allowThumbOverlap to true will allow the two thumbs to meet, and we’ll add some code to prevent them from overlapping. Setting the snapInterval to 1000, allows the slider to move in increments of 1000. Setting the allowTrackClick to true allows you to click on the track and the closest slider will move to the value under the mouse. Setting liveDragging to true allows the values to update immediately rather than waiting for the thumbrelease.

<mx:Label fontWeight="bold" text="Price"/>
<mx:HSlider id="hsPrice" allowThumbOverlap="true" thumbCount="2" values="{[PriceMin,PriceMax]}"
labels="{[priceFormatter.format(PriceMin), priceFormatter.format(PriceMax)]}"
snapInterval="1000" tickInterval="1000" allowTrackClick="true" liveDragging="true"
width="250" dataTipFormatFunction="priceDataTip"
minimum="{Number(PriceMin)}" maximum="{Number(PriceMax)}" change="priceSliderUpdate(event)" />
<mx:Label id="hsPriceLabel" text="" />

When the HSlider is updated, the priceSliderUpdate function gets notified with an event. Look at the help file for the Class SliderEvent, Look here for values that we can use. There are a lot of choices here, the only two items that I find usefull are thumbIndex and value. ‘thumbIndex’ is the zero-based index of the thumb whose position has changed and ‘value’ is the new value of the slider.
We’ll use these properties of the event to prevent the thumbs from overlapping.
First lets check to see which slider is moving, if it’s the left slider (thumbindex=0) then we need to check to see if it’s greater than the upper slider. If it is, then push the right slider along with it by setting the value of the upper slider equal to the lower slider. If the right slider is being updated, then check to see if the upper slider (hsPrice.values[1] ) is less than the lower slider (hsPrice.values[0]), and adjust accordingly (hsPrice.values[1] = hsPrice.values[0]).
Next, set the label below the HSlider to display the minimum and maximum values.

hsPriceLabel.text = "Min: " + priceFormatter.format(hsPrice.values[0])+ " Max: " + priceFormatter.format(hsPrice.values[1]);

Here’s the full code for this function:

private function priceSliderUpdate(evt:SliderEvent):void {
if (evt.thumbIndex == 0) { // the left slider is moving
if (hsPrice.values[0] > hsPrice.values[1]){
hsPrice.values[1] = hsPrice.values[0];
}
} else { // right slider is moving
// if the upper slider moves below the lower slider adjust the lower slider down
if (hsPrice.values[1] < hsPrice.values[0]){
hsPrice.values[0] = hsPrice.values[1];
}
}
hsPriceLabel.text = "Min: " + priceFormatter.format(hsPrice.values[0])+ " Max: " + priceFormatter.format(hsPrice.values[1]);
}

The only thing left is to code the dataTip for the sliders. A dataTip is a little bubble that appears above a slider when it is active. For the dataTip function, the parameter passed in is the value for the slider that moved. How do we know which slider this datatip is for?? If the value passed in is greater than the minimum value, then it must be for the slider of the maximum value. Let’s return a label with a formatted value. If it’s the maximum slider return (“Maximum: ” + priceFormatter.format(item) ), if it’s the minimum slider return: (“Minimum: ” + priceFormatter.format(item) ), if the two sliders are equal, just return the formatted number without a minimum or maximum label: priceFormatter.format(item).
Here is the full code for the DataTip function:
private function priceDataTip(item:Number):String {
if ( item > hsPrice.values[0]){
return ("Maximum: " + priceFormatter.format(item) ); // Maximum:
} else {
if ( hsPrice.values[0] == hsPrice.values[1]){
return (String(priceFormatter.format(item))); // min = max
}else {
return ("Minimum: " + priceFormatter.format(item)); // Minimum
}
}
}

Here is the complete code for everything:

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.SliderEvent;
import mx.controls.sliderClasses.Slider;
import mx.formatters.CurrencyFormatter;
[Bindable] public var PriceMin:uint = 0;
[Bindable] public var PriceMax:uint = 25000;
public var priceFormatter:CurrencyFormatter = new CurrencyFormatter();
private function priceSliderUpdate(evt:SliderEvent):void {
// if using allowTrackClick="true" then thumbdown or thumbdrag doesn't catch changes
// allowThumbOverlap doesn't allow both sliders to be on the same value
// if the lower slider moves past the upper slider, adjust upper slider
// so that the lower slider doesn't overlap the upper slider
if (evt.thumbIndex == 0) { // the left slider is moving
if (hsPrice.values[0] > hsPrice.values[1]){
hsPrice.values[1] = hsPrice.values[0];
}
} else { // right slider is moving
// if the upper slider moves below the lower slider adjust the lower slider down
if (hsPrice.values[1] < hsPrice.values[0]){
hsPrice.values[0] = hsPrice.values[1];
}
}
hsPriceLabel.text = "Min: " + priceFormatter.format(hsPrice.values[0])+ " Max: " + priceFormatter.format(hsPrice.values[1]);
}
private function priceDataTip(item:Number):String {
// item is the value for the slider that moved
// Set the data tip based on the value of the
// selected thumb - if greater than the minimum value,
// then it's the maximum datatip
if ( item > hsPrice.values[0]){
return ("Maximum: " + priceFormatter.format(item) ); // Maximum:
} else {
if ( hsPrice.values[0] == hsPrice.values[1]){
return (String(priceFormatter.format(item))); // min = max
}else {
return ("Minimum: " + priceFormatter.format(item)); // Minimum
}
}
}
]]>
</mx:Script>
<mx:Label fontWeight="bold" text="Price"/>
<mx:HSlider id="hsPrice" allowThumbOverlap="true" thumbCount="2" values="{[PriceMin,PriceMax]}"
labels="{[priceFormatter.format(PriceMin), priceFormatter.format(PriceMax)]}"
snapInterval="1000" tickInterval="1000" allowTrackClick="true" liveDragging="true"
width="250" dataTipFormatFunction="priceDataTip"
minimum="{Number(PriceMin)}" maximum="{Number(PriceMax)}" change="priceSliderUpdate(event)" />
<mx:Label id="hsPriceLabel" text="" />
</mx:Application>

This entry was posted in Flex. Bookmark the permalink.

One Response to Improved HSLider component for Flex (part 2)

  1. max is the maximum value of the slider. Note that max can be smaller than min to effectively swap the end values of the slider.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>