How can I draw a joined border around two components? - apache-flex

Before I tackled this myself, I thought I pick the minds of the SO community.
Let's assume I have an Image that's used a button. By default, the image has no border around it. Clicking on this Image will cause another component be displayed beneath it or next to it. When the second component is displayed, I want to draw a joined border around the Image and the second component.
The second component would not be visible by default. It would only be visible after clicking on the Image by using the popupManager, PopUpAnchor, setting the visible property, etc.
Example before clicking:
+--------------------------------+
| |
| XXX <-- My Image |
| XXX |
| |
| |
| |
| |
| |
| |
| |
+--------------------------------+
Example after clicking:
+--------------------------------+
| +---+ |
| |XXX| <-- My Image |
| |XXX|_______________ |
| | | |
| | My Second | |
| | Component | |
| | | |
| | | |
| +-------------------+ |
| |
+--------------------------------+
How difficult would it be to create something like this?

If you are using flex4 / spark, then you should consider skinning (anyway, the borderSides style is available only in Halo).
here is a small sample to achieve the behavior shown at the images:
It uses a custom component with two states (collapsed and expanded).
The text is only attached in the expanded state, and so are the border skins.
For this you'll need two skin classes that will be applied to the image- and to the text component's parent.
The Application:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:my="*">
<s:VGroup width="100%" height="100%" paddingLeft="20" paddingTop="20">
<my:ExpandableImage id="component" width="300" />
<s:Button label="{component.currentState}" click="component.changeState(event);" />
</s:VGroup>
</s:WindowedApplication>
ExpandableImage.mxml //the custom component with two states
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
verticalGap="0" width="100%"
currentState="collapsedState">
<fx:Script>
<![CDATA[
public function changeState(event:MouseEvent):void
{
if (currentState == 'collapsedState')
currentState = 'expandedState';
else
currentState = 'collapsedState';
}
]]>
</fx:Script>
<mx:VBox id="imgHolder" borderSkin="MyImageSkin"
width="50" height="50" includeIn="collapsedState, expandedState" cornerRadius="5"
backgroundAlpha="1" borderAlpha="0" backgroundColor="#FFFFFF" borderColor="#000000">
<mx:Image id="img" source="{IMyConstants.MYLOGO}"
width="48" height="48"
mouseEnabled="true" click="changeState(event)" />
</mx:VBox>
<mx:VBox id="txtHolder" borderSkin="MyDetailsSkin"
width="100%" height="100%" includeIn="expandedState" cornerRadius="5"
backgroundAlpha="1" borderAlpha=".5" backgroundColor="#FFFFFF" borderColor="#000000">
<mx:Text id="txt" text="{IMyConstants.LOREMIPSUM}"
width="100%" />
</mx:VBox>
<mx:states>
<s:State name="collapsedState" />
<s:State name="expandedState" />
</mx:states>
<mx:transitions>
<mx:Transition fromState="collapsedState" toState="expandedState">
<s:Parallel duration="500">
<mx:Resize target="{this}" />
<mx:SetStyleAction target="{imgHolder}"
name="borderAlpha" value=".5" />
</s:Parallel>
</mx:Transition>
<mx:Transition fromState="expandedState" toState="collapsedState">
<s:Parallel duration="500">
<mx:Resize target="{this}" />
<mx:SetStyleAction target="{imgHolder}"
name="borderAlpha" value="0" />
</s:Parallel>
</mx:Transition>
</mx:transitions>
</mx:VBox>
MyImageSkin.as //the skin to be applied on the image component's parent
public class MyImageSkin extends RectangularBorder
{
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
var cornerRadius:Number = getStyle("cornerRadius");
var borderColor:int = getStyle("borderColor");
var borderAlpha:Number = getStyle("borderAlpha");
var backgroundColor:int = getStyle("backgroundColor");
var backgroundAlpha:Number = getStyle("backgroundAlpha");
graphics.clear();
//border
drawRoundRect(0, 0, unscaledWidth, unscaledHeight,
{tl: cornerRadius, tr:cornerRadius, bl: 0, br: 0},
borderColor, borderAlpha);
//content
drawRoundRect(1, 1, unscaledWidth-2, unscaledHeight-1,
{tl: cornerRadius, tr:cornerRadius, bl: 0, br: 0},
backgroundColor, backgroundAlpha);
}
}
MyDetailsSkin.as //the skin to be applied on the text's parent
public class MyDetailsSkin extends RectangularBorder
{
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
var cornerRadius:Number = getStyle("cornerRadius");
var borderColor:int = getStyle("borderColor");
var borderAlpha:Number = getStyle("borderAlpha");
var backgroundColor:int = getStyle("backgroundColor");
var backgroundAlpha:Number = getStyle("backgroundAlpha");
graphics.clear();
//border
drawRoundRect(0, 0, unscaledWidth, unscaledHeight,
{tl: 0, tr:cornerRadius, bl: cornerRadius, br: cornerRadius},
borderColor, borderAlpha);
//content
drawRoundRect(1, 1, unscaledWidth-2, unscaledHeight-2,
{tl: 0, tr:cornerRadius, bl: cornerRadius, br: cornerRadius},
backgroundColor, backgroundAlpha);
//clear separator
drawRoundRect(1, 0, 49, 1, {tl: 0, tr:1, bl: 1, br: 1}, backgroundColor, 1);
}
}
i really hope this helps

Pretty sure you can do this with styles
Add a third container i marked with ****
turn the top and right borders off on my new container(***)
turn the bottom border off on your image container
turn the top border off on your second component
+--------------------------------+
| +---+**************** |
| |XXX| <-- My Image * |
| |XXX|_______________* |
| | | |
| | My Second | |
| | Component | |
| | | |
| | | |
| +-------------------+ |
| |
+--------------------------------+

Related

resize video child of VideoDisplay to the same size

i have the following code to play a video stored in the media server:
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="592" height="372">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.events.ResizeEvent;
private var ns:NetStream;
private var nc:NetConnection;
private var video:Video;
private var meta:Object;
private function ns_onMetaData(item:Object):void {
trace("meta");
meta = item;
// Resize Video object to same size as meta data.
video.width = item.width;
video.height = item.height;
// Resize UIComponent to same size as Video object.
myVid.width = video.width;
myVid.height = video.height;
}
private function fetch_rec():void {
var nsClient:Object = {};
nsClient.onMetaData = ns_onMetaData;
nc = new NetConnection();
nc.connect(null);
ns = new NetStream(nc);
ns.client = nsClient;
video = new Video();
video.attachNetStream(ns);
video.smoothing=true;
myVid.addChild(video);
ns.play("http://localhost:5080/oflaDemo/recordings/firstattempt.flv");
}
]]>
</fx:Script>
<mx:VideoDisplay id="myVid" bottom="0" width="100%" height="100%"
/*??how to make the progressbar work???*/ playheadUpdate="progBar.setProgress(myVid.playheadTime,myVid.totalTime)"/>
<s:HGroup right="10" top="7">
<mx:Button id="button4" x="498" y="250" label="Play/Reload" click="fetch_rec();"/>
<mx:Button id="button5" x="512" y="279" label="Pause" click="ns.pause()"/>
</s:HGroup>
<mx:ProgressBar id="progBar" left="10" right="10" bottom="7" height="10" label="" mode="manual"/>
</s:Group>
and i would like to know
1)how to make the video follow the resizing of the myVid VideoDisplay and does not show small inside it as it does and
2)how to make the progressbar work with the streamed video...
Thanks in advance for your help!
I can't answer for progress bar. For scaling video to video display I use Transform and Matrix, when VideoDisplay size is changed I just replace old Matrix with new scaled one. If your video will be smaller then target then it should expand.
Here are snippets of code (I skip creation, etc):
<!-- language: lang-xml -->
protected function scaleVideoToDisplay():void {
var ratio:Number;
if (video == null) {
return;
}else if (video.width == 0 || video.height == 0) {
ratio = 0.0;
}else {
ratio = Math.min(
videoDisplay.width / video.width,
videoDisplay.height / video.height);
}
video.transform.matrix = new Matrix(ratio, 0, 0, ratio);
}
<s:VideoDisplay width="100%" height="100%" resize="scaleVideoToDisplay()"/>
<!-- langugae:lang-none -->
For video creation I use:
<!-- language: lang-xml -->
video.transform = new Transform(video);
In addition to this You will probably need to scale video after creation.

Flex: Bottom-left align components?

As the title suggests, is there any way to bottom-left align components?
An <HBox .../> nested in a <Canvas .../> doesn't work because the elements in the HBox are top-aligned instead of bottom aligned.
For example, I'd like my components to be aligned like this:
+-------------+ <-- container
| components |
| | V |
| V +--+ |
| +-+ | | |
| +-+ +--+ |
+-------------+
You just need to set the verticalAlign and horizontalAlign styles on the hbox ie:
<mx:Canvas>
<mx:HBox verticalAlign="bottom" horizontalAlign="left" left="0" bottom="0"> </mx:HBox>
</mx:Canvas>
Extend the HBox and/or Box to change the positioning. I suspect you'll probably have to override the updateDisplayList method to change the way components are positioned. Probably instead of setting the y value as "0" you'll want to set it to unscaledHeight-component.width.

flex positioning a button within a panel

All I'm trying to do is place a button inside of a panel, rotate that button (so it is vertical) and place it on the edge of the panel. I can;t seem to do this correctly. Here is my code:
<mx:Panel id="weekList" width="260" height="100%" x="-500" title="Weeks" >
<mx:List id="weekButtonList" width="260" borderVisible="false" contentBackgroundAlpha="0" dataProvider="{_data.mappoints.week.#number}" itemClick="onWeekClick(event);" >
<mx:itemRenderer>
<mx:Component>
<mx:Button buttonMode="true" right="20" width="260" height="50" label="Week {data}" />
</mx:Component>
</mx:itemRenderer>
</mx:List>
<mx:HBox id="closeButtonHolder" rotation="90" width="100" >
<mx:Button label="OPEN" click="weekListToggle()" />
</mx:HBox>
</mx:Panel>
If you look at the part of the script you will see I am trying to rotate it and move it to the left. I am just trying to move it somewhere, and nothing is working. Also, the text seems to dissapear when I rotate it on a 90% axis. Anyone know what I can do for this?
Use mx:Canvas and set its width to 100%, like so:
<mx:Canvas width="100%">
<mx:Button buttonMode="true" right="20" width="260" height="50" label="Week {data}" />
</mx:Canvas>
InvertedSpear has answered the other part of your question, so I won't repeat that.
In order to rotate text you have to embed the font. I'll play around and see if I can get you a more complete answer for your other issue.
OK, here are some of your problems.
1) The x of your panel is -500 so it is WAY off the screen, maybe you need that for some reason but in my test it just pushed out of view.
2) rotation requires embedding fonts
3) when you rotate any UI component within another the default pivot is on the upper left hand corner, so when the button rotates, it actually rotates out of view. This is not easy to understand when you read it so here's a visual, consider the upper left hand corner of the container as 0,0 in XY coordinates:
normal hbox/button layout:
0,0_________________________________
| ________________________________ |
| | | | <-container outside
| | UI component | |
| |______________________________| |
|__________________________________|
rotated layout:
___________0,0__________________________
| | |
| UI | container |
| component| |
| | |
| |___________________________|
| |
| |
| |
| |
|__________|
See how the button has rotated outside of the visible area (in your case it is no longer on the panel) Solution would be to use a canvas or something that will allow you to pull the HBox away from the edge of the panel.

Flex spark Percent height not working

First of all I know there is a spark VolumeBar component but, for design requirements I can't use it..
I'm trying to create a custom component but heights are not responding as should
[Update]
This is were I call the class
<components:VolumeSlider steps="4" height="100" />
The problem is that the volume slider is adapting perfectly, but My custom items component doesn't.
<s:HGroup width="100%" height="100%" maxHeight="{height}" >
<s:VGroup width="100%" height="100%" paddingBottom="20" paddingTop="20">
<s:VSlider id="slider" width="100%" height="100%" maximum="{_steps-1}" />
</s:VGroup>
<s:VGroup id="items" width="100%" height="100%" />
</s:HGroup>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
[Bindable]
private var _steps:uint = 10;
public function set steps( value:uint ):void
{
_steps = value;
if ( items != null && items.numChildren != 0 )
{
items.removeAllElements();
}
create();
}
private function create():void
{
for ( var i:uint = 0; i < _steps; ++i )
{
var item:VolumeSliderItem = new VolumeSliderItem();
item.percentHeight = item.percentWidth = 100;
if ( items != null )items.addElement(item );
}
}
]]>
</fx:Script>
where VolumeSliderItem is a spark button
I don't see any call to create(). I added 'creationComplete="create()"' on the Application tag, and then it created 10 sliders to the VGroup with id 'items'. Is that what you're looking for?

Populate a datagrid from a Loop

I'm just starting to learn flex and AS3
I'm trying to get information into a datagrid that originates from a mathmatical formula. For example if I have 100 and I subtract 5 from it and continue to do so until it reaches 0 so my grid would be something like:
100 | -5
95 | -5
90 | -5
...
...
5 | -5
0
I'm guessing that it needs to do something like this but can't find any examples of how to impliment something like this:
var i:Number = 100;
do {
add row to datagrid
i-5;
} while (i < 0);
Thanks
Dave
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var myDataProvider:ArrayCollection = new ArrayCollection();
private function onCreationComplete():void
{
var i:int = 100;
while(i >= 0) {
myDataProvider.addItem({"index" : i});
i -= 5;
}
}
]]>
</mx:Script>
<mx:DataGrid dataProvider="{ myDataProvider }" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="index" headerText="#"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>
Also take a look in Flex language reference DataGrid, there is example at the bottom of the page.

Resources