I think I found a bug in the FlexLayout. I've tried to nest 2 FlexLayouts where the outer one should be the column container and the inner one the row container. But the page stays empty. I've already filed a bug report which was accept for triage but my guess is that it's going take some time to sort that out. So I have to solve this one myself I guess.
So here's the problem I would like to solve in a class I call the ColumnLayout. I am trying to get a text to flow from column to the next column and the next, etc like this.
The quick brown fox jumps over the lazy dog. --> The quick brown fox jumps over the lazy dog. --> etc.
The quick brown fox jumps over the lazy dog. | The quick brown fox jumps over the lazy dog. |
The quick brown fox jumps over the lazy dog. | The quick brown fox jumps over the lazy dog. |
The quick brown fox jumps over the lazy dog. | The quick brown fox jumps over the lazy dog. |
The quick brown fox jumps over the lazy dog. | The quick brown fox jumps over the lazy dog. |
The quick brown fox jumps over the lazy dog. | The quick brown fox jumps over the lazy dog. |
The quick brown fox jumps over the lazy dog. | The quick brown fox jumps over the lazy dog. |
---------------------------------------------- ----------------------------------------------
The text is inside StackLayouts with a horizontal orientation. Here's an impression of that. You can see the text flowing off of the page at the bottom. That's where I would like to a new column to begin to the right of the first one.
Here's the code that produces this output:
private BindableObject GetXaml(Document doc)
{
var column = new ColumnLayout()
{
StyleClass = new List<string> { "ColumnContainer" }
};
foreach (var line in doc.Lines)
{
var row = new FlexLayout
{
StyleClass = new List<string> { "RowContainer" }
};
column.Children.Add(row);
InterpretDirective(row, line);
}
return column;
}
This is ColumnLayout class which is currently just the StackLayout:
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace TGB.Xamarin.Forms.Layouts
{
public class ColumnLayout : StackLayout
{
public ColumnLayout() { }
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
var size = base.OnMeasure(widthConstraint, heightConstraint);
return size;
}
}
}
I'm not sure how to solve this puzzle. Xamarin components measure items from the outside in. It seems that the OnMeasure(double widthConstraint, double heightConstraint) somehow comes into play. Can someone explain to me how this works.
My idea is to add a stacklayout with vertical orientation into the main horizontal stacklayout when size of the column is exhausted but I don't know if that is the right strategy. Any help appreciated.
**** EDIT ****
Here's an example from another program that does just that:
Ok, after some fiddling around I've got it working. Documentation on some stuff of Xamarin.Forms is sparse but I managed to analyze how this should be done. Here's the full code for the ColumnLayout class:
using System;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;
namespace TGB.Xamarin.Forms.Layouts
{
public class ColumnLayout : Layout<View>
{
public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(
"ColumnSpacing",
typeof(double),
typeof(ColumnLayout),
5.0,
propertyChanged: (bindable, oldvalue, newvalue) =>
{
((ColumnLayout)bindable).InvalidateLayout();
});
public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(
"RowSpacing",
typeof(double),
typeof(ColumnLayout),
5.0,
propertyChanged: (bindable, oldvalue, newvalue) =>
{
((ColumnLayout)bindable).InvalidateLayout();
});
public double ColumnSpacing
{
set { SetValue(ColumnSpacingProperty, value); }
get { return (double)GetValue(ColumnSpacingProperty); }
}
public double RowSpacing
{
set { SetValue(RowSpacingProperty, value); }
get { return (double)GetValue(RowSpacingProperty); }
}
private (double totalHeight, double largestWidth) GetTotalChildSizes()
{
double totalHeight = 0;
double largetsWidth = 0;
foreach (var child in Children)
{
var request = child.Measure(double.PositiveInfinity, double.PositiveInfinity);
largetsWidth = request.Request.Width > largetsWidth ? request.Request.Width : largetsWidth;
}
largetsWidth = largetsWidth > Application.Current.MainPage.Width ? Application.Current.MainPage.Width : largetsWidth;
foreach (var child in Children)
{
var request = child.Measure(largetsWidth, double.PositiveInfinity);
totalHeight += request.Request.Height;
}
return (totalHeight, largetsWidth);
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
if (Children.Count(child => child.IsVisible) == 0)
{
return new SizeRequest();
}
var sizes = GetTotalChildSizes();
var columns = 0;
if (sizes.totalHeight < heightConstraint)
{
columns = 1;
}
else
{
columns = ((int)(sizes.totalHeight / heightConstraint)) + 1;
}
var requestedSize = new Size(columns * sizes.largestWidth, sizes.totalHeight) ;
return new SizeRequest(requestedSize);
}
protected override void LayoutChildren(double x, double y, double width, double height)
{
double xChild = x;
double yChild = y;
double yNew = 0;
var sizes = GetTotalChildSizes();
foreach (View child in Children)
{
if (child.IsVisible)
{
SizeRequest childSizeRequest = child.Measure(sizes.largestWidth, Double.PositiveInfinity);
if (yChild + childSizeRequest.Request.Height > height)
{
yChild = y;
yNew = y + RowSpacing + childSizeRequest.Request.Height;
xChild += ColumnSpacing + childSizeRequest.Request.Width;
}
else
{
yNew = yChild + RowSpacing + childSizeRequest.Request.Height;
}
LayoutChildIntoBoundingRegion(child, new Rectangle(new Point(xChild, yChild), childSizeRequest.Request));
yChild = yNew;
}
}
}
}
}
Here's the result.
I've not added caching yet but I think I should. The Measure() routines are called pretty often to get the right sizes so some caching would realy speed things up.
Related
Creating new components in JavaFX is still a but muddy to me compared to "Everything is a JPanel" in Swing.
I'm trying to make a fixed size component. I hesitate to call it a control, it's a pane of activity, not a button.
But here's my problem.
The fixed size I want is smaller than the contents of the element.
The grid is, in truth, 200x200. I'm shifting it up and left 25x25, and I'm trying to make the fixed size of 150x150. You can see in my example I've tried assorted ways of forcing it to 150, but in my tests, the size never sticks. Also, to be clear, I would expect the lines to clip at the boundary of the component.
This is, roughly, what I'm shooting for in my contrived case (note this looks bigger than 150x150 because of the retina display on my Mac, which doubles everything):
I've put some in to a FlowPane, and they stack right up, but ignore the 150x150 dimensions.
FlowPane fp = new FlowPane(new TestPane(), new TestPane(), new TestPane());
var scene = new Scene(fp, 640, 480);
stage.setScene(scene);
I tried sticking one in a ScrollPane, and the scroll bars never appear, even after resizing the window.
TestPane pane = new TestPane();
ScrollPane sp = new ScrollPane(pane);
var scene = new Scene(sp, 640, 480);
stage.setScene(scene);
And I struggle to discern whether I should be extending Region or Control in these cases.
I am missing something fundamental.
package pkg;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.shape.Line;
import javafx.scene.transform.Translate;
public class TestPane extends Control {
public TestPane() {
setMinHeight(150);
setMaxHeight(150);
setMinWidth(150);
setMaxWidth(150);
setPrefHeight(150);
setPrefWidth(150);
populate();
}
#Override
protected double computePrefHeight(double width) {
return 150;
}
#Override
protected double computePrefWidth(double height) {
return 150;
}
#Override
protected double computeMaxHeight(double width) {
return 150;
}
#Override
protected double computeMaxWidth(double height) {
return 150;
}
#Override
protected double computeMinHeight(double width) {
return 150;
}
#Override
protected double computeMinWidth(double height) {
return 150;
}
#Override
public boolean isResizable() {
return false;
}
private void populate() {
Translate translate = new Translate();
translate.setX(-25);
translate.setY(-25);
getTransforms().clear();
getTransforms().addAll(translate);
ObservableList<Node> children = getChildren();
for (int i = 0; i < 4; i++) {
Line line = new Line(0, i * 50, 200, i * 50);
children.add(line);
line = new Line(i * 50, 0, i * 50, 200);
children.add(line);
}
}
}
Addenda, to clarify.
I want a fixed sized component. It's a rectangle. I want it X x Y big.
I want to draw things in my box. Lines, circles, text.
I want the things I draw to clip to the boundaries of the component.
I don't want to use Canvas.
More addenda.
What I'm looking for is not much different from what a ScrollPane does, save I don't want any scroll bars, and I don't want the size of the outlying pane to grow or shrink.
TLDR:
Subclass Region,
make isResizable() return true to respect pref, min, and max sizes,
explicitly set a clip to avoid painting outside the local bounds.
Most of the documentation for this is in the package documentation for javafx.scene.layout
First, note the distinction between resizable and non-resizable nodes. Resizable nodes (for which isResizable() returns true) are resized by their parent during layout, and the parent will make a best-effort to respect their preferred, minimum, and maximum sizes.
Non-resizable nodes are not resized by their parent. If isResizable() returns false, then resize() is a no-op and the preferred, minimum, and maximum sizes are effectively ignored. Their sizes are computed internally and reported to the parent via its visual bounds. Ultimately, all JavaFX nodes have a peer node in the underlying graphical system, and AFAIK the only way a non-resizable node can determine its size is by directly setting the size of the peer. (I'm happy to be corrected on this.)
So unless you want to get your hands really dirty with custom peer nodes (and I don't even know if the API has mechanisms for this), I think the preferred way to create a "fixed size node" is by creating a resizable node with preferred, minimum, and maximum sizes all set to the same value. This is likely by design: as noted in a comment to your question, fixed-size nodes in layout-driven UI toolkits are generally discouraged, other than very low-level components (Text, Shape, etc).
Transformations applied to resizable nodes are generally applied after layout (i.e. they don't affect the layout bounds). Therefore using a translation to manage the internal positioning of the child nodes is not a good approach; it will have effects on the layout of the custom node in the parent which you probably don't intend.
As you note, you are not really defining a control here; it has no behavior or skin. Thus subclassing Control is not really the rigth approach. The most appropriate hook in the API is to subclass Region. Override the layoutChildren() method to position the child nodes (for Shapes and Text nodes, set their coordinates, for resizable children call resizeRelocate(...)).
Finally, to prevent the node spilling out of its intended bounds (150x150 in your example), either ensure no child nodes are positioned outside those bounds, or explicitly set the clip.
Here's a refactoring of your example:
import javafx.scene.layout.Region;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
public class TestPane extends Region {
private Line[] verticalLines ;
private Line[] horizontalLines ;
private static final int WIDTH = 150 ;
private static final int HEIGHT = 150 ;
private static final int LINE_GAP = 50 ;
public TestPane() {
populate();
}
#Override
protected double computePrefHeight(double width) {
return HEIGHT;
}
#Override
protected double computePrefWidth(double height) {
return HEIGHT;
}
#Override
protected double computeMaxHeight(double width) {
return HEIGHT;
}
#Override
protected double computeMaxWidth(double height) {
return WIDTH;
}
#Override
protected double computeMinHeight(double width) {
return WIDTH;
}
#Override
protected double computeMinWidth(double height) {
return WIDTH;
}
#Override
public boolean isResizable() {
return true;
}
#Override
public void layoutChildren() {
double w = getWidth();
double h = getHeight() ;
double actualWidth = verticalLines.length * LINE_GAP ;
double actualHeight = horizontalLines.length * LINE_GAP ;
double hOffset = (actualWidth - w) / 2 ;
double vOffset = (actualHeight - h) / 2 ;
for (int i = 0 ; i < verticalLines.length ; i++) {
double x = i * LINE_GAP - hOffset;
verticalLines[i].setStartX(x);
verticalLines[i].setEndX(x);
verticalLines[i].setStartY(0);
verticalLines[i].setEndY(h);
}
for (int i = 0 ; i < horizontalLines.length ; i++) {
double y = i * LINE_GAP - vOffset;
horizontalLines[i].setStartY(y);
horizontalLines[i].setEndY(y);
horizontalLines[i].setStartX(0);
horizontalLines[i].setEndX(w);
}
setClip(new Rectangle(0, 0, w, h));
}
private void populate() {
verticalLines = new Line[4] ;
horizontalLines = new Line[4] ;
for (int i = 0 ; i <verticalLines.length ; i++) {
verticalLines[i] = new Line();
getChildren().add(verticalLines[i]);
}
for (int i = 0 ; i <horizontalLines.length ; i++) {
horizontalLines[i] = new Line();
getChildren().add(horizontalLines[i]);
}
}
}
A more sophisticated example might have, for example, LINE_GAP as a property. When that property changes you would call requestLayout() to mark the component as "dirty", so its layoutChildren() method would be called again on the next frame rendered.
Here's a quick test case:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) {
FlowPane root = new FlowPane();
root.setAlignment(Pos.TOP_LEFT);
root.setPadding(new Insets(10));
root.setHgap(5);
root.setVgap(5);
for (int i = 0; i < 6 ; i++) {
root.getChildren().add(new TestPane());
}
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Which results in:
This plays nicely with the layout pane; resizing the window gives
I have a simple HBOX control which uses USE_COMPUTED_SIZE in Pref-Height, hence the size is all calculated and adjusted by the controls inside, which are a couple VBOX.
The issue comes when I try to add a new Pane as a children to the HBOX and draw a vertical line from top to bottom of the HBOX, so I write my line:
int startX = 5;
int startY = 0;
int endX = 5;
Line line = new Line(startX,startY,endX,hbox.getHeight());
Here, I need the hbox.getHeight(), but surprise: it is =-1, because it is using USE_COMPUTED_SIZE. So, how can I get the real (computed) value of hbox.getHeight()?
Not tested, but I think you can do something like:
public class HBoxWithLine extends HBox {
// Example of configurable property:
private final DoubleProperty lineOffset = new SimpleDoubleProperty(5);
public DoubleProperty lineOffsetProperty() {
return lineOffset ;
}
public final double getLineOffset() {
return lineOffsetProperty().get();
}
public final void setLineOffset(double lineOffset) {
lineOffsetProperty().set(lineOffset);
}
private final Line line = new Line();
public HBoxWithLine() {
getChildren().add(line);
// request layout when offset is invalidated:
lineOffset.addListener(obs -> requestLayout());
}
#Override
protected void layoutChildren() {
line.setStartX(getLineOffset());
line.setEndX(getLineOffset());
line.setStartY(0);
line.setEndY(getHeight());
super.layoutChildren();
}
}
Now you can just create a HBoxWithLine and add (additional) child nodes to it, set it's pref width and height to either fixed values, or Region.USE_COMPUTED_SIZE, etc., and it should just work.
I'm having a difficult time making the program loop moveBall and collisionDetection so that the program always checks if there is an collision and to move the balls.
At this moment the program is just checking for collision during the first iteration, after that, it doesn't do anything.
The ideal scenario is to get the balls to always sence if they're colliding or not.
I've included the whole Ball class and the code for the start-button. I've thought about doing a while loop around this little part of code but i never managed to get it working.
for (Ball b : balls) {
b.moveBall();
b.collisionDetection(balls);
}
Help & suggestions are appreciated!
Ball Class
package fx;
import java.util.ArrayList;
import javax.print.attribute.standard.MediaSize.Other;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
import javafx.util.Duration;
public class Ball {
public int id;
public Circle circle;
public int team;
public Ball(int x, int y, int _id, int _team) {
id = _id;
Circle ball = new Circle(10, Color.BLACK);
ball.relocate(x, y);
circle = ball;
team = _team;
}
public void moveBall() {
System.out.println(this.team);
//team blue
if (this.team == 0) {
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5),
new KeyValue(this.circle.layoutXProperty(),
980-((Circle)this.circle).getRadius())));
timeline.setAutoReverse(true);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}//team orange
else if (this.team == 1) {
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5),
new KeyValue(this.circle.layoutXProperty(),
35-((Circle)this.circle).getRadius())));
timeline.setAutoReverse(true);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
}
public Circle getCircle() {
return this.circle;
}
//collision detection
public void collisionDetection(ArrayList < Ball > balls) {
boolean collisionDetected = false;
for (Ball ball : balls) {
System.out.println(ball.id + " vs " + this.id);
if (ball.id == this.id) {
collisionDetected = false;
continue;
} else if (ball.id != this.id) {
Shape intersect = Shape.intersect(this.circle, ball.circle);
if (intersect.getBoundsInLocal().getWidth() != -1) {
collisionDetected = true;
}
}
}
if (collisionDetected == true) {
circle.setFill(Color.BROWN);
System.out.println("colided");
} else {
collisionDetected = false;
}
}
}
GUI Class
startBtn.setOnAction(e ->{
startBtn.setDisable(true);
// Get text
// Do a check. Gör typkontroll
int ballCount = Integer.parseInt(NrInput.getText());
Random r = new Random();
int Low = 20;
int High = 420;
int id = 0;
//System.out.println(bounds.getMaxX());
for (int i = 0; i < ballCount; i++) {
Ball ball;
// Randomize Y
int y = r.nextInt(High-Low) + Low;
//every other ball gets in different teams
if (i % 2 == 0) {
ball = new Ball(35, y, id, 0);
} else {
ball = new Ball(965, y, id, 1);
}
balls.add(ball);
canvas.getChildren().add(ball.getCircle());
id = id + 1;
}
for (Ball b : balls) {
b.moveBall();
b.collisionDetection(balls);
}
startBtn.setDisable(false);
});
The program looks like this
But the balls that i've circled with red will not change color even though they will collide.
But the balls that i've circled with red will not change color even though they will collide.
The reason is that the color doesn't change is because Ball.collisionDetection() is evaluated once, and it is evaluated when the collision hasn't occurred yet.
You need to use a binding to make sure that this is re-evaluated whenever the balls are being moved by the Timeline.
public Ball(int x, int y, int _id, int _team) {
id = _id;
Circle ball = new Circle(10, Color.BLACK);
ball.relocate(x, y);
circle = ball;
team = _team;
// New codes here
ball.fillProperty().bind(Bindings.createObjectBinding(() -> {
if (ball.checkCollision(balls)) {
return Color.BROWN;
}
else {
return Color.BLACK;
}
}, ball.layoutXProperty()));
}
// In Ball class
public boolean checkCollision(List<Ball> allBalls) {
// Your collision logic
}
You can move where you would want to put the binding at, since your collision logic probably require you to pass in a list of other Ball.
I am getting null pointer exception when I try spawning an item(Mushroom) when the player collides with a particular coin(As in MARIO game). I have been following this tutorial but I want the inputs to be buttons and not the keys. I have created 4 buttons and added to the stage . The inputs are working perfectly . But I am getting exception when,
I simply add the buttons to the stage and the input is still using keys
I am not getting exception when,
I remove all the buttons from the code and the input is using keys.
Here is my code for adding buttons to the stage ,
public class PlayScreen implements Screen, InputProcessor
{
.......
private Array<Item> items;
private LinkedBlockingQueue<ItemDef> itemsToSpawn;
private TextureAtlas atlas = new TextureAtlas("move_sprites.pack"); //contains the mushroom as well as the player packed together
public PlayScreen(MyGame game) {
.............
stage = new Stage();
atlas = new TextureAtlas("ui/button_pack.pack");
skin = new Skin(atlas);
white = new BitmapFont(Gdx.files.internal("fonts/white.fnt"), false);
black = new BitmapFont(Gdx.files.internal("fonts/black.fnt"), false);
TextButton.TextButtonStyle textButtonStyle = new TextButton.TextButtonStyle();
textButtonStyle.up = skin.getDrawable("buttonin");
textButtonStyle.down = skin.getDrawable("buttonout");
textButtonStyle.pressedOffsetX = 1;
textButtonStyle.font = black;
buttonright = new TextButton("Right", textButtonStyle);
buttonleft = new TextButton("Left", textButtonStyle);
//table.setBounds(500, 5, 250, 150);
buttonleft.setPosition(350, 5);
buttonleft.setHeight(100);
buttonleft.setWidth(100);
buttonright.setPosition(100, 5);
buttonright.setHeight(100);
buttonright.setWidth(100);
buttonup = new TextButton("up", textButtonStyle);
buttonup.setPosition(600, 5);
buttonup.setHeight(100);
buttonup.setWidth(100);
stage.addActor(buttonup);
stage.addActor(buttonright);
stage.addActor(buttonleft);
Gdx.input.setInputProcessor(stage);
......
}
//create a new method
public void spawnItem(ItemDef idef)
{
itemsToSpawn.add(idef);
}
public void handleSpawingItems()
{
if (!itemsToSpawn.isEmpty()) {
ItemDef idef = itemsToSpawn.poll(); //poll is pop
if (idef.type == Mushroom.class) {
items.add(new Mushroom(this, idef.position.x, idef.position.y));
}
}
}
public void update(float dt) {
handleInput(dt);
handleSpawingItems();
........
}
//Giving inputs using buttons
public void handleInput(float dt)
{
if (gamehero.currentState != Hero.State.DEAD) {
buttonup.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
gamehero.heroBody.setLinearVelocity(0, 4f);
}
return true;
}
}
);
}
}
public void render(float delta)
{
......
update(delta);
stage.act();
stage.draw();
}
}
}
Mushroom Class..
public class Mushroom extends Item {
public Mushroom(PlayScreen screen, float x, float y) {
super(screen, x, y);
//mush is one of the sprite packed together using texture packer. Exception is thrown in the below line
setRegion(screen.getAtlas().findRegion("mush"), 0, 0, 16, 16);
velocity = new Vector2(0.7f, 0); //velocity of mushroom
}
I am triggering this mushroom when mario head collides with coin like below,
public class Coins extends InteractiveTileObject {
....
public void onHeadHit(Hero hero) {
if (object.getProperties().containsKey("mushroom")) {
Gdx.app.log("coins", "collision");
//Exception occurs in the below line too. When the collision occurs, spawnItem method is called in the main class
screen.spawnItem(new ItemDef(new Vector2(body.getPosition().x, body.getPosition().y + 16 / MyGame.PPM), Mushroom.class));
}
ItemDef Class,
public class ItemDef {
public Vector2 position;
public Class<?> type;
public ItemDef(Vector2 position, Class<?> type)
{
this.position = position;
this.type = type;
}
}
Exception occurs if I just add a button to my code and gave the input using keys. Problem lies in spawning the mushroom if I just add a button to my code. Its weird. Please help. I am a beginner . Thanks in advance
I found the cause for this Exception.
I have given the same name for 2 TextureAtlas while declaring,
private TextureAtlas atlas = new TextureAtlas("move_sprites.pack");
and the other,
atlas = new TextureAtlas("ui/button_pack.pack");
But I am referring the first TextureAtlas in other class. Since I have declared the TextureAtlas only once but used for two different packs, It threw null pointer exception as there was name conflict. Thanks for all your answers.
In an AS3 mobile App, I would like to have a menu of buttons or icons that slides up from the bottom. Using a SlideViewTransition, I can get part of what I want.
var transition:SlideViewTransition = new SlideViewTransition();
transition.direction = ViewTransitionDirection.UP;
transition.mode = SlideViewTransitionMode.COVER;
view.navigator.pushView(ShareView, null, null, transition);
This works, but it does not do two things that I need to do.
1) I want the new transition to only go up 1/2 of the screen so that the top part of the screen displays the view underneath.
2) I want the new view that covers to be partially transparent. By setting the alpha of the incoming view's contentGroup background alpha, the new view is transparent as it comes in. But, once it covers the view underneath the view becomes opaque.
this.contentGroup.setStyle('backgroundAlpha', 0.5);
Does anyone have any ideas of how I would have a view slide up 1/2 way and be transparent? I have no idea where to start, view skinning?, or subclass transition?, or use something in flash namespace instead of a spark view.
I decided it would be simplest to just use the lower level ActionScript and do the animation myself using methods that are normally applied to a Sprite, but in this case use a VGroup so that I could add spark elements to it. The following is the base class I wrote.
public class SlideUpDialog extends VGroup {
private var pctHeight:Number;
private var stopHeight:Number;
private var vertIncrement:Number;
public function SlideUpDialog(pctHeight:Number) {
super();
this.pctHeight = pctHeight;
if (stage) {
addedToStageHandler();
} else {
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
}
private function addedToStageHandler(event:Event=null) : void {
removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
graphics.beginFill(0xEEEEEE, 0.8);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight * pctHeight);
graphics.endFill();
var bevel:BevelFilter = new BevelFilter(4, -45);
this.filters = [ bevel ];
x = 0;
y = stage.stageHeight;
stopHeight = y * (1 - pctHeight);
vertIncrement = y / 2 / 24 / 4;
}
public function open() : void {
addEventListener(Event.ENTER_FRAME, openFrameHandler);
}
private function openFrameHandler(event:Event) : void {
if (y > stopHeight) {
y -= vertIncrement;
} else {
removeEventListener(Event.ENTER_FRAME, openFrameHandler);
}
}
public function close() : void {
... the reverse of open
}
}