Codename One - how to use onTitleScrollAnimation with BorderLayout? - animation

as the Toolbar or Titlearea on scroll animation feature is referenced in the last section of the Toolbar API, and also in this great video tutorial (starting at about min 45), the animation works well under given circumstances.
I was not able to find any documentation about what these have to be, however I found one circumstance, in which it does not work. Here is a working example to demonstrate the problem:
Form hi = new Form("Title", new BoxLayout(BoxLayout.Y_AXIS));
EncodedImage placeholder = EncodedImage
.createFromImage(Image.createImage(hi.getWidth(), hi.getWidth() / 5, 0xffff0000), true);
URLImage background = URLImage.createToStorage(placeholder, "400px-AGameOfThrones.jpg",
"http://awoiaf.westeros.org/images/thumb/9/93/AGameOfThrones.jpg/400px-AGameOfThrones.jpg");
background.fetch();
Style stitle = hi.getToolbar().getTitleComponent().getUnselectedStyle();
stitle.setBgImage(background);
stitle.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stitle.setPaddingUnit(Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS);
stitle.setPaddingTop(15);
// hi.setLayout(new BorderLayout()); // uncomment this for the animation to break
Container contentContainer = new Container(BoxLayout.y());
contentContainer.setScrollableY(true);
// add some elements so we have something to scroll
for (int i = 0; i < 50; i++)
contentContainer.add(new Label("Entry " + i));
hi.add(contentContainer);
// hi.add(BorderLayout.CENTER, contentContainer); // use this line instead of the above for the animation to break
ComponentAnimation anim = hi.getToolbar().getTitleComponent().createStyleAnimation("Title", 200);
hi.getAnimationManager().onTitleScrollAnimation(anim);
hi.show();
With my current app and the codesample from the Toolbar API (which is roughly adapted here), I found out that the onScrollAnimation event is not being called, when a scroll occurs inside a BorderLayout. Even when I have a separate container, which is not the contentpane itself, and I set setScrollableY(true); to true, the animation works properly. The animation stops working, when this very container is put into Center of the Form, via Borderlayout. in the example above, the layout is exactly the same, as there are no other components in other areas of course, but it breaks the animation.
How to solve this? In my app, I have the need for a BorderLayout but still want to use this cool feature. Also, this is a very un-intuitive feature, if it works for some, but not all layouts. It should be completely layout-agnostic and work in every case.
Thank you.

The adapter is bound to the forms content pane scrolling so it won't work if you have a border layout in here. In that case scrolling isn't detected because the code just isn't aware of the scrolling. It would need to track the scrolling of any component in the UI to detect that scrolling.

as hinted by Shai, the solution is the following:
hi.setLayout(new BorderLayout());
Container contentContainer = new Container(BoxLayout.y());
contentContainer.setScrollableY(true);
// add some elements so we have something to scroll
for (int i = 0; i < 50; i++)
contentContainer.add(new Label("Entry " + i));
hi.add(BorderLayout.CENTER, contentContainer); // use this line instead of the above for the animation to break
ComponentAnimation anim = hi.getToolbar().getTitleComponent().createStyleAnimation("Title", 200);
hi.getAnimationManager().onTitleScrollAnimation(contentContainer, anim);
instead of using the onTitleScollAnimation to just add the animation, provide your own scrollable "body" or content container as the first argument, appended by the animation(s).

Related

Codename One Transition Form Automatically Scrolls to Top

I have an app with lots of components that use slide transitions.
The components, ScaleImageButtons and -Labels are added to the form directly via a GridLayout. There are also timer events involved, to make the scenario more dynamic and appealing
The form is, of course, scrollable.
Now, every time I call the form and scroll it down, it automatically jumps back to the first item at the top of the form, once that becomes animated.
But the form should stay at the current scroll position, independent of the position of the animated item, being this visible or not.
How can can make the form keep the current scroll position, independent of the position of the currently animated item?
formApps.setFocusScrolling(false); // does not help
Here is the code:
(THIS CODE WAS EDITED TO REFLECT THE WORKING SOLUTION)
Form formApps = new Form(new GridLayout(6, 2));
ArrayList<String> listApps = classStrings.listApps;
for (int i = 0; i < listApps.size(); i++) {
Container container = new Container();
ScaleImageButton scaleImageButton = new ScaleImageButton(image);
SpanButton spanButton = new SpanButton(stringDescrip);
container.add(scaleImageButton);
UITimer uit00 = new UITimer(() -> {
UITimer uit01 = new UITimer(() -> {
container.replace(spanButton, scaleImageButton,
CommonTransitions.createCover(CommonTransitions.SLIDE_HORIZONTAL, true, slideInterval));
});
uit01.schedule(startInterval, false, formApps);
UITimer uit02 = new UITimer(() -> {
container.replace(scaleImageButton, spanButton,
CommonTransitions.createCover(CommonTransitions.SLIDE_HORIZONTAL, true, slideInterval));
});
uit02.schedule(repeatIntervalOne, false, formApps);
});
uit00.schedule(repeatIntervalOne, true, formApps);
});
form.setFocusScrolling(false);
form.add(container);
container.setScrollableY(true);
formApps.show();
You shouldn't use Timer here directly. That violates the EDT. You should either wrap the timer callback in callSerially or better yet just use a UITimer which is designed exactly with this purpose in mind.
6x2 is a relatively small grid so it's possible that a small movement is enough to send you to the top as elements get resized. The size of the grid is determined by the preferred size of all the elements within. If the scaleImageButton is much larger than spanButton once they are replaced the layout might "jump" and you will end up with something much smaller which feels like scrolling but is just a new layout due to smaller preferred size.
A workaround for this would be:
Component.setSameSize(scaleImageButton, spanButton);

Storyboard animation only displays last frame

I have to create an animation for a Windows Phone 8.1 app. I want to change the ImageSource property of my Page.Background to display an animated splash when the app launches.
I used this sample as a model: http://www.codeproject.com/Tips/823622/Images-Animation-By-Using-Story-Board-in-Windows-P
In my code, the animation lasts 8 seconds for 80 images, but the screen stays black for 8 seconds, then only displays the last frame.
private void setupStoryboard()
{
storyboard = new Storyboard();
var animation = new ObjectAnimationUsingKeyFrames();
Storyboard.SetTarget(animation, BackgroundBrush);
Storyboard.SetTargetProperty(animation, "ImageSource");
storyboard.Children.Add(animation);
for (int i = 0; i <= 80; i++)
{
var keyframe = new DiscreteObjectKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(100 * i)), //Time Interval
Value = String.Format("ms-appx:///Assets/Animation/splash{0:D2}.png", i) //Source of images
};
animation.KeyFrames.Add(keyframe);
}
Resources.Add("Storyboard", storyboard); //Add story board to resources
}
This code is called from OnNavigatedTo, and I have a button to call storyboard.Begin();
This problem appears on simulator and device.
Update: The problem persists with a greater interval (1000ms), and smaller images (30KB jpg instead of 240KB png)
Update: Same problem with an <Image> tag
Update: Still not solved, but I managed to obtain the desired effect. I created a DispatcherTimer to manually change the image. Unfortunately, this creates a nasty flickering effect. I had to add a second identical image on top of the first, and alternate between the two: imageA loads source1, imageB loads source2, imageA loads source3, ImageB loads source4...
Mission completed, with duct tape.

Storyboard-Animating a Grid from CodeBehind

I need a MenuBar on the left side of my screen that can slide in and out. As this app is crossplattform it should be on the left side and not like the usual ApplicationBar on the Bottom or NavigationBar on Top.
What I have so far is:
private bool isTapped = false;
private void TEST_TAP(object sender, TappedRoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("TAP");
Storyboard s = new Storyboard();
DoubleAnimation doubleAni = new DoubleAnimation();
doubleAni.To = -30;
if (isTapped) doubleAni.To = 0;
doubleAni.Duration = new Duration(TimeSpan.FromMilliseconds(500));
Storyboard.SetTarget(doubleAni, LOGO);
Storyboard.SetTargetProperty(doubleAni, "(UIElement.RenderTransform).(TranslateTransform.XProperty)");
s.Children.Add(doubleAni);
s.Begin();
}
The bar I want to move is a Grid or should be something similar. But this even fails to move a single Image. I googled a bit and replaced "(UIElement.RenderTransform).(TranslateTransform.XProperty)" with "(Canvas.Left)". This doesn't crash but also doesn't move anything else.
Any ideas or solutions?
EDIT:
Well I played around a lot and now I know (and tested it) that "(Canvas.Left)" only works inside of a Canvas. Yay.
My App looks like this: Scrollview (horizontal) and inside is a Grid with columns to display different stuff. The left-most Column contains another Grid that I want to move out of the screen and back in again. But how do I move a grid with Storyboard-animation?
If anybody thinks of a different way to do this, I'm totally happy if it works in any way.
EDIT2:
It seems to be THE way to use Storyboard.SetTargetProperty(doubleAni, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)"); but it keeps crashing =/
Before using this transform you need to add it:
Storyboard s = new Storyboard();
DoubleAnimation doubleAni = new DoubleAnimation();
doubleAni.To = to;
doubleAni.From = from;
doubleAni.Duration = new Duration(TimeSpan.FromMilliseconds(250));
// THIS LINE IS NEW
NaviBar.RenderTransform = new CompositeTransform();
Storyboard.SetTarget(doubleAni, NaviBar);
Storyboard.SetTargetProperty(doubleAni, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)");
s.Children.Add(doubleAni);
s.Begin();
This does the trick. It's still not the functionality that I wanted but the hardest part - the animation - works now.

Is that possible to have a sliding panel?

I am developing an application in wp7, the application is already been developed in android and iPhone.
Both uses a component sliding panel as they are default in it, now for the same appearance am supposed to use the component gives the same look and feel.
Is that possible to use any combination of components OR any customized dll available for that component OR is there any alternated equivalent component available for this sliding pannel.
The sliding panel i expect is like this Video
My idea is very simple, just use popup with custom animation.
DoubleAnimation anima = new DoubleAnimation();
anima.From = //current position;
anima.To = //slider position;
//animation now proportional to pixel difference
anima.Duration = new Duration(System.TimeSpan.FromMilliseconds(Math.Abs((300))));
//Debug.WriteLine("Duration=" + TimeSpan.FromMilliseconds(Math.Abs((to - from) * 2)));
// Set attached properties
Storyboard.SetTarget(anima, m_Popup);
//Storyboard.SetTargetProperty(anima, new PropertyPath(System.Windows.Controls.Primitives.Popup.WidthProperty));
Storyboard.SetTargetProperty(anima, new PropertyPath(System.Windows.Controls.Primitives.Popup.VerticalOffsetProperty));
// Create storyboard, add animation, and fire it up!
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(anima);
storyboard.Begin();

Animate Button size, then revert to null

I am trying to create an animation to make it look like a button turns over and the back shows. So what I was trying to do is:
1- Show a button with BackgroundColor x. (The button now has a Width of null, the property ActualWidth does have a value.)
2- Create a double animation that changes the width of the button to zero.
DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.From = this.ActualWidth;
widthAnimation.To = 0;
widthAnimation.SpeedRatio = 3;
widthAnimation.Duration = TimeSpan.FromMilliseconds(800);
3- Change the BackgroundColor of the button.
ColorAnimation colorAnimation = new ColorAnimation();
colorAnimation.From = State ? _xColor : _yColor;
colorAnimation.To = State ? _yColor : _xColor;
colorAnimation.BeginTime = TimeSpan.FromMilliseconds(400);
colorAnimation.Duration = TimeSpan.Zero;
4- Change the width back to it's original value.
widthAnimation.AutoReverse = true;
The problem is when the animation runs twice the animation reads this.ActualWidth while animating, which causes it to fail to the original width. How can I solve this? I would like to set the Width back to null again, but it seems impossible to me.
You'd better use xaml style and template to "declare" what you want and let WPF/Silverlight take care of all.
If you try to do the same thing by code you can do it but you need to know what the framework does behind the scenes.
Basically you can set
- Style to define the values of some properties of the control
- DataTemplate to define the visual representation of the control's content
- ControlTemplate to define the appearance of the control
Each of those can have Triggers
- Property Triggers
to set properties or starts actions, such as an animation
when a property value changes or when an event is raised;
EventTriggers and Storyboards
to start a set of actions based on the occurrence of an event
If you like to learn about XAML Style and Template,
take a look at http://msdn.microsoft.com/en-us/library/ms745683.aspx
Spend a day to learn and save many hours (or days) of try and error and frustration!
To go right to the point, in your case I think you should use a Storyboard.
See http://msdn.microsoft.com/en-us/library/ms742868.aspx
where you can find also the code equivalent of XAML examples
I came to the idea to targetting the MaxWidth instead of the actual Width. I now use a KeyFrameCollection which sets the MaxWidth to int.MaxValue at the start (so also at the end when using autoreverse).
It will work fine untill there will be phones with a resolution bigger than the max int value.
The code:
DoubleAnimationUsingKeyFrames widthAnimation = new DoubleAnimationUsingKeyFrames();
widthAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame()
{
KeyTime = TimeSpan.Zero,
Value = int.MaxValue,
});
widthAnimation.KeyFrames.Add(new LinearDoubleKeyFrame()
{
KeyTime = TimeSpan.FromMilliseconds(1),
Value = ActualWidth,
});
widthAnimation.KeyFrames.Add(new LinearDoubleKeyFrame()
{
KeyTime = TimeSpan.FromMilliseconds(400),
Value = 0,
});
widthAnimation.Duration = TimeSpan.FromMilliseconds(400);
widthAnimation.AutoReverse = true;

Resources