I have a SkinnableContainer, which contains s:BitmapImage. I need a part of the image (Rect) to be fully transparent. Just make a transparent cut-out, that would be easy to position.
The image in the background is width="300" height="200" and transparent area should be width="200" height="20" and right="0" bottom="50". I managed to achieve similar effect with BlendMode.OVERLAY, but I believe there must be a better way.
Any ideas, suggestions, please?
You could apply another object (like a UIMovieClip) to the image as a Mask. You could also manipulate the pixels of a Bitmap and set it as the source of the BitmapImage. This may or may not make sense depending on your specific requirements.
<?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"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="onComplete(event)"
backgroundColor="0xEEEEEE">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.graphics.ImageSnapshot;
import mx.skins.RectangularBorder;
protected function onComplete(event:Event):void
{
var bW:int = 300;
var bH:int = 200;
var X:int = 0;
var Y:int = 150;
var W:int = 200;
var H:int = 20;
var topRect:Rectangle = new Rectangle(0,0,bW,Y);
var righRect:Rectangle = new Rectangle(W,0,bW-W,bH);
var bottomRect:Rectangle = new Rectangle(0,Y+H,bW,bH-(Y+H));
var targetBitmapData:BitmapData = ImageSnapshot.captureBitmapData(original);
var newBitmapData:BitmapData = new BitmapData(bW,bH,true,0x00000000);
newBitmapData.copyPixels(targetBitmapData, topRect, new Point(0, 0),null, null, true);
newBitmapData.copyPixels(targetBitmapData, righRect, new Point(W, 0),null, null, true);
newBitmapData.copyPixels(targetBitmapData, bottomRect, new Point(0,Y+H),null, null, true);
modified.source = new Bitmap(newBitmapData);
}
]]>
</fx:Script>
<s:VGroup gap="10" left="10" top="10">
<s:Group id="original">
<s:Rect width="300" height="200">
<s:fill>
<s:SolidColor/>
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0xFF0000"/>
</s:stroke>
</s:Rect>
</s:Group>
<s:Group>
<mx:Image width="300" height="200" id="modified" />
<s:Rect top="0" left="0" right="0" bottom="0">
<s:stroke>
<s:SolidColorStroke color="0xFF0000"/>
</s:stroke>
</s:Rect>
</s:Group>
</s:VGroup>
</s:Application>
An example for your particular case, maybe you'll extend this.
Related
I want to use datatips in my charts in Flex 4. I know how to use them, but the standard DataTip only shows text. I would like to show text + a button. How would I go on about this?
I thought about extending DataTip in a custom DataTip class and add the button there somewhere like this
public class MyDataTip extends DataTip {
// Override some methods to add the button here?
}
Is this possible / the correct idea? Or do I have to do it differently?
Any code examples for this?
You can use a DataTipRenderer to achieve this. Here is a example of it.
<?xml version="1.0"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
implements="mx.core.IFlexDisplayObject, mx.core.IDataRenderer"
width="120">
<fx:Metadata>
[Event(name="DataSelect", type="flash.events.Event")]
</fx:Metadata>
<fx:Script>
<![CDATA[
import flashx.textLayout.conversion.TextConverter;
import flashx.textLayout.elements.TextFlow;
import mx.charts.HitData;
import mx.charts.series.items.ColumnSeriesItem;
private var _data:HitData;
[Bindable]
private var _xValue:String;
[Bindable]
private var _yValue:String;
[Bindable]
private var _displayText:TextFlow;
public function get data():Object
{
// TODO Auto Generated method stub
return null;
}
public function set data(value:Object):void
{
// HitData data from chart
_data = value as HitData;
// The display text used in datatip which comes in HTML format
_displayText = TextConverter.importToFlow(_data.displayText, TextConverter.TEXT_FIELD_HTML_FORMAT);
// HitData contains a reference to the ChartItem
var item:ColumnSeriesItem = _data.chartItem as ColumnSeriesItem;
// ChartItem xValue and yValue
_xValue = String(item.xValue);
_yValue = String(item.yValue);
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<s:Rect right="0" left="0" bottom="0" top="0">
<s:filters>
<s:DropShadowFilter blurX="20" blurY="20" alpha="0.22" distance="5" angle="90" knockout="false" />
</s:filters>
<s:fill>
<s:SolidColor color="0xFFFFEE"/>
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0x1a1a19" weight="1" alpha=".2" />
</s:stroke>
</s:Rect>
<s:VGroup width="100%" height="100%" paddingTop="10" paddingRight="10" paddingBottom="10" paddingLeft="10">
<s:RichEditableText textFlow="{_displayText}" width="100%" textAlign="center" selectable="false" editable="false"/>
<s:Button label="Click me !"/>
</s:VGroup>
</s:Group>
And in your Chart mxml tag just add following
<mx:ColumnChart id="myChart" showDataTips="true" dataTipRenderer="com.CustomDataTipRenderer">
I created a custom button skin:
<?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:mx="library://ns.adobe.com/flex/mx"
minWidth="65" minHeight="22"
creationComplete="GlassButtonSkin_creationCompleteHandler(event)">
<fx:Metadata>[HostComponent("spark.components.Button")]</fx:Metadata>
<fx:Declarations>
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.graphics.RadialGradient;
import spark.effects.Fade;
import spark.effects.animation.RepeatBehavior;
[Bindable]
private var rectRollOverEffect:Rect = new Rect();
private var radialGradientRollOverEffect:RadialGradient = new RadialGradient();
private var gradientEntryRollOverEffect1:GradientEntry = new GradientEntry(0x8dbdff,NaN,0.7);
private var gradientEntryRollOverEffect2:GradientEntry = new GradientEntry(0x8dbdff,NaN,0);
private var indexOfRollOverEffect:int;
private var myFade:Fade;
protected function GlassButtonSkin_creationCompleteHandler(event:FlexEvent):void{
parent.mouseChildren = true;
this.addEventListener(MouseEvent.MOUSE_MOVE,mouseMoveHandler,true);
this.addEventListener(MouseEvent.MOUSE_OUT,mouseOutHandler,true);
this.addEventListener(MouseEvent.MOUSE_OVER,mouseOverHandler,true);
this.addElement(rectRollOverEffect);
indexOfRollOverEffect = this.getElementIndex(rectRollOverEffect);
this.removeElementAt(indexOfRollOverEffect);
}
private function mouseOverHandler(event:MouseEvent):void{
if(this.currentState == "disabled")
return;
createRollOverEffect(event,0);
myFade = new Fade(this.getElementAt(indexOfRollOverEffect));
myFade.alphaFrom = 0;
myFade.alphaTo = 1;
myFade.duration = 200;
myFade.end();
myFade.play();
}
private function mouseMoveHandler(event:MouseEvent):void{
if(this.currentState == "disabled")
return;
this.removeElementAt(indexOfRollOverEffect);
createRollOverEffect(event,1);
}
private function mouseOutHandler(event:MouseEvent):void{
if(this.currentState == "disabled")
return;
this.removeElementAt(indexOfRollOverEffect);
}
private function createRollOverEffect(event:MouseEvent,alpha:int):void{
rectRollOverEffect.alpha = alpha;
rectRollOverEffect.left = 2;
rectRollOverEffect.right = 2;
rectRollOverEffect.bottom = 2;
rectRollOverEffect.top = 2;
rectRollOverEffect.radiusX = 4;
rectRollOverEffect.radiusY = 4;
radialGradientRollOverEffect.entries = [gradientEntryRollOverEffect1,gradientEntryRollOverEffect2];
radialGradientRollOverEffect.x = event.localX;
radialGradientRollOverEffect.y = height-2;
radialGradientRollOverEffect.scaleX = width/1.5;
radialGradientRollOverEffect.scaleY = height;
rectRollOverEffect.fill = radialGradientRollOverEffect;
this.addElementAt(rectRollOverEffect,indexOfRollOverEffect);
}
]]>
</fx:Script>
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<s:transitions>
<s:Transition fromState="over" toState="disabled">
<s:CallAction target="{this}" functionName="removeElement" args="{[this.rectRollOverEffect]}"/>
</s:Transition>
</s:transitions>
<!-- outer border -->
<s:Rect left="0" right="0" top="0" bottom="0" id="outerBorder" radiusX="4" radiusY="4">
<s:stroke>
<s:SolidColorStroke id="outerBorderStroke" weight="1" color="#ffffff" />
</s:stroke>
</s:Rect>
<!-- inner border -->
<s:Rect left="1" right="1" top="1" bottom="1" id="innerBorder" radiusX="4" radiusY="4">
<s:stroke>
<s:SolidColorStroke id="innerBorderStroke" weight="1" color="#000000"/>
</s:stroke>
</s:Rect>
<!-- fill -->
<!--- Defines the appearance of the Button component's background. -->
<s:Rect id="background" left="1" right="1" top="1" bottom="1">
<s:fill>
<s:SolidColor alpha="0.5" color="#000000"/>
</s:fill>
</s:Rect>
<s:Rect id="backgroundTopPart" left="1" right="1" top="1" height="50%"
includeIn="up,over,disabled">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="#ffffff" alpha="0.5" ratio="0.1"/>
<s:GradientEntry color="#ffffff" alpha="0.1"/>
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Label id="labelDisplay"
text="Send"
textAlign="center"
verticalAlign="middle"
color="#FFFFFF"
horizontalCenter="0" verticalCenter="1"
left="10" right="10" top="2" bottom="2">
</s:Label>
<s:Rect id="disableForeground" left="0" right="0" top="0" bottom="0"
includeIn="disabled">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="#7B7B7B" alpha="0.6" ratio="0.1"/>
<s:GradientEntry color="#aaaaaa" alpha="0.3"/>
</s:LinearGradient>
</s:fill>
</s:Rect>
</s:SparkSkin>
The problem is that my hitarea seems to be wrong. When I click, it is OK, the area is right but with the mouse over event, the area seems to be different and smaller than the click area. I just don't understand why.
I even tried to change manually the hit area of the button skin by adding a line this.hitArea = this.interactiveGroup Where interactiveGroup is a group that contains the component borders but it didn't change anything.
I think its to do with you labelDisplay element of the skin. It has a right and left of 10.... if the right and left are set to 0, then the effect appears straight off, withough being too small.
I was wondering if there is a way to have an object A (e.g. a Rect) visually appear behind an object B (e.g. another Rect) but in front of it from a mouse rollover standpoint. In other words, if you move the mouse tip over the intersection of A and B, A rolls over instead of B.
If this doesn't exist out of the box I'm curious how you would implement something like this.
thank you
Maybe this sample suits your need?
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
private function putBlueRectInFront():void
{
blueRect.depth = 1;
redRect.depth = 0;
}
private function putRedRectInFront():void
{
redRect.depth = 1;
blueRect.depth = 0;
}
protected function rectContainer_rollOutHandler(event:MouseEvent):void
{
rectContainer.removeEventListener(MouseEvent.MOUSE_MOVE, rectContainer_mouseMoveHandler);
putBlueRectInFront();
}
protected function rectContainer_rollOverHandler(event:MouseEvent):void
{
rectContainer.addEventListener(MouseEvent.MOUSE_MOVE, rectContainer_mouseMoveHandler);
}
private function rectContainer_mouseMoveHandler(event:MouseEvent):void
{
if (event.localX <= redRect.width && event.localY <= redRect.height)
putRedRectInFront();
else
putBlueRectInFront();
}
]]>
</fx:Script>
<s:BorderContainer height="400" horizontalCenter="0" id="rectContainer"
rollOut="rectContainer_rollOutHandler(event)" rollOver="rectContainer_rollOverHandler(event)" verticalCenter="0"
width="400">
<s:borderStroke>
<s:SolidColorStroke color="black" weight="1" />
</s:borderStroke>
<s:Rect height="250" id="redRect" left="0" top="0" width="250">
<s:fill>
<s:SolidColor color="red" />
</s:fill>
</s:Rect>
<s:Rect bottom="0" height="250" id="blueRect" right="0" width="250">
<s:fill>
<s:SolidColor color="blue" />
</s:fill>
</s:Rect>
</s:BorderContainer>
</s:Application>
I don't think this works intrinsically.
To make ObjectA appear behind ObjectB, you just have to add ObjectA to the container before ObjectB. Flex handles the "Z-Order" based on the order items were added to the container.
I'd implement the Z-Order change using using swapChildren and mouse events. Possibly mouseOver to move ObjectA on top and then mouseOut to move ObjectA to the back.
If you have a more complicated structure with more than two classes, you can use swapChildrenAt.
I'm trying to create a simple skin for the flex progressbar control. Both the track and the bar should have rounded corners, and the bar should fill the track completely in the parts where it is being shown.
Here is the bar skin I've created based off this example:
<?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:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
override protected function initializationComplete():void {
useChromeColor = true;
super.initializationComplete();
}
]]>
</fx:Script>
<s:Rect radiusX="5" radiusY="5" top="0" left="0" right="0" bottom="0">
<s:fill>
<s:SolidColor color="#bb0203" />
</s:fill>
</s:Rect>
</s:SparkSkin>
Here is the progress bar declaration:
<mx:ProgressBar id="progressBar" name="progressBar" top="40" left="10" width="480" height="25"
label="" labelWidth="0"
trackSkin="Skins.ProgressBar.TrackSkin"
barSkin="Skins.ProgressBar.BarSkin" />
And here is the track skin:
<?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:mx="library://ns.adobe.com/flex/mx"
minHeight="25">
<fx:Script>
<![CDATA[
override protected function initializationComplete():void {
useChromeColor = true;
super.initializationComplete();
}
]]>
</fx:Script>
<s:Rect width="100%" height="100%" radiusX="5" radiusY="5">
<s:fill>
<s:SolidColor color="#d1d3d4" />
</s:fill>
</s:Rect>
</s:SparkSkin>
Unfortunately, it doesn't have quite the desired effect:
Instead of the bar being flush with the track, there's a margin, and the rounded border gets cut off.
How do I fix this?
In addition to setting the barSkin property, set the maskSkin property to the same value as barSkin:
You can assign this skin the same way as others in your code:
<mx:ProgressBar id="progressBar" name="progressBar" top="40" left="10" width="480" height="25"
label="" labelWidth="0"
trackSkin="Skins.ProgressBar.TrackSkin"
maskSkin="Skins.ProgressBar.BarSkin"
barSkin="Skins.ProgressBar.BarSkin" />
I develop interactive map: user can zoom some area and place labels by mouse click.
I am creating customSkin:
<ibm:Scroller top="0" left="0" width="100%" height="100%" id="scroller" doubleClickEnabled="true"
skinClass="com.ibm.ilog.elixir.skins.spark.ScrollerSkin">
<ibm:MapGroup id="mapGroup">
<ibm:MapFeatureGroup id="featureGroup" featureRenderer="skins.DistrictRenderer" keyField="district"/>
<ibm:MapDataGroup id="objectsDataGroup"
zoomable="false"
itemRenderer="skins.DistrictObjectsRenderer"
keyField="district" />
</ibm:MapGroup>
</ibm:Scroller>
In DistrictRenderer I dispatching mouse click.
But how can I place labels?
May be I must pass x,y to DistrictObjectsRenderer and there create new labels?
Its a final solution:
RegionMapSkin.mxml:
<ibm:Scroller top="0" left="0" width="100%" height="100%" id="scroller" skinClass="com.ibm.ilog.elixir.skins.spark.ScrollerSkin">
<ibm:MapGroup id="mapGroup">
<ibm:MapFeatureGroup id="featureGroup"
featureRenderer="com.skins.DistrictRenderer" />
<ibm:MapDataGroup id="objectsGroup"
zoomable="false"
itemRenderer="com.skins.ObjectsRenderer"
dataProvider="{ObjectsModel.getInstance().items}" />
</ibm:MapGroup>
</ibm:Scroller>
ObjectsRenderer.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:DataRenderer xmlns="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:ibm="http://www.ibm.com/xmlns/prod/ilog/elixir/2010"
left="{data.lon}"
top="{data.lat}">
<s:Label text="{data.title}"
color="#ffffff"
backgroundColor="#AB191A"
paddingLeft="3" paddingRight="3" paddingTop="4" paddingBottom="1"
top="-18" left="3"/>
<s:Ellipse width="6" height="6">
<s:fill>
<s:SolidColor color="#AB191A" />
</s:fill>
</s:Ellipse>
</s:DataRenderer>
And onMouseClick handler in DistrictRenderer:
private function onMouseClick(e:MouseEvent):void
{
var mapFeature:MapFeature = MapFeatureRenderer(e.currentTarget).mapFeature;
var map:RegionMap = app.map;
var p:Point = new Point(e.localX, e.localY);
p = map.mapToLongLat(p);
ObjectsModel.getInstance().items.addItem({
'title': parameters.title,
'lon': parameters.lon,
'lat': parameters.lat
});
}