I want to create a progress bar of which a % of it is filled in with a different color based on some variable. For example 33 % would fill 33 % of the progress bar with a different color and then 40 % would likewise, fill 40 % of it. What is the best way to do this in Actionscript and Flex 3?
The way I've done this in the past is to create a custom progress bar skin, then set the fill to be a gradient that goes the entire length of the bar (even though a smaller portion of the bar actually gets drawn.) Sounds strange to use a gradient for something that has hard stops to the colors, but it's actually pretty easy. You set the stop for the next color right next to an end stop for the previous color. Here's an example where the color changes from green to red at the mid point:
package some.package.skins
{
import flash.display.GradientType;
import flash.geom.Matrix;
import mx.core.UIComponent;
import mx.skins.halo.ProgressBarSkin;
public class ColoredProgressBarSkin extends ProgressBarSkin
{
override protected function updateDisplayList(w:Number, h:Number):void {
super.updateDisplayList(w, h);
graphics.clear();
var fullWidth:int = w;
if (parent != null && (parent as UIComponent).mask != null)
fullWidth = (parent as UIComponent).mask.width;
var matrix:Matrix = new Matrix();
matrix.createGradientBox(fullWidth, h);
var colors:Array = [0x00ff00, 0x00ff00, 0xff0000, 0xff0000];
this.graphics.lineStyle();
this.graphics.beginGradientFill(GradientType.LINEAR, colors, [1,1,1,1], [0,128,128,255], matrix);
this.graphics.drawRoundRect(2, 2, w - 4, h - 4, h - 4);
}
}
}
You then set this skin to the barSkin style on your progress bar, either in CSS or in the tag where you use the progress bar.
Hope that helps.
I had to do this a while back and opted to take the following route:
I built a custom preloader by extending the DownloadProgressBar and then integrated it with this Degrafa component. http://degrafa.org/source/CapacityIndicator/CapacityIndicator.html
To create the custom preloader, I think I used this tute:
http://iamjosh.wordpress.com/2007/12/18/flex-custom-preloader/
and then created a separate class that was responsible for dynamically adjusting the values in that degrafa component linked above. And then of course in your SWFDownloadProgress function (on the Progress Event), you can adjust those values accordingly.
I found this to be the quickest and a fairly clean way of doing it :)
Good luck!
Related
I'm making a trivia in an Air application, a question, three buttons, after you choose one, the right button gets coloured green, the wrong ones get coloured red. I was trying to do this changing the styles, so I created a Button.Right and a Button.Wrong style, but I also need to disable the buttons so they don't get clicked more than once while I'm showing the correct answers.
So I'm having trouble making it so the buttons don't look greyish and with the alpha turned down when I set their enabled property to false.
I'm trying to be as minimalistic as possible here, changing disabled-overlay-alpha or disabledOverlayAlpha in the css file doesn't seem to do the trick, neither does changing disabledBorderColor
any fast tricks to do this?
This might be a somewhat dirty workaround, but you could try disabling the buttons by setting their mouseEnabled property to false to forbid them from interacting with the mouse.
I have always used stateful skins to accomplish this. It's relatively easy, but this is how I would do it:
CSS (path changes depending on your assets):
Button {
skin:ClassReference('com.mysite.assets.skins.MyStatefulSkin');
}
Then, in /com/mysite/assets/skins, you would have:
package com.mysite.assets.skins {
import flash.display.GradientType;
import mx.containers.Canvas;
public class MyStatefulSkin extends Canvas {
import flash.display.Graphics;
import flash.geom.Rectangle;
import mx.graphics.GradientEntry;
import mx.graphics.LinearGradient;
public function MyStatefulSkin() {
super();
}
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth,unscaledHeight);
var w:Number = unscaledWidth;
var h:Number = unscaledHeight;
var cr:Number = getStyle('cornerRadius');
var backgroundFillColors:Array;
var g:Graphics = graphics;
g.clear();
switch( name ){
case "upSkin":
backgroundFillColors = [0xffffff,0xdddddd];
break;
case "overSkin":
backgroundFillColors = [0xffffff,0xdddddd];
break;
case "downSkin":
backgroundFillColors = [0xffffff,0xdddddd];
break;
case "disabledSkin":
backgroundFillColors = [0xffffff,0xdddddd];
break;
}
// Draw Background
g.beginGradientFill( GradientType.LINEAR, backgroundFillColors, [1.0,1.0], [0,255], verticalGradientMatrix( 0, 0, w, h ) );
g.drawRoundRectComplex( 0, 0, w, h, cr, cr, cr, cr );
g.endFill();
// Draw other things (borders, icons, etc)
}
}
}
Sometimes I don't use canvas, other times I do. I use things that allow me flexibility inside of the actual asset. However, the idea remains the same, this allows you to set up states for your skin where you can treat things differently depending on the skin.
Personally, I think you should just extend whatever component you use and add states (correctNormal, correctDisabled, wrongNormal, wrongDisable) and just change the state appropriately depending on the properties given to the component. With this, you can specify very easily how everything is suppose to look. Would be even easier if you were using Flex 4.
I have a several chart components that I have created in Flex. Basically I have set up a special UI that allows the user to select which of these charts they want to print. When they press the print button each of the selected charts is created dynamically then added to a container. Then I send this container off to FlexPrintJob.
i.e.
private function prePrint():void
{
var printSelection:Box = new Box();
printSelection.percentHeight = 100;
printSelection.percentWidth = 100;
printSelection.visible = true;
if (this.chkMyChart1.selected)
{
var rptMyChart1:Chart1Panel = new Chart1Panel();
rptMyChart1.percentHeight = 100;
rptMyChart1.percentWidth = 100;
rptMyChart1.visible = true;
printSelection.addChild(rptMyChart1);
}
print(printSelection);
}
private function print(container:Box):void
{
var job:FlexPrintJob;
job = new FlexPrintJob();
if (job.start()) {
job.addObject(container, FlexPrintJobScaleType.MATCH_WIDTH);
job.send();
}
}
This code works fine if the chart is actually displayed somewhere on the page but adding it dynamically as shown above does not. The print dialog will appear but nothing happens when I press OK.
So I really have two questions:
Is it possible to print flex components/charts when they are not visible on the screen?
If so, how do I do it / what am I doing wrong?
UPDATE:
Well, at least one thing wrong is my use of the percentages in the width and height. Using percentages doesn't really make sense when the Box is not contained in another object. Changing the height and width to fixed values actually allows the printing to progress and solves my initial problem.
printSelection.height = 100;
printSelection.width = 100;
But a new problem arises in that instead of seeing my chart, I see a black box instead. I have previously resolved this issue by setting the background colour of the chart to #FFFFFF but this doesn't seem to be working this time.
UPDATE 2:
I have seen some examples on the adobe site that add the container to the application but don't include it in the layout. This looks like the way to go.
i.e.
printSelection.includeInLayout = false;
addChild(printSelection);
Your component has to be on the stage in order to draw its contents, so you should try something like this:
printSelection.visible = false;
application.addChild(printSelection);
printSelection.width = ..;
printSelection.height = ...;
...
then do the printing
i'm not completely sure, but in one of my application I have to print out a complete Tab Navigator and the only method i have found to print it is to automatically scroll the tabnavigator tab in order to show the component on screen when i add them to the printjob.
In this way they are all printed. Before i created the tabnaviagotr scrolling the print (to PDF) result was a file with all the pages of the tab but only the one visible on screen really printed.
I have a question that might seem "basic" but I just cannot figure out how to do it...
I have a box and I'd like to change the borderColor. Till there, nothing special. Just a box.bordercolor = xxxxxx...
BUT, I'd like to have the top and bottom border with one color, and the left and right border with another color... And that's the part where I'm stuck.
Any tips? Suggestions?
Thanks for your help and time! ;)
Regards,
BS_C3
#Senz
Hi!
Unfortunately, I won't be able to share the code without making it "incomprehensible"...
But this is the idea... We have 2 main components: ArrowButton and Navigator.
ArrowButton is a hbox containing a label and an image (this image is the arrow tip and it changes depeding on the state of the ArrowButton).
Navigator is a hbox containing a series of ArrowButton. An ArrowButton overlaps the arrowButton on its right in order to create the pointed end of the button.
And then you just create a whole bunch of functionnalities around these components.
I hope this helps... Do not hesitate if you have some more questions =)
Regards.
I noticed you are asking about the Flex 3 SDK. Skins are a good approach. They have changed somewhat in Flex 4(for the better IMHO). If you are wanting to use the Flex Drawing API, then just extend the Box class into a custom class that would look something like this:
public class MultiColorBorderBox extends Box
{
// You could add getters/setters or constructor parameters to be able to change these values.
private var topColor:uint = 0xFF0000;
private var rightColor:uint = 0x00FF00;
private var bottomColor:uint = 0x0000FF;
private var leftColor:uint = 0xFF00FF;
private var borderWidth:Number = 20;
public function MultiColorBorderBox()
{
super();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// This just ensures you dont have content under your border
this.setStyle("paddingLeft", borderWidth);
this.setStyle("paddingRight", borderWidth);
this.setStyle("paddingTop", borderWidth);
this.setStyle("paddingBottom", borderWidth);
var g:Graphics = this.graphics; // This creates a new Graphics object and sets it to the MultiColorBorderBox graphics object. Since Box (superclass) descends from a Sprite object, it has a graphics object automatically.
g.clear();
g.moveTo(0,0); // Moves the position to the top left corner
g.lineStyle(borderWidth, topColor); // Sets the line style with the width and color
g.lineTo(unscaledWidth, 0); // Draws the top border from top left to top right corners
g.lineStyle(borderWidth, rightColor); // Changes the line style
g.lineTo(unscaledWidth, unscaledHeight); // Draws the line from top right to bottom right
g.lineStyle(borderWidth, bottomColor); //Changes the bottom border style
g.lineTo(0, unscaledHeight); // Draws the line from bottom right to bottom left
g.lineStyle(borderWidth, leftColor); // Changes the border color
g.lineTo(0,0); // Closes the box by drawing from bottom left to top left
}
I'm pretty sure you're going to have to create a borderSkin to accomplish this. I believe these are created in an external program, such as Flash Professional; but more info is in the docs.
I don't think that Flex makes any distinction between top/bottom borders and left/right borders. Creating a skin would certainly be the nifty-slick way to do it. A programmatic way might be to use box.graphics to draw your border by hand. I'd start by trying to override the updateDisplayList() function to draw your border...
I finally did a pretty simple thing.
I guess I wasn't detailed enough regarding the specifications.
The actual aim was to create a navigator with arrow shaped buttons.
Each button had to be highlighted when it was selected. And this http://www.freeimagehosting.net/uploads/bcd0d762d7.jpg is how the navigator looked like.
Each button is actually an HBox containing a Box (with a label) and an Image (for the arrow tip), with a horizontalGap = 0.
I didn't think about adding a glowfilter to the button. So I was trying to just change the colors of the top and bottom part of the Box...
So, the glowfilter in the button worked pretty well.
Sorry for the lack of explanations about the context >_< And thanks for your answers!!
Regards.
Here is the problem. I've created custom RectangularBorder and set it as border skin for TitleWindow. After this manipulation inner content of window is starting at 0,0 point of it. How could I set offset of it?
Just setting top padding does not work because scroll bar still begins from the top of the window after this manipulation.
There are lots of problems with trying to skin panels in Flex 3, and TitleWindow inherits from Panel.
This is the best explanation I've seen. (I'm not the same Glenn referenced in the italics :)).
For programmatic skin it went out pretty simple. One should override function get borderMetrics to do so:
public override function get borderMetrics():EdgeMetrics
{
var borderThickness:Number = getStyle("borderThickness");
var cornerRadius:Number = getStyle("cornerRadius");
var headerHeight:Number = getStyle("headerHeight");
return new EdgeMetrics(
borderThickness,
borderThickness + headerHeight,
borderThickness,
borderThickness);
}
try to use padding-top , padding-left etc as a style for your TitleWindow
I want to make a label that has a tiny title above it, for example so the label say $1,000 with a small retail price or our price above it. I am trying to add the title label to the display list of the main label. I get no error but the title does not show up. I also considered rawChildren but apparently Label has no rawChildren property.
Here is my code:
package
{
import mx.controls.Label;
public class PriceLabel extends StrikeThroughLabel //<-- exntension of label to add strike
{
private var _title:Label;
public function PriceLabel()
{
super();
}
[Bindable]
public function set title(s:String):void
{
if(_title == null)
{
_title = new Label();
addChild(_title);
this.alpha = .2;
}
_title.text = s;
}
public function get title():String
{
var s:String
if(_title != null)
{
s = _title.text;
}
return s;
}
}
}
If you add children to a Flex component that is not a container, then you have to manually manage sizing and positioning of those children. Containers do a lot of that work for you.
Here's what you should do:
Move the creation of your child Label into an override of the createChildren() function.
Set the text property of the child label in an override of the commitProperties() function. Your title getter and setter should save the value in a _title variable to be used later for the assignment in commitProperties(). This is actually important for performance.
Override the measure() function and update measuredWidth and measuredHeight to be the maximum width and height values of the main label and it's child.
Override updateDisplayList() and use setActualSize() on the child Label to set it to the required width and height.
That may seem like a lot of work, but in terms of best practices, that's the way you're supposed to build custom components. The Flex Team at Adobe spent a lot of time maximizing performance, and that's why things happen in several steps like that.
That's how to do it based on what you asked. Personally, I would make a subclass of UIComponent with two Labels or UITextFields as children, each with their own separate property.
By the way, the rawChildren property is only available on containers. It exists so that you can add "chrome" to a container that isn't part of the container's child layout algorithm. For example, Panel has a title bar and a border that aren't affected by the vertical/horizontal/absolute layout options.
Why not create a custom component that contains both labels as its children, instead of trying to throw a child on the Label? That feels cleaner to me, as adding children to build-in components like that doesn't seem right.