How to continuously update an image based on contentHeight? - actionscript-3

I'm trying to draw a solid background color behind an image. The image is a banner that is to take up 33% of the top of my canvas unless that puts it outside of it's aspect ratio. That's where the background color comes in; the background color fills in the right-side of what's remaining after the image fills in it's maximum width without breaking outside of it's 33% height or aspect ratio.
My problem is, with the code below, it only checks the contentHeight once and therefore, if the image height is reduced below what was initially loaded, my background color can be seen below the image (undesired). I can't use my image height since sometimes the height of my image exceeds what is actually shown (because of maintainAspectRatio).
Does anyone know how to obtain a consistent (and bindable) height of the image at all times that is matched to the content and not the container?
I could fully accept that I'm approaching this the wrong way as well so any alternatives to this method that have the same desired result would be much appreciated.
Component:
<mx:Canvas id="mainCanvas" width="100%" height="100%">
<mx:HBox x="0" y="0" backgroundColor="{myBgColor}" width="100%" height="{Math.min(myImg.contentHeight, mainCanvas.height * 0.33)}" />
<mx:Image id="myImg" source="{myImageSrc}" maintainAspectRatio="true" width="100%" height="33%"/>
</mx:Canvas>

My (untested) suggestion is to use the complete handler of Image (spark, not mx):
<fx:Script>
<![CDATA[
function myComplete(event:Event) {
myBox.height = Math.min(myImg.contentHeight, mainCanvas.height * 0.33);
}
]]>
</fx:Script>
<mx:Canvas id="mainCanvas" width="100%" height="100%">
<mx:HBox id="myBox" x="0" y="0" backgroundColor="{myBgColor}" width="100%" />
<s:Image id="myImg" complete="myComplete(event)" source="{myImageSrc}" maintainAspectRatio="true" width="100%" height="33%"/>
</mx:Canvas>
Also I don't understand why you use HBox and not Image's backgroundColor...

Eventually had to use a resize handler on my image since the complete handler never fired for my embedded image source. I was limited to using Flex 3 so no spark components are available.
Solution below:
protected function resizeHandler(event:ResizeEvent):void
{
var newHeight = Math.round(Math.min(myImg.contentHeight, mainCanvas.height * 0.33)) - 1; // Magic -1 because image had a bottom white padding
blueBg.height = newHeight;
}

Related

Scroll bars disappear when switching between interaction modes?

I have an application that switches between touch and mouse interactions. On a as needed basis I change between the two. However, when I switch from touch to mouse the scroll bars have disappeared.
It seems like a bug. I'll post an example soon but basically just switch between modes:
scroller.setStyle("interactionMode", "touch");
// later:
scroller.setStyle("interactionMode", "mouse");
// scrollers are invisible after this call
Example code:
<s:Scroller id="myScroller" top="20" right="40">
<s:Group height="100" width="100">
<s:Rect width="100" height="400">
<s:fill>
<s:SolidColor color="red"/>
</s:fill>
<s:stroke>
<s:SolidColorStroke color="blue" weight="2"/>
</s:stroke>
</s:Rect>
</s:Group>
</s:Scroller>
ActionScript:
protected function button1_clickHandler(event:MouseEvent):void
{
if (myScroller.getStyle("interactionMode")=="mouse") {
myScroller.setStyle("interactionMode", "touch");
}
else {
myScroller.setStyle("interactionMode", "mouse");
}
}
You have to click down on the scroller and move it while it's in touch mode and then the scrollers disappear and don't reappear.
The visible and includeInLayout properties are both true after setting back to mouse.
It looks like it has to do with some skin parts being reused but not being reset. From the Scroller design documents:
The first time Scroller detects it needs to display a ScrollBar it
will ensure one exists
Scroller detects that it needs to display ScrollBar at beginning of touch interaction mode or at skin attach time if interaction mode
is anything but "touch"
If the legacy ScrollBar part already exists, the Scroller will use that one else the ScrollBar will create an instance from the factory part, set the instance to the corresponding non-factory part and call
partAdded() for it.
For example Scroller will create an instance from
the horizontalScrollBarFactory part and will set horizontalScrollBar
to that instance with partAdded() being called.
I've looked at all the scrollbar parts and scale, visible and includeInLayout are all true or 1. So, it seems the only way is to force it to recreate the parts.
The below is a work around. It changes the skins, validates and then changes back to the original skin:
if (myScroller.getStyle("interactionMode")=="mouse") {
myScroller.setStyle("interactionMode", "touch");
}
else {
myScroller.setStyle("interactionMode", "mouse");
myScroller.setStyle("skinClass", skins.MinimalScrollerSkin);
myScroller.validateNow();
myScroller.setStyle("skinClass", spark.skins.spark.ScrollerSkin);
}

In AS3/MXML, why does binding a component's height to height / 2 work better than binding it to 50%?

Take the following AS3/MXML code:
<?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="*"
backgroundColor="#000000" showStatusBar="false" width="400" height="400"
minWidth="0" minHeight="0">
<s:Rect width="50%" height="50%">
<s:fill>
<s:SolidColor color="#0000FF"/>
</s:fill>
</s:Rect>
</s:WindowedApplication>
This mostly works. As I increase or decrease the size of the program, the Rect's size will scale to be 50% of the width and height of the WindowedApplication. But as I keep decreasing the window's height, the scaling down stops several pixels short of 0. This is as small as I can get the Rect to be along the y-axis:
After it gets to this point, even if I keep decreasing the size of the WindowedApplication, nothing happens. The Rect stays at exactly the same height until I start increasing the window's size again. What's more is that the Rect is 12 pixels in height, which is a pretty arbitrary number for it to be stopping on.
However if I change:
<s:Rect width="50%" height="50%">
to:
<s:Rect width="{width / 2}" height="{height / 2}">
the problem magically goes away:
The WindowedApplication's height is 5, and the Rect's height is about "two-and-a-half".
Why is there a distinction like this? In the previous example, I did try increasing, then decreasing the size again a few times, even slowly, but it always got stuck in the same spot. Thanks!
There are actually a few separate properties in play here:
height
percentHeight
explicitHeight
height is really just some smoke and mirrors behind percentHeight and explicitHeight.
In the first approach, even though you are specifying the height property as a percent in MXML; the Flex Compiler does some magic behind the scenes to check for the percent character '%' and set the percentHeight property instead of width.
Since no explicit height is given when setting the percent height; the s:Rect's validateSize() method should execute. This method seems like a parallel to the measure() method of the Flex Component lifeCycle.
The validateSize() method does some checking against the explicitMinHeight property
if (!isNaN(explicitMinHeight) && measuredHeight < explicitMinHeight)
measuredHeight = explicitMinHeight;
So, in essence, there is a minimum height; which the component's height will not go lower than.
However, in your second approach, you are not specifying a percentage you are specifying an actual value. As such the explicitHeight value is set. Because an explicit height is set that is given precedence and there is no need to execute the validateSize() method. This line in the validateSize() skips it:
if (!canSkipMeasurement())
measure();
And
protected function canSkipMeasurement():Boolean
{
return !isNaN(explicitWidth) && !isNaN(explicitHeight);
}
So, hopefully that makes sense.
In summary; if you set a percent height; then Flex calculates the height and there is a minimum value. If you set an explicit height; then Flex uses that value and skips its own internal calculations.
[I only did a cursory review of the Flex framework code, so you'll have to review code to dig down deeper]

Scroller acting different regarding his first child

im' facing a strange issue with Scroller in my application.
I'm using for my forms scrollers like this :
<s:Group id="mainGrp" width="100%" height="100%" >
<s:Scroller id="scroller" width="100%" bottom="50" top="30" >
<s:Group id="childrenGrp" width="100%" height="100%" >
<s:VGroup width="100%" height="100%" paddingTop="15" paddingLeft="20" gap="20" paddingRight="10" paddingBottom="10">
</s:VGroup>
</s:Group>
</s:Scroller>
</s:Group>
With this example, my scroll bar (with mouse wheel) is quite slow. If i remove the container Group with id "childrenGrp", i 've got a normal speed but the padding on the Vgroup make the scroller in some strange state (impossible to go to the top and the bottom of the container).
My questions are :
1/ Why the scroll speed change regarding scroller's first child ? (group make it slow, and vgroup is normal)
2/Why paddingTop and paddingBottom on scroller's first child make the scroller unusable ?
Thx in advance.
For number 1 I don't have an exact answer, but you could try this: Capture the mousewheel event and trace the event.delta (the amount of lines the mousewheel scroll moves the bar)
protected function foo_mouseWheelHandler(event:MouseEvent):void
{
trace(event.delta);
}
Is the amount different on both cases (with or without the group)

Why is Flex SolidColorStroke acting that weird?

I had to solve a problem concerning the SolidColorStroke of the flex framework. The scenario is simple, we have an visible object and we want a border around it. I built a graphics component that draws a Rect. That Rect was defined as follows
object; // the object which should get the border, defined somewhere outside,
// just fyi
var borderThickness:Number = 10;
rect.x = object.x - borderThickness;
rect.y = object.y - borderThickness;
rect.width = object.width + (borderThickness * 2);
rect.height = object.height + (borderThickness * 2);
rectStroke.weight = borderThickness;
//the MXML code
<s:Rect id="rect">
<s:stroke>
<s:SolidColorStroke id="rectStroke" />
</s:stroke>
</s:Rect>
I thought it should like this (just for illustration, not an exact image of what it should be, the border in this illustration is exactly around the object)
But I was wrong because the border did cover some parts of the objects on the right and on the bottom. My next thought was, that the stroke of the border doesn't grow inside the component, but on each side of the border in equally parts. What I mean by that is that when it grows inside the component and you place it at the location x = 0, y = 0 the width in my example of the left side of the border would be from 0 to 10. The thicker the border gets, the more it grows to the inside until there is just one big rectangle.
When I say it grows equally to each part of the border I mean that if you place the rect on x = 0, y = 0 and your border is 10px thick, the left side of the border goes from -5 to 5.
I hope it's clear what I mean.
So, as I said before I thought the border grows equally to both parts of the stroke. So I changed the calculations of the width and height to:
rect.width = object.width + borderThickness;
rect.height = object.height + borderThickness;
Now the width and height of the object is just increase the borderThickness (half of the borderThickness on every side). I thought now the border should fit exactly the object (as I expected it with my first version too...).
It look better than the first version but still some parts on the right and the left of the object are covered.
After a long time of thinking why it doesn't work as I expect I found a solution that worked for me. It seems that the stroke doesn't grow to equal parts on both sides, it seems that it grows by 75% to the inside and 25% to the outside. The following illustration shows what I mean by that.
The yellow line inside the border shows the actual border (when the stroke would be 1px). You can see that it is now exactly in the middle of the stroke but 75% from the inside and 25% from the outside.
My Question is, does anybody experience a similar behavior? Does anyone know why that works the way it does. Am I using it correct? Or what am I doing wrong?
The documentation of Adobe doesn't really tell you that the SolidColorStroke works this way. If you need more code, please let me know.
kind regards
Markus
<s:Group width="250" height="250" x="50" y="50">
<!--Normally borders grows inside so for ex: top should be equal to -weight of the stroke -->
<s:Rect top="-10" left="-10" right="-10" bottom="-10">
<s:stroke>
<s:SolidColorStroke weight="10" color="0x00FF00"/>
</s:stroke>
</s:Rect>
<s:Rect top="0" left="0" right="0" bottom="0">
<s:stroke>
<s:SolidColorStroke weight="1"/>
</s:stroke>
</s:Rect>
</s:Group>
AS Method:
private function drawSimpleBorders(obj:UIComponent, tickness:Number = 5):void
{
var gr:Graphics = obj.graphics;
gr.lineStyle(tickness,0,alpha);
var k:Number = tickness/2;
gr.drawRect(-k,-k, obj.width+tickness, obj.height+tickness);
}
I tried this and had no problems, just send target and you'll get borders.

ActionScript: manually scrolling a element wrapped inside a Scroller window

The answer to my question is possibly easy, yet I haven't found an example of solving it in the web, nor have I found a solution reading ActionScript reference.
My problem is the following: I have a big UIComponent derivate element inside a Scroller (spark.components.Scroller) window. The Scroller class is great because when my canvas element, that changes size dynamically, exceeds its boundaries, scrollbars appear automatically and handle scrolling of my UIComponent inside it.
However, I'd like not just to be able to Scroll using these automatically appeared scroll bars, but also by using a pan tool that I will myself implement (similar to the hand tool in Adobe software, for example). The thing is that I am not able to modify correctly the element's position inside the Scroller window.
I tried, as a first approach, accessing to my elements' 'x' and 'y' properties, however, when changing them, I dont get the resulkts wanted. The code for it is the following (in which, for symplifying reasons, I used a text label as my 'inside' element, and two buttons as an external scroll controller, instead of a hand tool)
Tests.mxml
<fx:Script>
<![CDATA[
protected function der_clickHandler(event:MouseEvent):void
{
text.x += 5;
}
protected function izq_clickHandler(event:MouseEvent):void
{
text.x -= 5;
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<s:Scroller id="sc" x="50" y="50" width="200" height="100">
<s:Group width="100%" height="100%">
<s:Label id="text" width="400" height="10" x="50" y="50" text="blebleblebleblebleblebleblebleblebleblebleble"/>
</s:Group>
</s:Scroller>
<s:Button id="der" x="154" y="182" label="->" click="der_clickHandler(event)"/>
<s:Button id="izq" x="76" y="182" label="<-" click="izq_clickHandler(event)"/>
</s:Application>
And a link to a compiled version, to see what I'm speaking about:
http://megaswf.com/file/1135821
(press 'view fullscreen' if you can't see it)
Help would be really appreciated, as I've been stuck all afternoon with this, and really need it for my project :S
Anyway, many thanks in advance, and regards!
I managed to solve it! (This is the second time this week that I solve something just after posting it here :D).
The property that I had to change is sc.viewport.horizontalScrollPosition+=5; instead of the internal elements 'x' position. And the same for vertical pos.