I don’t know about everyone else, but I think the default component skins in Flex 4 are a big mistake. Well, let me be clear: I know the new Spark components are now easier to customize. And that is great. I am all for improving customization and usability. But some people don’t like skinning the default components! Would it have been so hard to improve the functionality while still making the default skins pretty? Like the Halo skins from Flex 3? One of the best things about Flex 3 is that I could just pop in a bunch of UI elements and it would be a great-looking application right out of the box. With the addition of Spark, I am now forced to customize the look of the components.
So, for starters, I’ve corrected one of the main faults, which is the font. I found that simply changing the default Spark font, it makes a big impact. All you have to do is add the following style code to your application:
1
2
3
4
5
6
7
<fx:Style>@namespace s "library://ns.adobe.com/flex/spark";
s|Application{
font-family: "Verdana";
font-size: 10;
}</fx:Style>
As you can see in the example below, the left UI is the default skin. The right UI is with this minor font change. I think it makes a big difference:
I’m sure there are some other things I can do globally, to make Spark a little better-looking out of the box. The font is just a small step in the right direction.
Another Method
Now, I will say that there’s another method of forcing the compiler to use the Halo skins. I don’t like this method though. For starters, the proper skins don’t show up in Design View. Also there are a few syntax issues with mixing the skins. Having said that, if you want to go that route, you can: Go to your project properties, then go to Flex Compiler, and append this text to the Additional compiler arguments:
If you’ve ever tried to lower the rowHeight property of a DataGrid in Flex, you have probably noticed that if you lower the height too much, your text will get cut-off at the bottom. I’ve tried a number of solutions to correct this problem, but this one seems to be the best.
There are two properties addressed here: leading and paddingBottom.
leading: controls the bottom padding of the DataGrid header only. paddingBottom: controls the bottom padding for all the DataGrid rows, AND the DataGrid header.
Thus, the solution is to lower the value for paddingBottom, and compensate by increasing the leading value.
The rowHeight property of both DataGrid controls are set by the slider at the bottom. The DataGrid on the left has default values for leading (2) and paddingBottom (also 2). Those properties for the DataGrid on the right is controlled by the sliders.
This code will show you how to create zoom functionality on your flex charts, as shown here: (click and drag on the chart to create the zoom window)
Before I get to the code, there’s a couple of notes:
You will notice that the data tips don’t show up, even if you enable showDataTips. This is a known bug, caused by disabling filterData on the chart series. filterData forces the chart to render all data points, even if they’re not shown on the current chart view. This is important, because if those points aren’t rendered, the chart doesn’t draw the lines that extend to those points.
If you’re worried that rendering unnecessary data points will cause performance issues, you can extend your series class and override the updateFilter method. You’ll need to rewrite it so that it excludes/includes the proper data points.
If you’re not worried about the effects of filterData, and you’d like a quick work-around for showing data tips, you’ll still need to extend your series class, but its a bit more simple. You just need to override findDataPoints with some minor adjustments, and add the private function formatDataTip. Here’s a link with the edited class: FilterLineSeries.as I copied the code directly from the LineSeries source, and made some minor adjustments.
[Bindable]privatevar profits:Array;
privatevar dragStart:Point;
privatevar dragEnd:Point;
privatevar zooming:Boolean;
// initializes the data provider with random dataprivatefunction init():void{
profits = newArray({month: 0, profit: 15});
for(var i:int=1; i<40; i++)
profits.push({month: i, profit: Math.round(Math.random()*25-10)+profits[i-1].profit});
}// sets the start point of the zoom windowprivatefunction startDraw(e:MouseEvent):void{
zooming = true;
dragStart = new Point(series1.mouseX, series1.mouseY);
}// draws the zoom window as your mouse movesprivatefunction showDraw(e:MouseEvent):void{if(zooming){
dragEnd = new Point(series1.mouseX, series1.mouseY);
// finds the top-left and bottom-right ponits of the zoom windowvar TL:Point = new Point(); // top-left pointvar BR:Point = new Point(); // bottom-right pointif(dragStart.x< dragEnd.x){
TL.x = dragStart.x;
BR.x = dragEnd.x;
}else{
TL.x = dragEnd.x;
BR.x = dragStart.x;
}if(dragStart.y< dragEnd.y){
TL.y = dragStart.y;
BR.y = dragEnd.y;
}else{
TL.y = dragEnd.y;
BR.y = dragStart.y;
}// prevents the zoom window from going off the canvasif(TL.x<0) TL.x = 0;
if(BR.x> chartCanvas.width-1) BR.x = chartCanvas.width-1;
if(TL.y<0) TL.y = 0;
if(BR.y> chartCanvas.height-1) BR.y = chartCanvas.height-1;
// draw the actual zoom window
chartCanvas.graphics.clear();
chartCanvas.graphics.lineStyle(1, 0x000000, 0.25);
chartCanvas.graphics.beginFill(0xd4e3f0,0.5);
chartCanvas.graphics.drawRect(TL.x, TL.y, BR.x-TL.x, BR.y-TL.y);
chartCanvas.graphics.endFill();
}}// clears the drawing canvas and sets the new max/minsprivatefunction finishDraw(e:MouseEvent):void{
zooming = false;
chartCanvas.clear();
// converts the drag coordinates into axis data pointsvar chartValStart:Array = chartCanvas.localToData(dragStart);
var chartValEnd:Array = chartCanvas.localToData(dragEnd);
// sets the new maximum and minimum for both axes
haxis.minimum = (chartValStart[0]< chartValEnd[0]) ? chartValStart[0] : chartValEnd[0];
haxis.maximum = (chartValStart[0]< chartValEnd[0]) ? chartValEnd[0] : chartValStart[0];
vaxis.minimum = (chartValStart[1]< chartValEnd[1]) ? chartValStart[1] : chartValEnd[1];
vaxis.maximum = (chartValStart[1]< chartValEnd[1]) ? chartValEnd[1] : chartValStart[1];
}// resets the axis max/minsprivatefunction resetZoom():void{
haxis.minimum = NaN;
haxis.maximum = NaN;
vaxis.minimum = NaN;
vaxis.maximum = NaN;
}
I know there seems to be a lot of unnecessary code in the showDraw function, but it serves a good purpose. Your user isn’t always going to start their zoom at the top-left corner and drag down to the bottom-right. The code distinguishes where the corners are, and draws the box accordingly.
EDIT (11/2/2010): Here’s how it would be done if you’re using a DateTimeAxis for your horizontal axis:
main.mxml: