Transitions

Transitions are used to execute a set of operations over a given period of time, producing an animation effect. For example, they are used to slide sheets in and out of view, fade menu items, and "slide" from one card to another in a card pane.

Because transitions are often very specific to the item on which they operate, Pivot does not include any stock transitions. However, it is very easy to create one. The following application demonstrates the use of a "collapse transition" that causes a button to shrink and fade as it is removed from its parent container:

The BXML source for the example is shown below. It simply creates a box pane containing several push buttons:

            
            <effects:Transitions title="Transitions" maximized="true"
                xmlns:bxml="http://pivot.apache.org/bxml"
                xmlns:effects="org.apache.pivot.tutorials.effects"
                xmlns="org.apache.pivot.wtk">
                <BoxPane styles="{padding:4, spacing:4}">
                    <PushButton bxml:id="button1" buttonData="One"/>
                    <PushButton bxml:id="button2" buttonData="Two"/>
                    <PushButton bxml:id="button3" buttonData="Three"/>
                    <PushButton bxml:id="button4" buttonData="Four"/>
                </BoxPane>
            </effects:Transitions>
            
        

The Java source for the application is below. It attaches a button press listener to each button that starts the collapse transition when the button is pressed (or reverses it if one is already running):

            
            package org.apache.pivot.tutorials.effects;

            import java.net.URL;

            import org.apache.pivot.beans.Bindable;
            import org.apache.pivot.collections.Map;
            import org.apache.pivot.util.Resources;
            import org.apache.pivot.wtk.Button;
            import org.apache.pivot.wtk.ButtonPressListener;
            import org.apache.pivot.wtk.Component;
            import org.apache.pivot.wtk.PushButton;
            import org.apache.pivot.wtk.Window;
            import org.apache.pivot.wtk.effects.Transition;
            import org.apache.pivot.wtk.effects.TransitionListener;

            public class Transitions extends Window implements Bindable {
                private PushButton button1 = null;
                private PushButton button2 = null;
                private PushButton button3 = null;
                private PushButton button4 = null;

                private CollapseTransition collapseTransition = null;

                public static int TRANSITION_DURATION = 250;
                public static int TRANSITION_RATE = 30;

                @Override
                public void initialize(Map<String, Object> namespace, URL location, Resources resources) {
                    button1 = (PushButton)namespace.get("button1");
                    button2 = (PushButton)namespace.get("button2");
                    button3 = (PushButton)namespace.get("button3");
                    button4 = (PushButton)namespace.get("button4");

                    ButtonPressListener buttonPressListener = new ButtonPressListener() {
                        @Override
                        public void buttonPressed(final Button button) {
                            if (collapseTransition == null) {
                                collapseTransition = new CollapseTransition(button, TRANSITION_DURATION, TRANSITION_RATE);

                                TransitionListener transitionListener = new TransitionListener() {
                                    @Override
                                    public void transitionCompleted(Transition transition) {
                                        CollapseTransition collapseTransition = (CollapseTransition)transition;

                                        if (!transition.isReversed()) {
                                            Component component = collapseTransition.getComponent();
                                            component.getParent().remove(component);
                                        }

                                        Transitions.this.collapseTransition = null;
                                    }
                                };

                                collapseTransition.start(transitionListener);
                            } else {
                                collapseTransition.reverse();

                                if (collapseTransition.getComponent() != button) {
                                    collapseTransition.end();
                                }
                            }
                        }
                    };

                    button1.getButtonPressListeners().add(buttonPressListener);
                    button2.getButtonPressListeners().add(buttonPressListener);
                    button3.getButtonPressListeners().add(buttonPressListener);
                    button4.getButtonPressListeners().add(buttonPressListener);
                }
            }
            
        

Finally, the source for the transition itself is shown below:

            
            package org.apache.pivot.tutorials.effects;

            import org.apache.pivot.wtk.Component;
            import org.apache.pivot.wtk.effects.FadeDecorator;
            import org.apache.pivot.wtk.effects.Transition;
            import org.apache.pivot.wtk.effects.TransitionListener;
            import org.apache.pivot.wtk.effects.easing.Easing;
            import org.apache.pivot.wtk.effects.easing.Quadratic;

            public class CollapseTransition extends Transition {
                private Component component;
                private int initialWidth;
                private Easing easing = new Quadratic();
                private FadeDecorator fadeDecorator = new FadeDecorator();

                public CollapseTransition(Component component, int duration, int rate) {
                    super(duration, rate, false);

                    this.component = component;
                    initialWidth = component.getWidth();
                }

                public Component getComponent() {
                    return component;
                }

                @Override
                public void start(TransitionListener transitionListener) {
                    component.getDecorators().add(fadeDecorator);

                    super.start(transitionListener);
                }

                @Override
                public void stop() {
                    component.getDecorators().remove(fadeDecorator);

                    super.stop();
                }

                @Override
                protected void update() {
                    float percentComplete = getPercentComplete();

                    if (percentComplete < 1.0f) {
                        int duration = getDuration();
                        int width = (int)(initialWidth * (1.0f - percentComplete));

                        width = (int)easing.easeInOut(getElapsedTime(), initialWidth, width - initialWidth, duration);

                        component.setPreferredWidth(width);

                        fadeDecorator.setOpacity(1.0f - percentComplete);
                        component.repaint();
                    }
                }
            }
            
        

In the transition's start() method, it attaches a fade decorator to the component, which is later used by the update() method to produce the fade part of the effect. The decorator is removed in stop().

The update() method is called periodically to update the state of the transition. In this example, it will be called 30 times per second for a duration of 250 milliseconds. Each time update() is called, the transition adjusts the opacity of the fade decorator to the inverse of the completion percentage and sets the component's preferred width to the given percentage of the component's initial width when the transition started. When the transition is complete, the opacity value will be zero, as will its preferred width. It is then removed from the parent box pane by the transition listener that had been passed to the start() method.

Next: Data Binding