Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.animation;
027
028import javafx.collections.ListChangeListener.Change;
029import javafx.collections.ObservableList;
030import javafx.util.Duration;
031
032import com.sun.javafx.collections.TrackableObservableList;
033import com.sun.scenario.animation.AbstractMasterTimer;
034import com.sun.scenario.animation.shared.TimelineClipCore;
035
036/**
037 * A {@code Timeline} can be used to define a free from animation of any
038 * {@link javafx.beans.value.WritableValue}, e.g. all
039 * {@link javafx.beans.property.Property JavaFX Properties}.
040 * <p>
041 * A {@code Timeline}, defined by one or more {@link KeyFrame}s, processes
042 * individual {@code KeyFrame} sequentially, in the order specified by
043 * {@code KeyFrame.time}. The animated properties, defined as key values in
044 * {@code KeyFrame.values}, are interpolated 
045 * to/from the targeted key values at the specified time of the {@code KeyFrame}
046 * to {@code Timeline}'s initial position, depends on {@code Timeline}'s
047 * direction.
048 * <p>
049 * {@code Timeline} processes individual {@code KeyFrame} at or after specified
050 * time interval elapsed, it does not guarantee the timing when {@code KeyFrame}
051 * is processed.
052 * <p>
053 * The {@link #cycleDurationProperty()} will be set to the largest time value
054 * of Timeline's keyFrames.
055 * <p>
056 * If a {@code KeyFrame} is not provided for the {@code time==0s} instant, one
057 * will be synthesized using the target values that are current at the time
058 * {@link #play()} or {@link #playFromStart()} is called.
059 * <p>
060 * It is not possible to change the {@code keyFrames} of a running {@code Timeline}.
061 * If the value of {@code keyFrames} is changed for a running {@code Timeline}, it 
062 * has to be stopped and started again to pick up the new value.
063 * 
064 * @see Animation
065 * @see KeyFrame
066 * @see KeyValue
067 * 
068 */
069public final class Timeline extends Animation {
070    /* Package-private for testing purposes */
071    final TimelineClipCore clipCore;
072    
073    /**
074     * Returns the {@link KeyFrame KeyFrames} of this {@code Timeline}.
075     */
076    public final ObservableList<KeyFrame> getKeyFrames() {
077        return keyFrames;
078    }
079    private final ObservableList<KeyFrame> keyFrames = new TrackableObservableList<KeyFrame>() {
080        @Override
081        protected void onChanged(Change<KeyFrame> c) {
082            while (c.next()) {
083                if (!c.wasPermutated()) {
084                    for (final KeyFrame keyFrame : c.getRemoved()) {
085                        final String cuePoint = keyFrame.getName();
086                        if (cuePoint != null) {
087                            getCuePoints().remove(cuePoint);
088                        }
089                    }
090                    for (final KeyFrame keyFrame : c.getAddedSubList()) {
091                        final String cuePoint = keyFrame.getName();
092                        if (cuePoint != null) {
093                            getCuePoints().put(cuePoint, keyFrame.getTime());
094                        }
095                    }
096                    final Duration duration = clipCore.setKeyFrames(getKeyFrames());
097                    setCycleDuration(duration);
098                }
099            }
100        }
101    };
102
103    /**
104     * The constructor of {@code Timeline}.
105     * 
106     * This constructor allows to define a {@link Animation#targetFramerate}.
107     * 
108     * @param targetFramerate
109     *            The custom target frame rate for this {@code Timeline}
110     * @param keyFrames
111     *            The keyframes of this {@code Timeline}
112     */
113    public Timeline(double targetFramerate, KeyFrame... keyFrames) {
114        super(targetFramerate);
115        clipCore = new TimelineClipCore(this);
116        getKeyFrames().setAll(keyFrames);
117    }
118
119    /**
120     * The constructor of {@code Timeline}.
121     * 
122     * @param keyFrames
123     *            The keyframes of this {@code Timeline}
124     */
125    public Timeline(KeyFrame... keyFrames) {
126        super();
127        clipCore = new TimelineClipCore(this);
128        getKeyFrames().setAll(keyFrames);
129    }
130
131    /**
132     * The constructor of {@code Timeline}.
133     * 
134     * This constructor allows to define a {@link Animation#targetFramerate}.
135     * 
136     * @param targetFramerate
137     *            The custom target frame rate for this {@code Timeline}
138     */
139    public Timeline(double targetFramerate) {
140        super(targetFramerate);
141        clipCore = new TimelineClipCore(this);
142    }
143
144    /**
145     * The constructor of {@code Timeline}.
146     */
147    public Timeline() {
148        super();
149        clipCore = new TimelineClipCore(this);
150    }
151
152    // This constructor is only for testing purposes
153    Timeline(final AbstractMasterTimer timer) {
154        super(timer);
155        clipCore = new TimelineClipCore(this);
156    }
157
158    @Override
159    void impl_playTo(long currentTicks, long cycleTicks) {
160        clipCore.playTo(currentTicks);
161    }
162
163    @Override
164    void impl_jumpTo(long currentTicks, long cycleTicks, boolean forceJump) {
165        impl_sync(false);
166        impl_setCurrentTicks(currentTicks);
167        clipCore.jumpTo(currentTicks, forceJump);
168    }
169
170    @Override
171    void impl_setCurrentRate(double currentRate) {
172        super.impl_setCurrentRate(currentRate);
173        clipCore.notifyCurrentRateChanged();
174    }
175
176    @Override
177    void impl_start(boolean forceSync) {
178        super.impl_start(forceSync);
179        clipCore.start(forceSync);
180    }
181
182    /**
183     * {@inheritDoc}
184     */
185    @Override
186    public void stop() {
187        if (parent != null) {
188            throw new IllegalStateException("Cannot stop when embedded in another animation");
189        }
190        if (getStatus() == Status.RUNNING) {
191            clipCore.abort();
192        }
193        super.stop();
194    }
195}