Bug 1255162 - Replace NineOldAndroids with system API. r=liuche

NineOldAndroids is a backport of the animation API that was introduced with Honeycomb.
Since we stopped supporting Gingerbread we do not need this library anymore and can
just use the system API.

MozReview-Commit-ID: Fv6ALlIktt5

--HG--
extra : rebase_source : 1b0b40303edb985361a23536921de38d5295604a
This commit is contained in:
Sebastian Kaspari 2016-03-10 13:38:28 +01:00
parent 58887c9d97
commit d785077dda
40 changed files with 54 additions and 9094 deletions

View File

@ -143,8 +143,8 @@ import android.widget.RelativeLayout;
import android.widget.ViewFlipper;
import com.keepsafe.switchboard.AsyncConfigLoader;
import com.keepsafe.switchboard.SwitchBoard;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import org.json.JSONException;
import org.json.JSONObject;

View File

@ -4,7 +4,7 @@
package org.mozilla.gecko.animation;
import com.nineoldandroids.animation.Animator;
import android.animation.Animator;
import java.util.ArrayList;
import java.util.Collections;

View File

@ -11,9 +11,9 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ObjectAnimator;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;

View File

@ -13,10 +13,9 @@ import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.view.ViewHelper;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import org.mozilla.gecko.Restrictions;
import org.mozilla.gecko.Telemetry;
@ -122,8 +121,8 @@ public class FirstrunPager extends ViewPager {
}
private void animateLoad() {
ViewHelper.setTranslationY(this, 500);
ViewHelper.setAlpha(this, 0);
setTranslationY(500);
setAlpha(0);
final Animator translateAnimator = ObjectAnimator.ofFloat(this, "translationY", 0);
translateAnimator.setDuration(400);

View File

@ -23,11 +23,10 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.view.ViewHelper;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
public class TabQueuePrompt extends Locales.LocaleAwareActivity {
public static final String LOGTAG = "Gecko" + TabQueuePrompt.class.getSimpleName();
@ -96,8 +95,8 @@ public class TabQueuePrompt extends Locales.LocaleAwareActivity {
buttonContainer = findViewById(R.id.button_container);
enabledConfirmation = findViewById(R.id.enabled_confirmation);
ViewHelper.setTranslationY(containerView, 500);
ViewHelper.setAlpha(containerView, 0);
containerView.setTranslationY(500);
containerView.setAlpha(0);
final Animator translateAnimator = ObjectAnimator.ofFloat(containerView, "translationY", 0);
translateAnimator.setDuration(400);
@ -124,7 +123,7 @@ public class TabQueuePrompt extends Locales.LocaleAwareActivity {
private void onConfirmButtonPressed() {
enabledConfirmation.setVisibility(View.VISIBLE);
ViewHelper.setAlpha(enabledConfirmation, 0);
enabledConfirmation.setAlpha(0);
final Animator buttonsAlphaAnimator = ObjectAnimator.ofFloat(buttonContainer, "alpha", 0);
buttonsAlphaAnimator.setDuration(300);

View File

@ -18,10 +18,10 @@ import android.view.animation.DecelerateInterpolator;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.Animator.AnimatorListener;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import java.util.ArrayList;
import java.util.List;

View File

@ -34,12 +34,11 @@ import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.GridView;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.animation.PropertyValuesHolder;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.view.ViewHelper;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import java.util.ArrayList;
import java.util.List;
@ -316,9 +315,9 @@ class TabsGridLayout extends GridView
}
private void resetTransforms(View view) {
ViewHelper.setAlpha(view, 1);
ViewHelper.setTranslationX(view, 0);
ViewHelper.setTranslationY(view, 0);
view.setAlpha(1);
view.setTranslationX(0);
view.setTranslationY(0);
((TabsLayoutItemView) view).setCloseVisible(true);
}
@ -591,7 +590,7 @@ class TabsGridLayout extends GridView
boolean dismiss = false;
float deltaX = ViewHelper.getTranslationX(mSwipeView);
float deltaX = mSwipeView.getTranslationX();
if (Math.abs(deltaX) > mTabWidth / 2) {
dismiss = true;
@ -645,10 +644,9 @@ class TabsGridLayout extends GridView
}
if (mSwiping) {
ViewHelper.setTranslationX(mSwipeView, delta);
mSwipeView.setTranslationX(delta);
ViewHelper.setAlpha(mSwipeView, Math.min(1f,
1f - 2f * Math.abs(delta) / mTabWidth));
mSwipeView.setAlpha(Math.min(1f, 1f - 2f * Math.abs(delta) / mTabWidth));
return true;
}

View File

@ -15,11 +15,10 @@ import android.content.Intent;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.view.ViewHelper;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
public class TrackingProtectionPrompt extends Locales.LocaleAwareActivity {
public static final String LOGTAG = "Gecko" + TrackingProtectionPrompt.class.getSimpleName();
@ -63,8 +62,8 @@ public class TrackingProtectionPrompt extends Locales.LocaleAwareActivity {
containerView = findViewById(R.id.tracking_protection_inner_container);
ViewHelper.setTranslationY(containerView, 500);
ViewHelper.setAlpha(containerView, 0);
containerView.setTranslationY(500);
containerView.setAlpha(0);
final Animator translateAnimator = ObjectAnimator.ofFloat(containerView, "translationY", 0);
translateAnimator.setDuration(400);

View File

@ -12,7 +12,6 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import com.nineoldandroids.view.ViewHelper;
import org.mozilla.gecko.widget.themed.ThemedImageView;
/**
@ -54,8 +53,8 @@ public abstract class CropImageView extends ThemedImageView {
// Setting the pivots means that the image will be drawn from the top left hand corner. There are
// issues in Android 4.1 (16) which mean setting these values to 0 may not work.
// http://stackoverflow.com/questions/26658124/setpivotx-doesnt-work-on-android-4-1-1-nineoldandroids
ViewHelper.setPivotX(this, 1);
ViewHelper.setPivotY(this, 1);
setPivotX(1);
setPivotY(1);
}
/**

View File

@ -26,11 +26,10 @@ import android.widget.AbsListView;
import android.widget.AbsListView.RecyclerListener;
import android.widget.ListView;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.view.ViewHelper;
import com.nineoldandroids.view.ViewPropertyAnimator;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.view.ViewPropertyAnimator;
import org.mozilla.gecko.R;
@ -173,8 +172,8 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
// To reset the view to the correct height after its animation, the view's height
// is stored in its tag. Reset the view here.
if (tag instanceof Integer) {
ViewHelper.setAlpha(view, 1f);
ViewHelper.setTranslationX(view, 0);
view.setAlpha(1f);
view.setTranslationX(0);
final ViewGroup.LayoutParams lp = view.getLayoutParams();
lp.height = (int) tag;
view.setLayoutParams(lp);
@ -255,7 +254,7 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
mDismissing = true;
final View downView = mDownView; // mDownView gets null'd before animation ends
final int downPosition = mDownPosition;
ViewPropertyAnimator.animate(mDownView)
mDownView.animate()
.translationX(dismissRight ? mViewWidth : -mViewWidth)
.alpha(0)
.setDuration(mAnimationTime)
@ -267,7 +266,7 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
});
} else {
// cancel
ViewPropertyAnimator.animate(mDownView)
mDownView.animate()
.translationX(0)
.alpha(1)
.setDuration(mAnimationTime)
@ -307,9 +306,8 @@ public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
}
if (mSwiping) {
ViewHelper.setTranslationX(mDownView, deltaX);
ViewHelper.setAlpha(mDownView, Math.max(0f, Math.min(1f,
1f - 2f * Math.abs(deltaX) / mViewWidth)));
mDownView.setTranslationX(deltaX);
mDownView.setAlpha(Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth)));
return true;
}
break;

View File

@ -799,34 +799,6 @@ gtjar.sources += [ thirdparty_source_dir + f for f in [
'com/keepsafe/switchboard/Preferences.java',
'com/keepsafe/switchboard/Switch.java',
'com/keepsafe/switchboard/SwitchBoard.java',
'com/nineoldandroids/animation/Animator.java',
'com/nineoldandroids/animation/AnimatorInflater.java',
'com/nineoldandroids/animation/AnimatorListenerAdapter.java',
'com/nineoldandroids/animation/AnimatorSet.java',
'com/nineoldandroids/animation/ArgbEvaluator.java',
'com/nineoldandroids/animation/FloatEvaluator.java',
'com/nineoldandroids/animation/FloatKeyframeSet.java',
'com/nineoldandroids/animation/IntEvaluator.java',
'com/nineoldandroids/animation/IntKeyframeSet.java',
'com/nineoldandroids/animation/Keyframe.java',
'com/nineoldandroids/animation/KeyframeSet.java',
'com/nineoldandroids/animation/ObjectAnimator.java',
'com/nineoldandroids/animation/PreHoneycombCompat.java',
'com/nineoldandroids/animation/PropertyValuesHolder.java',
'com/nineoldandroids/animation/TimeAnimator.java',
'com/nineoldandroids/animation/TypeEvaluator.java',
'com/nineoldandroids/animation/ValueAnimator.java',
'com/nineoldandroids/util/FloatProperty.java',
'com/nineoldandroids/util/IntProperty.java',
'com/nineoldandroids/util/NoSuchPropertyException.java',
'com/nineoldandroids/util/Property.java',
'com/nineoldandroids/util/ReflectiveProperty.java',
'com/nineoldandroids/view/animation/AnimatorProxy.java',
'com/nineoldandroids/view/ViewHelper.java',
'com/nineoldandroids/view/ViewPropertyAnimator.java',
'com/nineoldandroids/view/ViewPropertyAnimatorHC.java',
'com/nineoldandroids/view/ViewPropertyAnimatorICS.java',
'com/nineoldandroids/view/ViewPropertyAnimatorPreHC.java',
'com/squareup/leakcanary/LeakCanary.java',
'com/squareup/leakcanary/RefWatcher.java',
'com/squareup/picasso/Action.java',

View File

@ -29,9 +29,9 @@ import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
/**
* The main entrance for the Android search intent.

View File

@ -1,278 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
import java.util.ArrayList;
import android.view.animation.Interpolator;
/**
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
public abstract class Animator implements Cloneable {
/**
* The set of listeners to be sent events through the life of an animation.
*/
ArrayList<AnimatorListener> mListeners = null;
/**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
* value(s) set immediately, followed by calls to
* {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator.
*
* <p>The animation started by calling this method will be run on the thread that called
* this method. This thread should have a Looper on it (a runtime exception will be thrown if
* this is not the case). Also, if the animation will animate
* properties of objects in the view hierarchy, then the calling thread should be the UI
* thread for that view hierarchy.</p>
*
*/
public void start() {
}
/**
* Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
* stop in its tracks, sending an
* {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
* its listeners, followed by an
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
*
* <p>This method must be called on the thread that is running the animation.</p>
*/
public void cancel() {
}
/**
* Ends the animation. This causes the animation to assign the end value of the property being
* animated, then calling the
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
* its listeners.
*
* <p>This method must be called on the thread that is running the animation.</p>
*/
public void end() {
}
/**
* The amount of time, in milliseconds, to delay starting the animation after
* {@link #start()} is called.
*
* @return the number of milliseconds to delay running the animation
*/
public abstract long getStartDelay();
/**
* The amount of time, in milliseconds, to delay starting the animation after
* {@link #start()} is called.
* @param startDelay The amount of the delay, in milliseconds
*/
public abstract void setStartDelay(long startDelay);
/**
* Sets the length of the animation.
*
* @param duration The length of the animation, in milliseconds.
*/
public abstract Animator setDuration(long duration);
/**
* Gets the length of the animation.
*
* @return The length of the animation, in milliseconds.
*/
public abstract long getDuration();
/**
* The time interpolator used in calculating the elapsed fraction of this animation. The
* interpolator determines whether the animation runs with linear or non-linear motion,
* such as acceleration and deceleration. The default value is
* {@link android.view.animation.AccelerateDecelerateInterpolator}
*
* @param value the interpolator to be used by this animation
*/
public abstract void setInterpolator(/*Time*/Interpolator value);
/**
* Returns whether this Animator is currently running (having been started and gone past any
* initial startDelay period and not yet ended).
*
* @return Whether the Animator is running.
*/
public abstract boolean isRunning();
/**
* Returns whether this Animator has been started and not yet ended. This state is a superset
* of the state of {@link #isRunning()}, because an Animator with a nonzero
* {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during the
* delay phase, whereas {@link #isRunning()} will return true only after the delay phase
* is complete.
*
* @return Whether the Animator has been started and not yet ended.
*/
public boolean isStarted() {
// Default method returns value for isRunning(). Subclasses should override to return a
// real value.
return isRunning();
}
/**
* Adds a listener to the set of listeners that are sent events through the life of an
* animation, such as start, repeat, and end.
*
* @param listener the listener to be added to the current set of listeners for this animation.
*/
public void addListener(AnimatorListener listener) {
if (mListeners == null) {
mListeners = new ArrayList<AnimatorListener>();
}
mListeners.add(listener);
}
/**
* Removes a listener from the set listening to this animation.
*
* @param listener the listener to be removed from the current set of listeners for this
* animation.
*/
public void removeListener(AnimatorListener listener) {
if (mListeners == null) {
return;
}
mListeners.remove(listener);
if (mListeners.size() == 0) {
mListeners = null;
}
}
/**
* Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
* listening for events on this <code>Animator</code> object.
*
* @return ArrayList<AnimatorListener> The set of listeners.
*/
public ArrayList<AnimatorListener> getListeners() {
return mListeners;
}
/**
* Removes all listeners from this object. This is equivalent to calling
* <code>getListeners()</code> followed by calling <code>clear()</code> on the
* returned list of listeners.
*/
public void removeAllListeners() {
if (mListeners != null) {
mListeners.clear();
mListeners = null;
}
}
@Override
public Animator clone() {
try {
final Animator anim = (Animator) super.clone();
if (mListeners != null) {
ArrayList<AnimatorListener> oldListeners = mListeners;
anim.mListeners = new ArrayList<AnimatorListener>();
int numListeners = oldListeners.size();
for (int i = 0; i < numListeners; ++i) {
anim.mListeners.add(oldListeners.get(i));
}
}
return anim;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
/**
* This method tells the object to use appropriate information to extract
* starting values for the animation. For example, a AnimatorSet object will pass
* this call to its child objects to tell them to set up the values. A
* ObjectAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
* An ValueAnimator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupStartValues() {
}
/**
* This method tells the object to use appropriate information to extract
* ending values for the animation. For example, a AnimatorSet object will pass
* this call to its child objects to tell them to set up the values. A
* ObjectAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
* An ValueAnimator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupEndValues() {
}
/**
* Sets the target object whose property will be animated by this animation. Not all subclasses
* operate on target objects (for example, {@link ValueAnimator}, but this method
* is on the superclass for the convenience of dealing generically with those subclasses
* that do handle targets.
*
* @param target The object being animated
*/
public void setTarget(Object target) {
}
/**
* <p>An animation listener receives notifications from an animation.
* Notifications indicate animation related events, such as the end or the
* repetition of the animation.</p>
*/
public static interface AnimatorListener {
/**
* <p>Notifies the start of the animation.</p>
*
* @param animation The started animation.
*/
void onAnimationStart(Animator animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd(Animator animation);
/**
* <p>Notifies the cancellation of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which was canceled.
*/
void onAnimationCancel(Animator animation);
/**
* <p>Notifies the repetition of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationRepeat(Animator animation);
}
}

View File

@ -1,344 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.content.res.Resources.NotFoundException;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.util.Xml;
import android.view.animation.AnimationUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
/**
* This class is used to instantiate animator XML files into Animator objects.
* <p>
* For performance reasons, inflation relies heavily on pre-processing of
* XML files that is done at build time. Therefore, it is not currently possible
* to use this inflater with an XmlPullParser over a plain XML file at runtime;
* it only works with an XmlPullParser returned from a compiled resource (R.
* <em>something</em> file.)
*/
public class AnimatorInflater {
private static final int[] AnimatorSet = new int[] {
/* 0 */ android.R.attr.ordering,
};
private static final int AnimatorSet_ordering = 0;
private static final int[] PropertyAnimator = new int[] {
/* 0 */ android.R.attr.propertyName,
};
private static final int PropertyAnimator_propertyName = 0;
private static final int[] Animator = new int[] {
/* 0 */ android.R.attr.interpolator,
/* 1 */ android.R.attr.duration,
/* 2 */ android.R.attr.startOffset,
/* 3 */ android.R.attr.repeatCount,
/* 4 */ android.R.attr.repeatMode,
/* 5 */ android.R.attr.valueFrom,
/* 6 */ android.R.attr.valueTo,
/* 7 */ android.R.attr.valueType,
};
private static final int Animator_interpolator = 0;
private static final int Animator_duration = 1;
private static final int Animator_startOffset = 2;
private static final int Animator_repeatCount = 3;
private static final int Animator_repeatMode = 4;
private static final int Animator_valueFrom = 5;
private static final int Animator_valueTo = 6;
private static final int Animator_valueType = 7;
/**
* These flags are used when parsing AnimatorSet objects
*/
private static final int TOGETHER = 0;
//private static final int SEQUENTIALLY = 1;
/**
* Enum values used in XML attributes to indicate the value for mValueType
*/
private static final int VALUE_TYPE_FLOAT = 0;
//private static final int VALUE_TYPE_INT = 1;
//private static final int VALUE_TYPE_COLOR = 4;
//private static final int VALUE_TYPE_CUSTOM = 5;
/**
* Loads an {@link Animator} object from a resource
*
* @param context Application context used to access resources
* @param id The resource id of the animation to load
* @return The animator object reference by the specified id
* @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
*/
public static Animator loadAnimator(Context context, int id)
throws NotFoundException {
XmlResourceParser parser = null;
try {
parser = context.getResources().getAnimation(id);
return createAnimatorFromXml(context, parser);
} catch (XmlPullParserException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} catch (IOException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} finally {
if (parser != null) parser.close();
}
}
private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
throws XmlPullParserException, IOException {
return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
}
private static Animator createAnimatorFromXml(Context c, XmlPullParser parser,
AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
throws XmlPullParserException, IOException {
Animator anim = null;
ArrayList<Animator> childAnims = null;
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equals("objectAnimator")) {
anim = loadObjectAnimator(c, attrs);
} else if (name.equals("animator")) {
anim = loadAnimator(c, attrs, null);
} else if (name.equals("set")) {
anim = new AnimatorSet();
TypedArray a = c.obtainStyledAttributes(attrs,
/*com.android.internal.R.styleable.*/AnimatorSet);
TypedValue orderingValue = new TypedValue();
a.getValue(/*com.android.internal.R.styleable.*/AnimatorSet_ordering, orderingValue);
int ordering = orderingValue.type == TypedValue.TYPE_INT_DEC ? orderingValue.data : TOGETHER;
createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim, ordering);
a.recycle();
} else {
throw new RuntimeException("Unknown animator name: " + parser.getName());
}
if (parent != null) {
if (childAnims == null) {
childAnims = new ArrayList<Animator>();
}
childAnims.add(anim);
}
}
if (parent != null && childAnims != null) {
Animator[] animsArray = new Animator[childAnims.size()];
int index = 0;
for (Animator a : childAnims) {
animsArray[index++] = a;
}
if (sequenceOrdering == TOGETHER) {
parent.playTogether(animsArray);
} else {
parent.playSequentially(animsArray);
}
}
return anim;
}
private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs)
throws NotFoundException {
ObjectAnimator anim = new ObjectAnimator();
loadAnimator(context, attrs, anim);
TypedArray a =
context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/PropertyAnimator);
String propertyName = a.getString(/*com.android.internal.R.styleable.*/PropertyAnimator_propertyName);
anim.setPropertyName(propertyName);
a.recycle();
return anim;
}
/**
* Creates a new animation whose parameters come from the specified context and
* attributes set.
*
* @param context the application environment
* @param attrs the set of attributes holding the animation parameters
*/
private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim)
throws NotFoundException {
TypedArray a =
context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/Animator);
long duration = a.getInt(/*com.android.internal.R.styleable.*/Animator_duration, 0);
long startDelay = a.getInt(/*com.android.internal.R.styleable.*/Animator_startOffset, 0);
int valueType = a.getInt(/*com.android.internal.R.styleable.*/Animator_valueType,
VALUE_TYPE_FLOAT);
if (anim == null) {
anim = new ValueAnimator();
}
//TypeEvaluator evaluator = null;
int valueFromIndex = /*com.android.internal.R.styleable.*/Animator_valueFrom;
int valueToIndex = /*com.android.internal.R.styleable.*/Animator_valueTo;
boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
TypedValue tvFrom = a.peekValue(valueFromIndex);
boolean hasFrom = (tvFrom != null);
int fromType = hasFrom ? tvFrom.type : 0;
TypedValue tvTo = a.peekValue(valueToIndex);
boolean hasTo = (tvTo != null);
int toType = hasTo ? tvTo.type : 0;
if ((hasFrom && (fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(fromType <= TypedValue.TYPE_LAST_COLOR_INT)) ||
(hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT))) {
// special case for colors: ignore valueType and get ints
getFloats = false;
anim.setEvaluator(new ArgbEvaluator());
}
if (getFloats) {
float valueFrom;
float valueTo;
if (hasFrom) {
if (fromType == TypedValue.TYPE_DIMENSION) {
valueFrom = a.getDimension(valueFromIndex, 0f);
} else {
valueFrom = a.getFloat(valueFromIndex, 0f);
}
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = a.getDimension(valueToIndex, 0f);
} else {
valueTo = a.getFloat(valueToIndex, 0f);
}
anim.setFloatValues(valueFrom, valueTo);
} else {
anim.setFloatValues(valueFrom);
}
} else {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = a.getDimension(valueToIndex, 0f);
} else {
valueTo = a.getFloat(valueToIndex, 0f);
}
anim.setFloatValues(valueTo);
}
} else {
int valueFrom;
int valueTo;
if (hasFrom) {
if (fromType == TypedValue.TYPE_DIMENSION) {
valueFrom = (int) a.getDimension(valueFromIndex, 0f);
} else if ((fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(fromType <= TypedValue.TYPE_LAST_COLOR_INT)) {
valueFrom = a.getColor(valueFromIndex, 0);
} else {
valueFrom = a.getInt(valueFromIndex, 0);
}
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = (int) a.getDimension(valueToIndex, 0f);
} else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
valueTo = a.getColor(valueToIndex, 0);
} else {
valueTo = a.getInt(valueToIndex, 0);
}
anim.setIntValues(valueFrom, valueTo);
} else {
anim.setIntValues(valueFrom);
}
} else {
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = (int) a.getDimension(valueToIndex, 0f);
} else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
valueTo = a.getColor(valueToIndex, 0);
} else {
valueTo = a.getInt(valueToIndex, 0);
}
anim.setIntValues(valueTo);
}
}
}
anim.setDuration(duration);
anim.setStartDelay(startDelay);
if (a.hasValue(/*com.android.internal.R.styleable.*/Animator_repeatCount)) {
anim.setRepeatCount(
a.getInt(/*com.android.internal.R.styleable.*/Animator_repeatCount, 0));
}
if (a.hasValue(/*com.android.internal.R.styleable.*/Animator_repeatMode)) {
anim.setRepeatMode(
a.getInt(/*com.android.internal.R.styleable.*/Animator_repeatMode,
ValueAnimator.RESTART));
}
//if (evaluator != null) {
// anim.setEvaluator(evaluator);
//}
final int resID =
a.getResourceId(/*com.android.internal.R.styleable.*/Animator_interpolator, 0);
if (resID > 0) {
anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID));
}
a.recycle();
return anim;
}
}

View File

@ -1,54 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
/**
* This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}.
* Any custom listener that cares only about a subset of the methods of this listener can
* simply subclass this adapter class instead of implementing the interface directly.
*/
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener {
/**
* {@inheritDoc}
*/
@Override
public void onAnimationCancel(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationEnd(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationRepeat(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationStart(Animator animation) {
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,59 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
/**
* This evaluator can be used to perform type interpolation between integer
* values that represent ARGB colors.
*/
public class ArgbEvaluator implements TypeEvaluator {
/**
* This function returns the calculated in-between value for a color
* given integers that represent the start and end values in the four
* bytes of the 32-bit int. Each channel is separately linearly interpolated
* and the resulting calculated values are recombined into the return value.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @param endValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @return A value that is calculated to be the linearly interpolated
* result, derived by separating the start and end values into separate
* color channels and interpolating each one separately, recombining the
* resulting values in the same way.
*/
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24);
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24);
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
(int)((startR + (int)(fraction * (endR - startR))) << 16) |
(int)((startG + (int)(fraction * (endG - startG))) << 8) |
(int)((startB + (int)(fraction * (endB - startB))));
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
/**
* This evaluator can be used to perform type interpolation between <code>float</code> values.
*/
public class FloatEvaluator implements TypeEvaluator<Number> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>float</code> or
* <code>Float</code>
* @param endValue The end value; should be of type <code>float</code> or <code>Float</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}

View File

@ -1,136 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
import android.view.animation.Interpolator;
import com.nineoldandroids.animation.Keyframe.FloatKeyframe;
import java.util.ArrayList;
/**
* This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
*
* <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclass for
* int, exists to speed up the getValue() method when there is no custom
* TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
* Object equivalents of these primitive types.</p>
*/
class FloatKeyframeSet extends KeyframeSet {
private float firstValue;
private float lastValue;
private float deltaValue;
private boolean firstTime = true;
public FloatKeyframeSet(FloatKeyframe... keyframes) {
super(keyframes);
}
@Override
public Object getValue(float fraction) {
return getFloatValue(fraction);
}
@Override
public FloatKeyframeSet clone() {
ArrayList<Keyframe> keyframes = mKeyframes;
int numKeyframes = mKeyframes.size();
FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone();
}
FloatKeyframeSet newSet = new FloatKeyframeSet(newKeyframes);
return newSet;
}
public float getFloatValue(float fraction) {
if (mNumKeyframes == 2) {
if (firstTime) {
firstTime = false;
firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
deltaValue = lastValue - firstValue;
}
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
if (mEvaluator == null) {
return firstValue + fraction * deltaValue;
} else {
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
}
}
if (fraction <= 0f) {
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
} else if (fraction >= 1f) {
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
}
FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
for (int i = 1; i < mNumKeyframes; ++i) {
FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
}
prevKeyframe = nextKeyframe;
}
// shouldn't get here
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
/**
* This evaluator can be used to perform type interpolation between <code>int</code> values.
*/
public class IntEvaluator implements TypeEvaluator<Integer> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>int</code> or
* <code>Integer</code>
* @param endValue The end value; should be of type <code>int</code> or <code>Integer</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}

View File

@ -1,135 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
import android.view.animation.Interpolator;
import com.nineoldandroids.animation.Keyframe.IntKeyframe;
import java.util.ArrayList;
/**
* This class holds a collection of IntKeyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
*
* <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclass for
* float, exists to speed up the getValue() method when there is no custom
* TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
* Object equivalents of these primitive types.</p>
*/
class IntKeyframeSet extends KeyframeSet {
private int firstValue;
private int lastValue;
private int deltaValue;
private boolean firstTime = true;
public IntKeyframeSet(IntKeyframe... keyframes) {
super(keyframes);
}
@Override
public Object getValue(float fraction) {
return getIntValue(fraction);
}
@Override
public IntKeyframeSet clone() {
ArrayList<Keyframe> keyframes = mKeyframes;
int numKeyframes = mKeyframes.size();
IntKeyframe[] newKeyframes = new IntKeyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
newKeyframes[i] = (IntKeyframe) keyframes.get(i).clone();
}
IntKeyframeSet newSet = new IntKeyframeSet(newKeyframes);
return newSet;
}
public int getIntValue(float fraction) {
if (mNumKeyframes == 2) {
if (firstTime) {
firstTime = false;
firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();
lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();
deltaValue = lastValue - firstValue;
}
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
if (mEvaluator == null) {
return firstValue + (int)(fraction * deltaValue);
} else {
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
}
}
if (fraction <= 0f) {
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1);
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
intValue();
} else if (fraction >= 1f) {
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2);
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1);
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
}
IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
for (int i = 1; i < mNumKeyframes; ++i) {
IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
return mEvaluator == null ?
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
intValue();
}
prevKeyframe = nextKeyframe;
}
// shouldn't get here
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue();
}
}

View File

@ -1,360 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
import android.view.animation.Interpolator;
/**
* This class holds a time/value pair for an animation. The Keyframe class is used
* by {@link ValueAnimator} to define the values that the animation target will have over the course
* of the animation. As the time proceeds from one keyframe to the other, the value of the
* target object will animate between the value at the previous keyframe and the value at the
* next keyframe. Each keyframe also holds an optional {@link TimeInterpolator}
* object, which defines the time interpolation over the intervalue preceding the keyframe.
*
* <p>The Keyframe class itself is abstract. The type-specific factory methods will return
* a subclass of Keyframe specific to the type of value being stored. This is done to improve
* performance when dealing with the most common cases (e.g., <code>float</code> and
* <code>int</code> values). Other types will fall into a more general Keyframe class that
* treats its values as Objects. Unless your animation requires dealing with a custom type
* or a data structure that needs to be animated directly (and evaluated using an implementation
* of {@link TypeEvaluator}), you should stick to using float and int as animations using those
* types have lower runtime overhead than other types.</p>
*/
public abstract class Keyframe implements Cloneable {
/**
* The time at which mValue will hold true.
*/
float mFraction;
/**
* The type of the value in this Keyframe. This type is determined at construction time,
* based on the type of the <code>value</code> object passed into the constructor.
*/
Class mValueType;
/**
* The optional time interpolator for the interval preceding this keyframe. A null interpolator
* (the default) results in linear interpolation over the interval.
*/
private /*Time*/Interpolator mInterpolator = null;
/**
* Flag to indicate whether this keyframe has a valid value. This flag is used when an
* animation first starts, to populate placeholder keyframes with real values derived
* from the target object.
*/
boolean mHasValue = false;
/**
* Constructs a Keyframe object with the given time and value. The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
* @param value The value that the object will animate to as the animation time approaches
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
public static Keyframe ofInt(float fraction, int value) {
return new IntKeyframe(fraction, value);
}
/**
* Constructs a Keyframe object with the given time. The value at this time will be derived
* from the target object when the animation first starts (note that this implies that keyframes
* with no initial value must be used as part of an {@link ObjectAnimator}).
* The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
*/
public static Keyframe ofInt(float fraction) {
return new IntKeyframe(fraction);
}
/**
* Constructs a Keyframe object with the given time and value. The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
* @param value The value that the object will animate to as the animation time approaches
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
public static Keyframe ofFloat(float fraction, float value) {
return new FloatKeyframe(fraction, value);
}
/**
* Constructs a Keyframe object with the given time. The value at this time will be derived
* from the target object when the animation first starts (note that this implies that keyframes
* with no initial value must be used as part of an {@link ObjectAnimator}).
* The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
*/
public static Keyframe ofFloat(float fraction) {
return new FloatKeyframe(fraction);
}
/**
* Constructs a Keyframe object with the given time and value. The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
* @param value The value that the object will animate to as the animation time approaches
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
public static Keyframe ofObject(float fraction, Object value) {
return new ObjectKeyframe(fraction, value);
}
/**
* Constructs a Keyframe object with the given time. The value at this time will be derived
* from the target object when the animation first starts (note that this implies that keyframes
* with no initial value must be used as part of an {@link ObjectAnimator}).
* The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
*/
public static Keyframe ofObject(float fraction) {
return new ObjectKeyframe(fraction, null);
}
/**
* Indicates whether this keyframe has a valid value. This method is called internally when
* an {@link ObjectAnimator} first starts; keyframes without values are assigned values at
* that time by deriving the value for the property from the target object.
*
* @return boolean Whether this object has a value assigned.
*/
public boolean hasValue() {
return mHasValue;
}
/**
* Gets the value for this Keyframe.
*
* @return The value for this Keyframe.
*/
public abstract Object getValue();
/**
* Sets the value for this Keyframe.
*
* @param value value for this Keyframe.
*/
public abstract void setValue(Object value);
/**
* Gets the time for this keyframe, as a fraction of the overall animation duration.
*
* @return The time associated with this keyframe, as a fraction of the overall animation
* duration. This should be a value between 0 and 1.
*/
public float getFraction() {
return mFraction;
}
/**
* Sets the time for this keyframe, as a fraction of the overall animation duration.
*
* @param fraction time associated with this keyframe, as a fraction of the overall animation
* duration. This should be a value between 0 and 1.
*/
public void setFraction(float fraction) {
mFraction = fraction;
}
/**
* Gets the optional interpolator for this Keyframe. A value of <code>null</code> indicates
* that there is no interpolation, which is the same as linear interpolation.
*
* @return The optional interpolator for this Keyframe.
*/
public /*Time*/Interpolator getInterpolator() {
return mInterpolator;
}
/**
* Sets the optional interpolator for this Keyframe. A value of <code>null</code> indicates
* that there is no interpolation, which is the same as linear interpolation.
*
* @return The optional interpolator for this Keyframe.
*/
public void setInterpolator(/*Time*/Interpolator interpolator) {
mInterpolator = interpolator;
}
/**
* Gets the type of keyframe. This information is used by ValueAnimator to determine the type of
* {@link TypeEvaluator} to use when calculating values between keyframes. The type is based
* on the type of Keyframe created.
*
* @return The type of the value stored in the Keyframe.
*/
public Class getType() {
return mValueType;
}
@Override
public abstract Keyframe clone();
/**
* This internal subclass is used for all types which are not int or float.
*/
static class ObjectKeyframe extends Keyframe {
/**
* The value of the animation at the time mFraction.
*/
Object mValue;
ObjectKeyframe(float fraction, Object value) {
mFraction = fraction;
mValue = value;
mHasValue = (value != null);
mValueType = mHasValue ? value.getClass() : Object.class;
}
public Object getValue() {
return mValue;
}
public void setValue(Object value) {
mValue = value;
mHasValue = (value != null);
}
@Override
public ObjectKeyframe clone() {
ObjectKeyframe kfClone = new ObjectKeyframe(getFraction(), mValue);
kfClone.setInterpolator(getInterpolator());
return kfClone;
}
}
/**
* Internal subclass used when the keyframe value is of type int.
*/
static class IntKeyframe extends Keyframe {
/**
* The value of the animation at the time mFraction.
*/
int mValue;
IntKeyframe(float fraction, int value) {
mFraction = fraction;
mValue = value;
mValueType = int.class;
mHasValue = true;
}
IntKeyframe(float fraction) {
mFraction = fraction;
mValueType = int.class;
}
public int getIntValue() {
return mValue;
}
public Object getValue() {
return mValue;
}
public void setValue(Object value) {
if (value != null && value.getClass() == Integer.class) {
mValue = ((Integer)value).intValue();
mHasValue = true;
}
}
@Override
public IntKeyframe clone() {
IntKeyframe kfClone = new IntKeyframe(getFraction(), mValue);
kfClone.setInterpolator(getInterpolator());
return kfClone;
}
}
/**
* Internal subclass used when the keyframe value is of type float.
*/
static class FloatKeyframe extends Keyframe {
/**
* The value of the animation at the time mFraction.
*/
float mValue;
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
FloatKeyframe(float fraction) {
mFraction = fraction;
mValueType = float.class;
}
public float getFloatValue() {
return mValue;
}
public Object getValue() {
return mValue;
}
public void setValue(Object value) {
if (value != null && value.getClass() == Float.class) {
mValue = ((Float)value).floatValue();
mHasValue = true;
}
}
@Override
public FloatKeyframe clone() {
FloatKeyframe kfClone = new FloatKeyframe(getFraction(), mValue);
kfClone.setInterpolator(getInterpolator());
return kfClone;
}
}
}

View File

@ -1,227 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
import java.util.ArrayList;
import java.util.Arrays;
import android.view.animation.Interpolator;
import com.nineoldandroids.animation.Keyframe.FloatKeyframe;
import com.nineoldandroids.animation.Keyframe.IntKeyframe;
import com.nineoldandroids.animation.Keyframe.ObjectKeyframe;
/**
* This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
*/
class KeyframeSet {
int mNumKeyframes;
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
/*Time*/Interpolator mInterpolator; // only used in the 2-keyframe case
ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
TypeEvaluator mEvaluator;
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = new ArrayList<Keyframe>();
mKeyframes.addAll(Arrays.asList(keyframes));
mFirstKeyframe = mKeyframes.get(0);
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
mInterpolator = mLastKeyframe.getInterpolator();
}
public static KeyframeSet ofInt(int... values) {
int numKeyframes = values.length;
IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
} else {
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
}
}
return new IntKeyframeSet(keyframes);
}
public static KeyframeSet ofFloat(float... values) {
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
} else {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
}
}
return new FloatKeyframeSet(keyframes);
}
public static KeyframeSet ofKeyframe(Keyframe... keyframes) {
// if all keyframes of same primitive type, create the appropriate KeyframeSet
int numKeyframes = keyframes.length;
boolean hasFloat = false;
boolean hasInt = false;
boolean hasOther = false;
for (int i = 0; i < numKeyframes; ++i) {
if (keyframes[i] instanceof FloatKeyframe) {
hasFloat = true;
} else if (keyframes[i] instanceof IntKeyframe) {
hasInt = true;
} else {
hasOther = true;
}
}
if (hasFloat && !hasInt && !hasOther) {
FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
floatKeyframes[i] = (FloatKeyframe) keyframes[i];
}
return new FloatKeyframeSet(floatKeyframes);
} else if (hasInt && !hasFloat && !hasOther) {
IntKeyframe intKeyframes[] = new IntKeyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
intKeyframes[i] = (IntKeyframe) keyframes[i];
}
return new IntKeyframeSet(intKeyframes);
} else {
return new KeyframeSet(keyframes);
}
}
public static KeyframeSet ofObject(Object... values) {
int numKeyframes = values.length;
ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
} else {
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
}
}
return new KeyframeSet(keyframes);
}
/**
* Sets the TypeEvaluator to be used when calculating animated values. This object
* is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet,
* both of which assume their own evaluator to speed up calculations with those primitive
* types.
*
* @param evaluator The TypeEvaluator to be used to calculate animated values.
*/
public void setEvaluator(TypeEvaluator evaluator) {
mEvaluator = evaluator;
}
@Override
public KeyframeSet clone() {
ArrayList<Keyframe> keyframes = mKeyframes;
int numKeyframes = mKeyframes.size();
Keyframe[] newKeyframes = new Keyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
newKeyframes[i] = keyframes.get(i).clone();
}
KeyframeSet newSet = new KeyframeSet(newKeyframes);
return newSet;
}
/**
* Gets the animated value, given the elapsed fraction of the animation (interpolated by the
* animation's interpolator) and the evaluator used to calculate in-between values. This
* function maps the input fraction to the appropriate keyframe interval and a fraction
* between them and returns the interpolated value. Note that the input fraction may fall
* outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
* spring interpolation that might send the fraction past 1.0). We handle this situation by
* just using the two keyframes at the appropriate end when the value is outside those bounds.
*
* @param fraction The elapsed fraction of the animation
* @return The animated value.
*/
public Object getValue(float fraction) {
// Special-case optimization for the common case of only two keyframes
if (mNumKeyframes == 2) {
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
mLastKeyframe.getValue());
}
if (fraction <= 0f) {
final Keyframe nextKeyframe = mKeyframes.get(1);
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = mFirstKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
nextKeyframe.getValue());
} else if (fraction >= 1f) {
final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
final /*Time*/Interpolator interpolator = mLastKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(mLastKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
mLastKeyframe.getValue());
}
Keyframe prevKeyframe = mFirstKeyframe;
for (int i = 1; i < mNumKeyframes; ++i) {
Keyframe nextKeyframe = mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
nextKeyframe.getValue());
}
prevKeyframe = nextKeyframe;
}
// shouldn't reach here
return mLastKeyframe.getValue();
}
@Override
public String toString() {
String returnVal = " ";
for (int i = 0; i < mNumKeyframes; ++i) {
returnVal += mKeyframes.get(i).getValue() + " ";
}
return returnVal;
}
}

View File

@ -1,515 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.util.Log;
import android.view.View;
import com.nineoldandroids.util.Property;
import com.nineoldandroids.view.animation.AnimatorProxy;
/**
* This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
* The constructors of this class take parameters to define the target object that will be animated
* as well as the name of the property that will be animated. Appropriate set/get functions
* are then determined internally and the animation will call these functions as necessary to
* animate the property.
*
* @see #setPropertyName(String)
*
*/
public final class ObjectAnimator extends ValueAnimator {
private static final boolean DBG = false;
private static final Map<String, Property> PROXY_PROPERTIES = new HashMap<String, Property>();
static {
PROXY_PROPERTIES.put("alpha", PreHoneycombCompat.ALPHA);
PROXY_PROPERTIES.put("pivotX", PreHoneycombCompat.PIVOT_X);
PROXY_PROPERTIES.put("pivotY", PreHoneycombCompat.PIVOT_Y);
PROXY_PROPERTIES.put("translationX", PreHoneycombCompat.TRANSLATION_X);
PROXY_PROPERTIES.put("translationY", PreHoneycombCompat.TRANSLATION_Y);
PROXY_PROPERTIES.put("rotation", PreHoneycombCompat.ROTATION);
PROXY_PROPERTIES.put("rotationX", PreHoneycombCompat.ROTATION_X);
PROXY_PROPERTIES.put("rotationY", PreHoneycombCompat.ROTATION_Y);
PROXY_PROPERTIES.put("scaleX", PreHoneycombCompat.SCALE_X);
PROXY_PROPERTIES.put("scaleY", PreHoneycombCompat.SCALE_Y);
PROXY_PROPERTIES.put("scrollX", PreHoneycombCompat.SCROLL_X);
PROXY_PROPERTIES.put("scrollY", PreHoneycombCompat.SCROLL_Y);
PROXY_PROPERTIES.put("x", PreHoneycombCompat.X);
PROXY_PROPERTIES.put("y", PreHoneycombCompat.Y);
}
// The target object on which the property exists, set in the constructor
private Object mTarget;
private String mPropertyName;
private Property mProperty;
/**
* Sets the name of the property that will be animated. This name is used to derive
* a setter function that will be called to set animated values.
* For example, a property name of <code>foo</code> will result
* in a call to the function <code>setFoo()</code> on the target object. If either
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
* also be derived and called.
*
* <p>For best performance of the mechanism that calls the setter function determined by the
* name of the property being animated, use <code>float</code> or <code>int</code> typed values,
* and make the setter function for those properties have a <code>void</code> return value. This
* will cause the code to take an optimized path for these constrained circumstances. Other
* property types and return types will work, but will have more overhead in processing
* the requests due to normal reflection mechanisms.</p>
*
* <p>Note that the setter function derived from this property name
* must take the same parameter type as the
* <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
* the setter function will fail.</p>
*
* <p>If this ObjectAnimator has been set up to animate several properties together,
* using more than one PropertyValuesHolder objects, then setting the propertyName simply
* sets the propertyName in the first of those PropertyValuesHolder objects.</p>
*
* @param propertyName The name of the property being animated. Should not be null.
*/
public void setPropertyName(String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
/**
* Sets the property that will be animated. Property objects will take precedence over
* properties specified by the {@link #setPropertyName(String)} method. Animations should
* be set up to use one or the other, not both.
*
* @param property The property being animated. Should not be null.
*/
public void setProperty(Property property) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setProperty(property);
mValuesMap.remove(oldName);
mValuesMap.put(mPropertyName, valuesHolder);
}
if (mProperty != null) {
mPropertyName = property.getName();
}
mProperty = property;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
/**
* Gets the name of the property that will be animated. This name will be used to derive
* a setter function that will be called to set animated values.
* For example, a property name of <code>foo</code> will result
* in a call to the function <code>setFoo()</code> on the target object. If either
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
* also be derived and called.
*/
public String getPropertyName() {
return mPropertyName;
}
/**
* Creates a new ObjectAnimator object. This default constructor is primarily for
* use internally; the other constructors which take parameters are more generally
* useful.
*/
public ObjectAnimator() {
}
/**
* Private utility constructor that initializes the target object and name of the
* property being animated.
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
*/
private ObjectAnimator(Object target, String propertyName) {
mTarget = target;
setPropertyName(propertyName);
}
/**
* Private utility constructor that initializes the target object and property being animated.
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
*/
private <T> ObjectAnimator(T target, Property<T, ?> property) {
mTarget = target;
setProperty(property);
}
/**
* Constructs and returns an ObjectAnimator that animates between int values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between int values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setIntValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
float... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setFloatValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between Object values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param evaluator A TypeEvaluator that will be called on each animation frame to
* provide the necessary interpolation between the Object values to derive the animated
* value.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofObject(Object target, String propertyName,
TypeEvaluator evaluator, Object... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between Object values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
* @param evaluator A TypeEvaluator that will be called on each animation frame to
* provide the necessary interpolation between the Object values to derive the animated
* value.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property,
TypeEvaluator<V> evaluator, V... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between the sets of values specified
* in <code>PropertyValueHolder</code> objects. This variant should be used when animating
* several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
* you to associate a set of animation values with a property name.
*
* @param target The object whose property is to be animated. Depending on how the
* PropertyValuesObjects were constructed, the target object should either have the {@link
* android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
* PropertyValuesHOlder objects were created with property names) the target object should have
* public methods on it called <code>setName()</code>, where <code>name</code> is the name of
* the property passed in as the <code>propertyName</code> parameter for each of the
* PropertyValuesHolder objects.
* @param values A set of PropertyValuesHolder objects whose values will be animated between
* over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofPropertyValuesHolder(Object target,
PropertyValuesHolder... values) {
ObjectAnimator anim = new ObjectAnimator();
anim.mTarget = target;
anim.setValues(values);
return anim;
}
@Override
public void setIntValues(int... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofInt(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
}
} else {
super.setIntValues(values);
}
}
@Override
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
@Override
public void setObjectValues(Object... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values));
} else {
setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
}
} else {
super.setObjectValues(values);
}
}
@Override
public void start() {
if (DBG) {
Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
Log.d("ObjectAnimator", " Values[" + i + "]: " +
pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
}
}
super.start();
}
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation. This includes setting mEvaluator, if the user has not yet
* set it up, and the setter/getter methods, if the user did not supply
* them.
*
* <p>Overriders of this method should call the superclass method to cause
* internal mechanisms to be set up correctly.</p>
*/
@Override
void initAnimation() {
if (!mInitialized) {
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
if ((mProperty == null) && AnimatorProxy.NEEDS_PROXY && (mTarget instanceof View) && PROXY_PROPERTIES.containsKey(mPropertyName)) {
setProperty(PROXY_PROPERTIES.get(mPropertyName));
}
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupSetterAndGetter(mTarget);
}
super.initAnimation();
}
}
/**
* Sets the length of the animation. The default duration is 300 milliseconds.
*
* @param duration The length of the animation, in milliseconds.
* @return ObjectAnimator The object called with setDuration(). This return
* value makes it easier to compose statements together that construct and then set the
* duration, as in
* <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>.
*/
@Override
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration);
return this;
}
/**
* The target object whose property will be animated by this animation
*
* @return The object being animated
*/
public Object getTarget() {
return mTarget;
}
/**
* Sets the target object whose property will be animated by this animation
*
* @param target The object being animated
*/
@Override
public void setTarget(Object target) {
if (mTarget != target) {
final Object oldTarget = mTarget;
mTarget = target;
if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) {
return;
}
// New target type should cause re-initialization prior to starting
mInitialized = false;
}
}
@Override
public void setupStartValues() {
initAnimation();
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupStartValue(mTarget);
}
}
@Override
public void setupEndValues() {
initAnimation();
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupEndValue(mTarget);
}
}
/**
* This method is called with the elapsed fraction of the animation during every
* animation frame. This function turns the elapsed fraction into an interpolated fraction
* and then into an animated value (from the evaluator. The function is called mostly during
* animation updates, but it is also called when the <code>end()</code>
* function is called, to set the final value on the property.
*
* <p>Overrides of this method must call the superclass to perform the calculation
* of the animated value.</p>
*
* @param fraction The elapsed fraction of the animation.
*/
@Override
void animateValue(float fraction) {
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(mTarget);
}
}
@Override
public ObjectAnimator clone() {
final ObjectAnimator anim = (ObjectAnimator) super.clone();
return anim;
}
@Override
public String toString() {
String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " +
mTarget;
if (mValues != null) {
for (int i = 0; i < mValues.length; ++i) {
returnVal += "\n " + mValues[i].toString();
}
}
return returnVal;
}
}

View File

@ -1,168 +0,0 @@
package com.nineoldandroids.animation;
import android.view.View;
import com.nineoldandroids.util.FloatProperty;
import com.nineoldandroids.util.IntProperty;
import com.nineoldandroids.util.Property;
import com.nineoldandroids.view.animation.AnimatorProxy;
final class PreHoneycombCompat {
static Property<View, Float> ALPHA = new FloatProperty<View>("alpha") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setAlpha(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getAlpha();
}
};
static Property<View, Float> PIVOT_X = new FloatProperty<View>("pivotX") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setPivotX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getPivotX();
}
};
static Property<View, Float> PIVOT_Y = new FloatProperty<View>("pivotY") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setPivotY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getPivotY();
}
};
static Property<View, Float> TRANSLATION_X = new FloatProperty<View>("translationX") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setTranslationX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getTranslationX();
}
};
static Property<View, Float> TRANSLATION_Y = new FloatProperty<View>("translationY") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setTranslationY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getTranslationY();
}
};
static Property<View, Float> ROTATION = new FloatProperty<View>("rotation") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setRotation(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getRotation();
}
};
static Property<View, Float> ROTATION_X = new FloatProperty<View>("rotationX") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setRotationX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getRotationX();
}
};
static Property<View, Float> ROTATION_Y = new FloatProperty<View>("rotationY") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setRotationY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getRotationY();
}
};
static Property<View, Float> SCALE_X = new FloatProperty<View>("scaleX") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setScaleX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getScaleX();
}
};
static Property<View, Float> SCALE_Y = new FloatProperty<View>("scaleY") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setScaleY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getScaleY();
}
};
static Property<View, Integer> SCROLL_X = new IntProperty<View>("scrollX") {
@Override
public void setValue(View object, int value) {
AnimatorProxy.wrap(object).setScrollX(value);
}
@Override
public Integer get(View object) {
return AnimatorProxy.wrap(object).getScrollX();
}
};
static Property<View, Integer> SCROLL_Y = new IntProperty<View>("scrollY") {
@Override
public void setValue(View object, int value) {
AnimatorProxy.wrap(object).setScrollY(value);
}
@Override
public Integer get(View object) {
return AnimatorProxy.wrap(object).getScrollY();
}
};
static Property<View, Float> X = new FloatProperty<View>("x") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getX();
}
};
static Property<View, Float> Y = new FloatProperty<View>("y") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getY();
}
};
//No instances
private PreHoneycombCompat() {}
}

View File

@ -1,78 +0,0 @@
package com.nineoldandroids.animation;
/**
* This class provides a simple callback mechanism to listeners that is synchronized with other
* animators in the system. There is no duration, interpolation, or object value-setting
* with this Animator. Instead, it is simply started and proceeds to send out events on every
* animation frame to its TimeListener (if set), with information about this animator,
* the total elapsed time, and the time since the last animation frame.
*
* @hide
*/
public class TimeAnimator extends ValueAnimator {
private TimeListener mListener;
private long mPreviousTime = -1;
@Override
boolean animationFrame(long currentTime) {
if (mPlayingState == STOPPED) {
mPlayingState = RUNNING;
if (mSeekTime < 0) {
mStartTime = currentTime;
} else {
mStartTime = currentTime - mSeekTime;
// Now that we're playing, reset the seek time
mSeekTime = -1;
}
}
if (mListener != null) {
long totalTime = currentTime - mStartTime;
long deltaTime = (mPreviousTime < 0) ? 0 : (currentTime - mPreviousTime);
mPreviousTime = currentTime;
mListener.onTimeUpdate(this, totalTime, deltaTime);
}
return false;
}
/**
* Sets a listener that is sent update events throughout the life of
* an animation.
*
* @param listener the listener to be set.
*/
public void setTimeListener(TimeListener listener) {
mListener = listener;
}
@Override
void animateValue(float fraction) {
// Noop
}
@Override
void initAnimation() {
// noop
}
/**
* Implementors of this interface can set themselves as update listeners
* to a <code>TimeAnimator</code> instance to receive callbacks on every animation
* frame to receive the total time since the animator started and the delta time
* since the last frame. The first time the listener is called, totalTime and
* deltaTime should both be zero.
*
* @hide
*/
public static interface TimeListener {
/**
* <p>Notifies listeners of the occurrence of another frame of the animation,
* along with information about the elapsed time.</p>
*
* @param animation The animator sending out the notification.
* @param totalTime The
*/
void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime);
}
}

View File

@ -1,44 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.animation;
/**
* Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
* allow developers to create animations on arbitrary property types, by allowing them to supply
* custom evaulators for types that are not automatically understood and used by the animation
* system.
*
* @see ValueAnimator#setEvaluator(TypeEvaluator)
*/
public interface TypeEvaluator<T> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value.
* @param endValue The end value.
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public T evaluate(float fraction, T startValue, T endValue);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.util;
/**
* An implementation of {@link android.util.Property} to be used specifically with fields of type
* <code>float</code>. This type-specific subclass enables performance benefit by allowing
* calls to a {@link #set(Object, Float) set()} function that takes the primitive
* <code>float</code> type and avoids autoboxing and other overhead associated with the
* <code>Float</code> class.
*
* @param <T> The class on which the Property is declared.
*
* @hide
*/
public abstract class FloatProperty<T> extends Property<T, Float> {
public FloatProperty(String name) {
super(Float.class, name);
}
/**
* A type-specific override of the {@link #set(Object, Float)} that is faster when dealing
* with fields of type <code>float</code>.
*/
public abstract void setValue(T object, float value);
@Override
final public void set(T object, Float value) {
setValue(object, value);
}
}

View File

@ -1,46 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.util;
/**
* An implementation of {@link android.util.Property} to be used specifically with fields of type
* <code>int</code>. This type-specific subclass enables performance benefit by allowing
* calls to a {@link #set(Object, Integer) set()} function that takes the primitive
* <code>int</code> type and avoids autoboxing and other overhead associated with the
* <code>Integer</code> class.
*
* @param <T> The class on which the Property is declared.
*
* @hide
*/
public abstract class IntProperty<T> extends Property<T, Integer> {
public IntProperty(String name) {
super(Integer.class, name);
}
/**
* A type-specific override of the {@link #set(Object, Integer)} that is faster when dealing
* with fields of type <code>int</code>.
*/
public abstract void setValue(T object, int value);
@Override
final public void set(T object, Integer value) {
set(object, value.intValue());
}
}

View File

@ -1,30 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.util;
/**
* Thrown when code requests a {@link Property} on a class that does
* not expose the appropriate method or field.
*
* @see Property#of(java.lang.Class, java.lang.Class, java.lang.String)
*/
public class NoSuchPropertyException extends RuntimeException {
public NoSuchPropertyException(String s) {
super(s);
}
}

View File

@ -1,106 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.util;
/**
* A property is an abstraction that can be used to represent a <emb>mutable</em> value that is held
* in a <em>host</em> object. The Property's {@link #set(Object, Object)} or {@link #get(Object)}
* methods can be implemented in terms of the private fields of the host object, or via "setter" and
* "getter" methods or by some other mechanism, as appropriate.
*
* @param <T> The class on which the property is declared.
* @param <V> The type that this property represents.
*/
public abstract class Property<T, V> {
private final String mName;
private final Class<V> mType;
/**
* This factory method creates and returns a Property given the <code>class</code> and
* <code>name</code> parameters, where the <code>"name"</code> parameter represents either:
* <ul>
* <li>a public <code>getName()</code> method on the class which takes no arguments, plus an
* optional public <code>setName()</code> method which takes a value of the same type
* returned by <code>getName()</code>
* <li>a public <code>isName()</code> method on the class which takes no arguments, plus an
* optional public <code>setName()</code> method which takes a value of the same type
* returned by <code>isName()</code>
* <li>a public <code>name</code> field on the class
* </ul>
*
* <p>If either of the get/is method alternatives is found on the class, but an appropriate
* <code>setName()</code> method is not found, the <code>Property</code> will be
* {@link #isReadOnly() readOnly}. Calling the {@link #set(Object, Object)} method on such
* a property is allowed, but will have no effect.</p>
*
* <p>If neither the methods nor the field are found on the class a
* {@link NoSuchPropertyException} exception will be thrown.</p>
*/
public static <T, V> Property<T, V> of(Class<T> hostType, Class<V> valueType, String name) {
return new ReflectiveProperty<T, V>(hostType, valueType, name);
}
/**
* A constructor that takes an identifying name and {@link #getType() type} for the property.
*/
public Property(Class<V> type, String name) {
mName = name;
mType = type;
}
/**
* Returns true if the {@link #set(Object, Object)} method does not set the value on the target
* object (in which case the {@link #set(Object, Object) set()} method should throw a {@link
* NoSuchPropertyException} exception). This may happen if the Property wraps functionality that
* allows querying the underlying value but not setting it. For example, the {@link #of(Class,
* Class, String)} factory method may return a Property with name "foo" for an object that has
* only a <code>getFoo()</code> or <code>isFoo()</code> method, but no matching
* <code>setFoo()</code> method.
*/
public boolean isReadOnly() {
return false;
}
/**
* Sets the value on <code>object</code> which this property represents. If the method is unable
* to set the value on the target object it will throw an {@link UnsupportedOperationException}
* exception.
*/
public void set(T object, V value) {
throw new UnsupportedOperationException("Property " + getName() +" is read-only");
}
/**
* Returns the current value that this property represents on the given <code>object</code>.
*/
public abstract V get(T object);
/**
* Returns the name for this property.
*/
public String getName() {
return mName;
}
/**
* Returns the type for this property.
*/
public Class<V> getType() {
return mType;
}
}

View File

@ -1,182 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Internal class to automatically generate a Property for a given class/name pair, given the
* specification of {@link Property#of(java.lang.Class, java.lang.Class, java.lang.String)}
*/
class ReflectiveProperty<T, V> extends Property<T, V> {
private static final String PREFIX_GET = "get";
private static final String PREFIX_IS = "is";
private static final String PREFIX_SET = "set";
private Method mSetter;
private Method mGetter;
private Field mField;
/**
* For given property name 'name', look for getName/isName method or 'name' field.
* Also look for setName method (optional - could be readonly). Failing method getters and
* field results in throwing NoSuchPropertyException.
*
* @param propertyHolder The class on which the methods or field are found
* @param name The name of the property, where this name is capitalized and appended to
* "get" and "is to search for the appropriate methods. If the get/is methods are not found,
* the constructor will search for a field with that exact name.
*/
public ReflectiveProperty(Class<T> propertyHolder, Class<V> valueType, String name) {
// TODO: cache reflection info for each new class/name pair
super(valueType, name);
char firstLetter = Character.toUpperCase(name.charAt(0));
String theRest = name.substring(1);
String capitalizedName = firstLetter + theRest;
String getterName = PREFIX_GET + capitalizedName;
try {
mGetter = propertyHolder.getMethod(getterName, (Class<?>[]) null);
} catch (NoSuchMethodException e) {
try {
/* The native implementation uses JNI to do reflection, which allows access to private methods.
* getDeclaredMethod(..) does not find superclass methods, so it's implemented as a fallback.
*/
mGetter = propertyHolder.getDeclaredMethod(getterName, (Class<?>[]) null);
mGetter.setAccessible(true);
} catch (NoSuchMethodException e2) {
// getName() not available - try isName() instead
getterName = PREFIX_IS + capitalizedName;
try {
mGetter = propertyHolder.getMethod(getterName, (Class<?>[]) null);
} catch (NoSuchMethodException e3) {
try {
/* The native implementation uses JNI to do reflection, which allows access to private methods.
* getDeclaredMethod(..) does not find superclass methods, so it's implemented as a fallback.
*/
mGetter = propertyHolder.getDeclaredMethod(getterName, (Class<?>[]) null);
mGetter.setAccessible(true);
} catch (NoSuchMethodException e4) {
// Try public field instead
try {
mField = propertyHolder.getField(name);
Class fieldType = mField.getType();
if (!typesMatch(valueType, fieldType)) {
throw new NoSuchPropertyException("Underlying type (" + fieldType + ") " +
"does not match Property type (" + valueType + ")");
}
return;
} catch (NoSuchFieldException e5) {
// no way to access property - throw appropriate exception
throw new NoSuchPropertyException("No accessor method or field found for"
+ " property with name " + name);
}
}
}
}
}
Class getterType = mGetter.getReturnType();
// Check to make sure our getter type matches our valueType
if (!typesMatch(valueType, getterType)) {
throw new NoSuchPropertyException("Underlying type (" + getterType + ") " +
"does not match Property type (" + valueType + ")");
}
String setterName = PREFIX_SET + capitalizedName;
try {
// mSetter = propertyHolder.getMethod(setterName, getterType);
// The native implementation uses JNI to do reflection, which allows access to private methods.
mSetter = propertyHolder.getDeclaredMethod(setterName, getterType);
mSetter.setAccessible(true);
} catch (NoSuchMethodException ignored) {
// Okay to not have a setter - just a readonly property
}
}
/**
* Utility method to check whether the type of the underlying field/method on the target
* object matches the type of the Property. The extra checks for primitive types are because
* generics will force the Property type to be a class, whereas the type of the underlying
* method/field will probably be a primitive type instead. Accept float as matching Float,
* etc.
*/
private boolean typesMatch(Class<V> valueType, Class getterType) {
if (getterType != valueType) {
if (getterType.isPrimitive()) {
return (getterType == float.class && valueType == Float.class) ||
(getterType == int.class && valueType == Integer.class) ||
(getterType == boolean.class && valueType == Boolean.class) ||
(getterType == long.class && valueType == Long.class) ||
(getterType == double.class && valueType == Double.class) ||
(getterType == short.class && valueType == Short.class) ||
(getterType == byte.class && valueType == Byte.class) ||
(getterType == char.class && valueType == Character.class);
}
return false;
}
return true;
}
@Override
public void set(T object, V value) {
if (mSetter != null) {
try {
mSetter.invoke(object, value);
} catch (IllegalAccessException e) {
throw new AssertionError();
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
} else if (mField != null) {
try {
mField.set(object, value);
} catch (IllegalAccessException e) {
throw new AssertionError();
}
} else {
throw new UnsupportedOperationException("Property " + getName() +" is read-only");
}
}
@Override
public V get(T object) {
if (mGetter != null) {
try {
return (V) mGetter.invoke(object, (Object[])null);
} catch (IllegalAccessException e) {
throw new AssertionError();
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
} else if (mField != null) {
try {
return (V) mField.get(object);
} catch (IllegalAccessException e) {
throw new AssertionError();
}
}
// Should not get here: there should always be a non-null getter or field
throw new AssertionError();
}
/**
* Returns false if there is no setter or public field underlying this Property.
*/
@Override
public boolean isReadOnly() {
return (mSetter == null && mField == null);
}
}

View File

@ -1,292 +0,0 @@
package com.nineoldandroids.view;
import android.view.View;
import static com.nineoldandroids.view.animation.AnimatorProxy.NEEDS_PROXY;
import static com.nineoldandroids.view.animation.AnimatorProxy.wrap;
public final class ViewHelper {
private ViewHelper() {}
public static float getAlpha(View view) {
return NEEDS_PROXY ? wrap(view).getAlpha() : Honeycomb.getAlpha(view);
}
public static void setAlpha(View view, float alpha) {
if (NEEDS_PROXY) {
wrap(view).setAlpha(alpha);
} else {
Honeycomb.setAlpha(view, alpha);
}
}
public static float getPivotX(View view) {
return NEEDS_PROXY ? wrap(view).getPivotX() : Honeycomb.getPivotX(view);
}
public static void setPivotX(View view, float pivotX) {
if (NEEDS_PROXY) {
wrap(view).setPivotX(pivotX);
} else {
Honeycomb.setPivotX(view, pivotX);
}
}
public static float getPivotY(View view) {
return NEEDS_PROXY ? wrap(view).getPivotY() : Honeycomb.getPivotY(view);
}
public static void setPivotY(View view, float pivotY) {
if (NEEDS_PROXY) {
wrap(view).setPivotY(pivotY);
} else {
Honeycomb.setPivotY(view, pivotY);
}
}
public static float getRotation(View view) {
return NEEDS_PROXY ? wrap(view).getRotation() : Honeycomb.getRotation(view);
}
public static void setRotation(View view, float rotation) {
if (NEEDS_PROXY) {
wrap(view).setRotation(rotation);
} else {
Honeycomb.setRotation(view, rotation);
}
}
public static float getRotationX(View view) {
return NEEDS_PROXY ? wrap(view).getRotationX() : Honeycomb.getRotationX(view);
}
public static void setRotationX(View view, float rotationX) {
if (NEEDS_PROXY) {
wrap(view).setRotationX(rotationX);
} else {
Honeycomb.setRotationX(view, rotationX);
}
}
public static float getRotationY(View view) {
return NEEDS_PROXY ? wrap(view).getRotationY() : Honeycomb.getRotationY(view);
}
public static void setRotationY(View view, float rotationY) {
if (NEEDS_PROXY) {
wrap(view).setRotationY(rotationY);
} else {
Honeycomb.setRotationY(view, rotationY);
}
}
public static float getScaleX(View view) {
return NEEDS_PROXY ? wrap(view).getScaleX() : Honeycomb.getScaleX(view);
}
public static void setScaleX(View view, float scaleX) {
if (NEEDS_PROXY) {
wrap(view).setScaleX(scaleX);
} else {
Honeycomb.setScaleX(view, scaleX);
}
}
public static float getScaleY(View view) {
return NEEDS_PROXY ? wrap(view).getScaleY() : Honeycomb.getScaleY(view);
}
public static void setScaleY(View view, float scaleY) {
if (NEEDS_PROXY) {
wrap(view).setScaleY(scaleY);
} else {
Honeycomb.setScaleY(view, scaleY);
}
}
public static float getScrollX(View view) {
return NEEDS_PROXY ? wrap(view).getScrollX() : Honeycomb.getScrollX(view);
}
public static void setScrollX(View view, int scrollX) {
if (NEEDS_PROXY) {
wrap(view).setScrollX(scrollX);
} else {
Honeycomb.setScrollX(view, scrollX);
}
}
public static float getScrollY(View view) {
return NEEDS_PROXY ? wrap(view).getScrollY() : Honeycomb.getScrollY(view);
}
public static void setScrollY(View view, int scrollY) {
if (NEEDS_PROXY) {
wrap(view).setScrollY(scrollY);
} else {
Honeycomb.setScrollY(view, scrollY);
}
}
public static float getTranslationX(View view) {
return NEEDS_PROXY ? wrap(view).getTranslationX() : Honeycomb.getTranslationX(view);
}
public static void setTranslationX(View view, float translationX) {
if (NEEDS_PROXY) {
wrap(view).setTranslationX(translationX);
} else {
Honeycomb.setTranslationX(view, translationX);
}
}
public static float getTranslationY(View view) {
return NEEDS_PROXY ? wrap(view).getTranslationY() : Honeycomb.getTranslationY(view);
}
public static void setTranslationY(View view, float translationY) {
if (NEEDS_PROXY) {
wrap(view).setTranslationY(translationY);
} else {
Honeycomb.setTranslationY(view, translationY);
}
}
public static float getX(View view) {
return NEEDS_PROXY ? wrap(view).getX() : Honeycomb.getX(view);
}
public static void setX(View view, float x) {
if (NEEDS_PROXY) {
wrap(view).setX(x);
} else {
Honeycomb.setX(view, x);
}
}
public static float getY(View view) {
return NEEDS_PROXY ? wrap(view).getY() : Honeycomb.getY(view);
}
public static void setY(View view, float y) {
if (NEEDS_PROXY) {
wrap(view).setY(y);
} else {
Honeycomb.setY(view, y);
}
}
private static final class Honeycomb {
static float getAlpha(View view) {
return view.getAlpha();
}
static void setAlpha(View view, float alpha) {
view.setAlpha(alpha);
}
static float getPivotX(View view) {
return view.getPivotX();
}
static void setPivotX(View view, float pivotX) {
view.setPivotX(pivotX);
}
static float getPivotY(View view) {
return view.getPivotY();
}
static void setPivotY(View view, float pivotY) {
view.setPivotY(pivotY);
}
static float getRotation(View view) {
return view.getRotation();
}
static void setRotation(View view, float rotation) {
view.setRotation(rotation);
}
static float getRotationX(View view) {
return view.getRotationX();
}
static void setRotationX(View view, float rotationX) {
view.setRotationX(rotationX);
}
static float getRotationY(View view) {
return view.getRotationY();
}
static void setRotationY(View view, float rotationY) {
view.setRotationY(rotationY);
}
static float getScaleX(View view) {
return view.getScaleX();
}
static void setScaleX(View view, float scaleX) {
view.setScaleX(scaleX);
}
static float getScaleY(View view) {
return view.getScaleY();
}
static void setScaleY(View view, float scaleY) {
view.setScaleY(scaleY);
}
static float getScrollX(View view) {
return view.getScrollX();
}
static void setScrollX(View view, int scrollX) {
view.setScrollX(scrollX);
}
static float getScrollY(View view) {
return view.getScrollY();
}
static void setScrollY(View view, int scrollY) {
view.setScrollY(scrollY);
}
static float getTranslationX(View view) {
return view.getTranslationX();
}
static void setTranslationX(View view, float translationX) {
view.setTranslationX(translationX);
}
static float getTranslationY(View view) {
return view.getTranslationY();
}
static void setTranslationY(View view, float translationY) {
view.setTranslationY(translationY);
}
static float getX(View view) {
return view.getX();
}
static void setX(View view, float x) {
view.setX(x);
}
static float getY(View view) {
return view.getY();
}
static void setY(View view, float y) {
view.setY(y);
}
}
}

View File

@ -1,346 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.view;
import java.util.WeakHashMap;
import android.os.Build;
import android.view.View;
import android.view.animation.Interpolator;
import com.nineoldandroids.animation.Animator;
/**
* This class enables automatic and optimized animation of select properties on View objects.
* If only one or two properties on a View object are being animated, then using an
* {@link android.animation.ObjectAnimator} is fine; the property setters called by ObjectAnimator
* are well equipped to do the right thing to set the property and invalidate the view
* appropriately. But if several properties are animated simultaneously, or if you just want a
* more convenient syntax to animate a specific property, then ViewPropertyAnimator might be
* more well-suited to the task.
*
* <p>This class may provide better performance for several simultaneous animations, because
* it will optimize invalidate calls to take place only once for several properties instead of each
* animated property independently causing its own invalidation. Also, the syntax of using this
* class could be easier to use because the caller need only tell the View object which
* property to animate, and the value to animate either to or by, and this class handles the
* details of configuring the underlying Animator class and starting it.</p>
*
* <p>This class is not constructed by the caller, but rather by the View whose properties
* it will animate. Calls to {@link android.view.View#animate()} will return a reference
* to the appropriate ViewPropertyAnimator object for that View.</p>
*
*/
public abstract class ViewPropertyAnimator {
private static final WeakHashMap<View, ViewPropertyAnimator> ANIMATORS =
new WeakHashMap<View, ViewPropertyAnimator>(0);
/**
* This method returns a ViewPropertyAnimator object, which can be used to animate specific
* properties on this View.
*
* @param view View to animate.
* @return The ViewPropertyAnimator associated with this View.
*/
public static ViewPropertyAnimator animate(View view) {
ViewPropertyAnimator animator = ANIMATORS.get(view);
if (animator == null) {
final int version = Integer.valueOf(Build.VERSION.SDK);
if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
animator = new ViewPropertyAnimatorICS(view);
} else if (version >= Build.VERSION_CODES.HONEYCOMB) {
animator = new ViewPropertyAnimatorHC(view);
} else {
animator = new ViewPropertyAnimatorPreHC(view);
}
ANIMATORS.put(view, animator);
}
return animator;
}
/**
* Sets the duration for the underlying animator that animates the requested properties.
* By default, the animator uses the default value for ValueAnimator. Calling this method
* will cause the declared value to be used instead.
* @param duration The length of ensuing property animations, in milliseconds. The value
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator setDuration(long duration);
/**
* Returns the current duration of property animations. If the duration was set on this
* object, that value is returned. Otherwise, the default value of the underlying Animator
* is returned.
*
* @see #setDuration(long)
* @return The duration of animations, in milliseconds.
*/
public abstract long getDuration();
/**
* Returns the current startDelay of property animations. If the startDelay was set on this
* object, that value is returned. Otherwise, the default value of the underlying Animator
* is returned.
*
* @see #setStartDelay(long)
* @return The startDelay of animations, in milliseconds.
*/
public abstract long getStartDelay();
/**
* Sets the startDelay for the underlying animator that animates the requested properties.
* By default, the animator uses the default value for ValueAnimator. Calling this method
* will cause the declared value to be used instead.
* @param startDelay The delay of ensuing property animations, in milliseconds. The value
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator setStartDelay(long startDelay);
/**
* Sets the interpolator for the underlying animator that animates the requested properties.
* By default, the animator uses the default interpolator for ValueAnimator. Calling this method
* will cause the declared object to be used instead.
*
* @param interpolator The TimeInterpolator to be used for ensuing property animations.
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator setInterpolator(/*Time*/Interpolator interpolator);
/**
* Sets a listener for events in the underlying Animators that run the property
* animations.
*
* @param listener The listener to be called with AnimatorListener events.
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator setListener(Animator.AnimatorListener listener);
/**
* Starts the currently pending property animations immediately. Calling <code>start()</code>
* is optional because all animations start automatically at the next opportunity. However,
* if the animations are needed to start immediately and synchronously (not at the time when
* the next event is processed by the hierarchy, which is when the animations would begin
* otherwise), then this method can be used.
*/
public abstract void start();
/**
* Cancels all property animations that are currently running or pending.
*/
public abstract void cancel();
/**
* This method will cause the View's <code>x</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator x(float value);
/**
* This method will cause the View's <code>x</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator xBy(float value);
/**
* This method will cause the View's <code>y</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator y(float value);
/**
* This method will cause the View's <code>y</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator yBy(float value);
/**
* This method will cause the View's <code>rotation</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setRotation(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator rotation(float value);
/**
* This method will cause the View's <code>rotation</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setRotation(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator rotationBy(float value);
/**
* This method will cause the View's <code>rotationX</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setRotationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator rotationX(float value);
/**
* This method will cause the View's <code>rotationX</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setRotationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator rotationXBy(float value);
/**
* This method will cause the View's <code>rotationY</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setRotationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator rotationY(float value);
/**
* This method will cause the View's <code>rotationY</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setRotationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator rotationYBy(float value);
/**
* This method will cause the View's <code>translationX</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setTranslationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator translationX(float value);
/**
* This method will cause the View's <code>translationX</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setTranslationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator translationXBy(float value);
/**
* This method will cause the View's <code>translationY</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setTranslationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator translationY(float value);
/**
* This method will cause the View's <code>translationY</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setTranslationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator translationYBy(float value);
/**
* This method will cause the View's <code>scaleX</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setScaleX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator scaleX(float value);
/**
* This method will cause the View's <code>scaleX</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setScaleX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator scaleXBy(float value);
/**
* This method will cause the View's <code>scaleY</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setScaleY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator scaleY(float value);
/**
* This method will cause the View's <code>scaleY</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setScaleY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator scaleYBy(float value);
/**
* This method will cause the View's <code>alpha</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @see View#setAlpha(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator alpha(float value);
/**
* This method will cause the View's <code>alpha</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @see View#setAlpha(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator alphaBy(float value);
}

View File

@ -1,723 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.view;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import android.view.View;
import android.view.animation.Interpolator;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ValueAnimator;
class ViewPropertyAnimatorHC extends ViewPropertyAnimator {
/**
* A WeakReference holding the View whose properties are being animated by this class.
* This is set at construction time.
*/
private final WeakReference<View> mView;
/**
* The duration of the underlying Animator object. By default, we don't set the duration
* on the Animator and just use its default duration. If the duration is ever set on this
* Animator, then we use the duration that it was set to.
*/
private long mDuration;
/**
* A flag indicating whether the duration has been set on this object. If not, we don't set
* the duration on the underlying Animator, but instead just use its default duration.
*/
private boolean mDurationSet = false;
/**
* The startDelay of the underlying Animator object. By default, we don't set the startDelay
* on the Animator and just use its default startDelay. If the startDelay is ever set on this
* Animator, then we use the startDelay that it was set to.
*/
private long mStartDelay = 0;
/**
* A flag indicating whether the startDelay has been set on this object. If not, we don't set
* the startDelay on the underlying Animator, but instead just use its default startDelay.
*/
private boolean mStartDelaySet = false;
/**
* The interpolator of the underlying Animator object. By default, we don't set the interpolator
* on the Animator and just use its default interpolator. If the interpolator is ever set on
* this Animator, then we use the interpolator that it was set to.
*/
private /*Time*/Interpolator mInterpolator;
/**
* A flag indicating whether the interpolator has been set on this object. If not, we don't set
* the interpolator on the underlying Animator, but instead just use its default interpolator.
*/
private boolean mInterpolatorSet = false;
/**
* Listener for the lifecycle events of the underlying
*/
private Animator.AnimatorListener mListener = null;
/**
* This listener is the mechanism by which the underlying Animator causes changes to the
* properties currently being animated, as well as the cleanup after an animation is
* complete.
*/
private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener();
/**
* This list holds the properties that have been asked to animate. We allow the caller to
* request several animations prior to actually starting the underlying animator. This
* enables us to run one single animator to handle several properties in parallel. Each
* property is tossed onto the pending list until the animation actually starts (which is
* done by posting it onto mView), at which time the pending list is cleared and the properties
* on that list are added to the list of properties associated with that animator.
*/
ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>();
/**
* Constants used to associate a property being requested and the mechanism used to set
* the property (this class calls directly into View to set the properties in question).
*/
private static final int NONE = 0x0000;
private static final int TRANSLATION_X = 0x0001;
private static final int TRANSLATION_Y = 0x0002;
private static final int SCALE_X = 0x0004;
private static final int SCALE_Y = 0x0008;
private static final int ROTATION = 0x0010;
private static final int ROTATION_X = 0x0020;
private static final int ROTATION_Y = 0x0040;
private static final int X = 0x0080;
private static final int Y = 0x0100;
private static final int ALPHA = 0x0200;
private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y |
ROTATION | ROTATION_X | ROTATION_Y | X | Y;
/**
* The mechanism by which the user can request several properties that are then animated
* together works by posting this Runnable to start the underlying Animator. Every time
* a property animation is requested, we cancel any previous postings of the Runnable
* and re-post it. This means that we will only ever run the Runnable (and thus start the
* underlying animator) after the caller is done setting the properties that should be
* animated together.
*/
private Runnable mAnimationStarter = new Runnable() {
@Override
public void run() {
startAnimation();
}
};
/**
* This class holds information about the overall animation being run on the set of
* properties. The mask describes which properties are being animated and the
* values holder is the list of all property/value objects.
*/
private static class PropertyBundle {
int mPropertyMask;
ArrayList<NameValuesHolder> mNameValuesHolder;
PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) {
mPropertyMask = propertyMask;
mNameValuesHolder = nameValuesHolder;
}
/**
* Removes the given property from being animated as a part of this
* PropertyBundle. If the property was a part of this bundle, it returns
* true to indicate that it was, in fact, canceled. This is an indication
* to the caller that a cancellation actually occurred.
*
* @param propertyConstant The property whose cancellation is requested.
* @return true if the given property is a part of this bundle and if it
* has therefore been canceled.
*/
boolean cancel(int propertyConstant) {
if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) {
int count = mNameValuesHolder.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i);
if (nameValuesHolder.mNameConstant == propertyConstant) {
mNameValuesHolder.remove(i);
mPropertyMask &= ~propertyConstant;
return true;
}
}
}
return false;
}
}
/**
* This list tracks the list of properties being animated by any particular animator.
* In most situations, there would only ever be one animator running at a time. But it is
* possible to request some properties to animate together, then while those properties
* are animating, to request some other properties to animate together. The way that
* works is by having this map associate the group of properties being animated with the
* animator handling the animation. On every update event for an Animator, we ask the
* map for the associated properties and set them accordingly.
*/
private HashMap<Animator, PropertyBundle> mAnimatorMap =
new HashMap<Animator, PropertyBundle>();
/**
* This is the information we need to set each property during the animation.
* mNameConstant is used to set the appropriate field in View, and the from/delta
* values are used to calculate the animated value for a given animation fraction
* during the animation.
*/
private static class NameValuesHolder {
int mNameConstant;
float mFromValue;
float mDeltaValue;
NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {
mNameConstant = nameConstant;
mFromValue = fromValue;
mDeltaValue = deltaValue;
}
}
/**
* Constructor, called by View. This is private by design, as the user should only
* get a ViewPropertyAnimator by calling View.animate().
*
* @param view The View associated with this ViewPropertyAnimator
*/
ViewPropertyAnimatorHC(View view) {
mView = new WeakReference<View>(view);
}
/**
* Sets the duration for the underlying animator that animates the requested properties.
* By default, the animator uses the default value for ValueAnimator. Calling this method
* will cause the declared value to be used instead.
* @param duration The length of ensuing property animations, in milliseconds. The value
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDurationSet = true;
mDuration = duration;
return this;
}
/**
* Returns the current duration of property animations. If the duration was set on this
* object, that value is returned. Otherwise, the default value of the underlying Animator
* is returned.
*
* @see #setDuration(long)
* @return The duration of animations, in milliseconds.
*/
public long getDuration() {
if (mDurationSet) {
return mDuration;
} else {
// Just return the default from ValueAnimator, since that's what we'd get if
// the value has not been set otherwise
return new ValueAnimator().getDuration();
}
}
@Override
public long getStartDelay() {
if (mStartDelaySet) {
return mStartDelay;
} else {
// Just return the default from ValueAnimator (0), since that's what we'd get if
// the value has not been set otherwise
return 0;
}
}
@Override
public ViewPropertyAnimator setStartDelay(long startDelay) {
if (startDelay < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
startDelay);
}
mStartDelaySet = true;
mStartDelay = startDelay;
return this;
}
@Override
public ViewPropertyAnimator setInterpolator(/*Time*/Interpolator interpolator) {
mInterpolatorSet = true;
mInterpolator = interpolator;
return this;
}
@Override
public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
mListener = listener;
return this;
}
@Override
public void start() {
startAnimation();
}
@Override
public void cancel() {
if (mAnimatorMap.size() > 0) {
HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
(HashMap<Animator, PropertyBundle>)mAnimatorMap.clone();
Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
for (Animator runningAnim : animatorSet) {
runningAnim.cancel();
}
}
mPendingAnimations.clear();
View v = mView.get();
if (v != null) {
v.removeCallbacks(mAnimationStarter);
}
}
@Override
public ViewPropertyAnimator x(float value) {
animateProperty(X, value);
return this;
}
@Override
public ViewPropertyAnimator xBy(float value) {
animatePropertyBy(X, value);
return this;
}
@Override
public ViewPropertyAnimator y(float value) {
animateProperty(Y, value);
return this;
}
@Override
public ViewPropertyAnimator yBy(float value) {
animatePropertyBy(Y, value);
return this;
}
@Override
public ViewPropertyAnimator rotation(float value) {
animateProperty(ROTATION, value);
return this;
}
@Override
public ViewPropertyAnimator rotationBy(float value) {
animatePropertyBy(ROTATION, value);
return this;
}
@Override
public ViewPropertyAnimator rotationX(float value) {
animateProperty(ROTATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator rotationXBy(float value) {
animatePropertyBy(ROTATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator rotationY(float value) {
animateProperty(ROTATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator rotationYBy(float value) {
animatePropertyBy(ROTATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator translationX(float value) {
animateProperty(TRANSLATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator translationXBy(float value) {
animatePropertyBy(TRANSLATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator translationY(float value) {
animateProperty(TRANSLATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator translationYBy(float value) {
animatePropertyBy(TRANSLATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator scaleX(float value) {
animateProperty(SCALE_X, value);
return this;
}
@Override
public ViewPropertyAnimator scaleXBy(float value) {
animatePropertyBy(SCALE_X, value);
return this;
}
@Override
public ViewPropertyAnimator scaleY(float value) {
animateProperty(SCALE_Y, value);
return this;
}
@Override
public ViewPropertyAnimator scaleYBy(float value) {
animatePropertyBy(SCALE_Y, value);
return this;
}
@Override
public ViewPropertyAnimator alpha(float value) {
animateProperty(ALPHA, value);
return this;
}
@Override
public ViewPropertyAnimator alphaBy(float value) {
animatePropertyBy(ALPHA, value);
return this;
}
/**
* Starts the underlying Animator for a set of properties. We use a single animator that
* simply runs from 0 to 1, and then use that fractional value to set each property
* value accordingly.
*/
private void startAnimation() {
ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
ArrayList<NameValuesHolder> nameValueList =
(ArrayList<NameValuesHolder>) mPendingAnimations.clone();
mPendingAnimations.clear();
int propertyMask = 0;
int propertyCount = nameValueList.size();
for (int i = 0; i < propertyCount; ++i) {
NameValuesHolder nameValuesHolder = nameValueList.get(i);
propertyMask |= nameValuesHolder.mNameConstant;
}
mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
animator.addUpdateListener(mAnimatorEventListener);
animator.addListener(mAnimatorEventListener);
if (mStartDelaySet) {
animator.setStartDelay(mStartDelay);
}
if (mDurationSet) {
animator.setDuration(mDuration);
}
if (mInterpolatorSet) {
animator.setInterpolator(mInterpolator);
}
animator.start();
}
/**
* Utility function, called by the various x(), y(), etc. methods. This stores the
* constant name for the property along with the from/delta values that will be used to
* calculate and set the property during the animation. This structure is added to the
* pending animations, awaiting the eventual start() of the underlying animator. A
* Runnable is posted to start the animation, and any pending such Runnable is canceled
* (which enables us to end up starting just one animator for all of the properties
* specified at one time).
*
* @param constantName The specifier for the property being animated
* @param toValue The value to which the property will animate
*/
private void animateProperty(int constantName, float toValue) {
float fromValue = getValue(constantName);
float deltaValue = toValue - fromValue;
animatePropertyBy(constantName, fromValue, deltaValue);
}
/**
* Utility function, called by the various xBy(), yBy(), etc. methods. This method is
* just like animateProperty(), except the value is an offset from the property's
* current value, instead of an absolute "to" value.
*
* @param constantName The specifier for the property being animated
* @param byValue The amount by which the property will change
*/
private void animatePropertyBy(int constantName, float byValue) {
float fromValue = getValue(constantName);
animatePropertyBy(constantName, fromValue, byValue);
}
/**
* Utility function, called by animateProperty() and animatePropertyBy(), which handles the
* details of adding a pending animation and posting the request to start the animation.
*
* @param constantName The specifier for the property being animated
* @param startValue The starting value of the property
* @param byValue The amount by which the property will change
*/
private void animatePropertyBy(int constantName, float startValue, float byValue) {
// First, cancel any existing animations on this property
if (mAnimatorMap.size() > 0) {
Animator animatorToCancel = null;
Set<Animator> animatorSet = mAnimatorMap.keySet();
for (Animator runningAnim : animatorSet) {
PropertyBundle bundle = mAnimatorMap.get(runningAnim);
if (bundle.cancel(constantName)) {
// property was canceled - cancel the animation if it's now empty
// Note that it's safe to break out here because every new animation
// on a property will cancel a previous animation on that property, so
// there can only ever be one such animation running.
if (bundle.mPropertyMask == NONE) {
// the animation is no longer changing anything - cancel it
animatorToCancel = runningAnim;
break;
}
}
}
if (animatorToCancel != null) {
animatorToCancel.cancel();
}
}
NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
mPendingAnimations.add(nameValuePair);
View v = mView.get();
if (v != null) {
v.removeCallbacks(mAnimationStarter);
v.post(mAnimationStarter);
}
}
/**
* This method handles setting the property values directly in the View object's fields.
* propertyConstant tells it which property should be set, value is the value to set
* the property to.
*
* @param propertyConstant The property to be set
* @param value The value to set the property to
*/
private void setValue(int propertyConstant, float value) {
//final View.TransformationInfo info = mView.mTransformationInfo;
View v = mView.get();
if (v != null) {
switch (propertyConstant) {
case TRANSLATION_X:
//info.mTranslationX = value;
v.setTranslationX(value);
break;
case TRANSLATION_Y:
//info.mTranslationY = value;
v.setTranslationY(value);
break;
case ROTATION:
//info.mRotation = value;
v.setRotation(value);
break;
case ROTATION_X:
//info.mRotationX = value;
v.setRotationX(value);
break;
case ROTATION_Y:
//info.mRotationY = value;
v.setRotationY(value);
break;
case SCALE_X:
//info.mScaleX = value;
v.setScaleX(value);
break;
case SCALE_Y:
//info.mScaleY = value;
v.setScaleY(value);
break;
case X:
//info.mTranslationX = value - v.mLeft;
v.setX(value);
break;
case Y:
//info.mTranslationY = value - v.mTop;
v.setY(value);
break;
case ALPHA:
//info.mAlpha = value;
v.setAlpha(value);
break;
}
}
}
/**
* This method gets the value of the named property from the View object.
*
* @param propertyConstant The property whose value should be returned
* @return float The value of the named property
*/
private float getValue(int propertyConstant) {
//final View.TransformationInfo info = mView.mTransformationInfo;
View v = mView.get();
if (v != null) {
switch (propertyConstant) {
case TRANSLATION_X:
//return info.mTranslationX;
return v.getTranslationX();
case TRANSLATION_Y:
//return info.mTranslationY;
return v.getTranslationY();
case ROTATION:
//return info.mRotation;
return v.getRotation();
case ROTATION_X:
//return info.mRotationX;
return v.getRotationX();
case ROTATION_Y:
//return info.mRotationY;
return v.getRotationY();
case SCALE_X:
//return info.mScaleX;
return v.getScaleX();
case SCALE_Y:
//return info.mScaleY;
return v.getScaleY();
case X:
//return v.mLeft + info.mTranslationX;
return v.getX();
case Y:
//return v.mTop + info.mTranslationY;
return v.getY();
case ALPHA:
//return info.mAlpha;
return v.getAlpha();
}
}
return 0;
}
/**
* Utility class that handles the various Animator events. The only ones we care
* about are the end event (which we use to clean up the animator map when an animator
* finishes) and the update event (which we use to calculate the current value of each
* property and then set it on the view object).
*/
private class AnimatorEventListener
implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationStart(Animator animation) {
if (mListener != null) {
mListener.onAnimationStart(animation);
}
}
@Override
public void onAnimationCancel(Animator animation) {
if (mListener != null) {
mListener.onAnimationCancel(animation);
}
}
@Override
public void onAnimationRepeat(Animator animation) {
if (mListener != null) {
mListener.onAnimationRepeat(animation);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (mListener != null) {
mListener.onAnimationEnd(animation);
}
mAnimatorMap.remove(animation);
// If the map is empty, it means all animation are done or canceled, so the listener
// isn't needed anymore. Not nulling it would cause it to leak any objects used in
// its implementation
if (mAnimatorMap.isEmpty()) {
mListener = null;
}
}
/**
* Calculate the current value for each property and set it on the view. Invalidate
* the view object appropriately, depending on which properties are being animated.
*
* @param animation The animator associated with the properties that need to be
* set. This animator holds the animation fraction which we will use to calculate
* the current value of each property.
*/
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// alpha requires slightly different treatment than the other (transform) properties.
// The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
// logic is dependent on how the view handles an internal call to onSetAlpha().
// We track what kinds of properties are set, and how alpha is handled when it is
// set, and perform the invalidation steps appropriately.
//boolean alphaHandled = false;
//mView.invalidateParentCaches();
float fraction = animation.getAnimatedFraction();
PropertyBundle propertyBundle = mAnimatorMap.get(animation);
int propertyMask = propertyBundle.mPropertyMask;
if ((propertyMask & TRANSFORM_MASK) != 0) {
View v = mView.get();
if (v != null) {
v.invalidate(/*false*/);
}
}
ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
if (valueList != null) {
int count = valueList.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder values = valueList.get(i);
float value = values.mFromValue + fraction * values.mDeltaValue;
//if (values.mNameConstant == ALPHA) {
// alphaHandled = mView.setAlphaNoInvalidation(value);
//} else {
setValue(values.mNameConstant, value);
//}
}
}
/*if ((propertyMask & TRANSFORM_MASK) != 0) {
mView.mTransformationInfo.mMatrixDirty = true;
mView.mPrivateFlags |= View.DRAWN; // force another invalidation
}*/
// invalidate(false) in all cases except if alphaHandled gets set to true
// via the call to setAlphaNoInvalidation(), above
View v = mView.get();
if (v != null) {
v.invalidate(/*alphaHandled*/);
}
}
}
}

View File

@ -1,298 +0,0 @@
package com.nineoldandroids.view;
import java.lang.ref.WeakReference;
import android.view.View;
import android.view.animation.Interpolator;
import com.nineoldandroids.animation.Animator.AnimatorListener;
class ViewPropertyAnimatorICS extends ViewPropertyAnimator {
/**
* A value to be returned when the WeakReference holding the native implementation
* returns <code>null</code>
*/
private final static long RETURN_WHEN_NULL = -1L;
/**
* A WeakReference holding the native implementation of ViewPropertyAnimator
*/
private final WeakReference<android.view.ViewPropertyAnimator> mNative;
ViewPropertyAnimatorICS(View view) {
mNative = new WeakReference<android.view.ViewPropertyAnimator>(view.animate());
}
@Override
public ViewPropertyAnimator setDuration(long duration) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.setDuration(duration);
}
return this;
}
@Override
public long getDuration() {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
return n.getDuration();
}
return RETURN_WHEN_NULL;
}
@Override
public ViewPropertyAnimator setStartDelay(long startDelay) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.setStartDelay(startDelay);
}
return this;
}
@Override
public long getStartDelay() {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
return n.getStartDelay();
}
return RETURN_WHEN_NULL;
}
@Override
public ViewPropertyAnimator setInterpolator(Interpolator interpolator) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.setInterpolator(interpolator);
}
return this;
}
@Override
public ViewPropertyAnimator setListener(final AnimatorListener listener) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
if (listener == null) {
n.setListener(null);
} else {
n.setListener(new android.animation.Animator.AnimatorListener() {
@Override
public void onAnimationStart(android.animation.Animator animation) {
listener.onAnimationStart(null);
}
@Override
public void onAnimationRepeat(android.animation.Animator animation) {
listener.onAnimationRepeat(null);
}
@Override
public void onAnimationEnd(android.animation.Animator animation) {
listener.onAnimationEnd(null);
}
@Override
public void onAnimationCancel(android.animation.Animator animation) {
listener.onAnimationCancel(null);
}
});
}
}
return this;
}
@Override
public void start() {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.start();
}
}
@Override
public void cancel() {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.cancel();
}
}
@Override
public ViewPropertyAnimator x(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.x(value);
}
return this;
}
@Override
public ViewPropertyAnimator xBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.xBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator y(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.y(value);
}
return this;
}
@Override
public ViewPropertyAnimator yBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.yBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotation(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotation(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationX(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationX(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationXBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationXBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationY(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationY(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationYBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationYBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator translationX(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.translationX(value);
}
return this;
}
@Override
public ViewPropertyAnimator translationXBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.translationXBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator translationY(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.translationY(value);
}
return this;
}
@Override
public ViewPropertyAnimator translationYBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.translationYBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator scaleX(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.scaleX(value);
}
return this;
}
@Override
public ViewPropertyAnimator scaleXBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.scaleXBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator scaleY(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.scaleY(value);
}
return this;
}
@Override
public ViewPropertyAnimator scaleYBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.scaleYBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator alpha(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.alpha(value);
}
return this;
}
@Override
public ViewPropertyAnimator alphaBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.alphaBy(value);
}
return this;
}
}

View File

@ -1,724 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nineoldandroids.view;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import android.view.View;
import android.view.animation.Interpolator;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.view.animation.AnimatorProxy;
class ViewPropertyAnimatorPreHC extends ViewPropertyAnimator {
/**
* Proxy animation class which will allow us access to post-Honeycomb properties that were not
* otherwise available.
*/
private final AnimatorProxy mProxy;
/**
* A WeakReference holding the View whose properties are being animated by this class. This is
* set at construction time.
*/
private final WeakReference<View> mView;
/**
* The duration of the underlying Animator object. By default, we don't set the duration
* on the Animator and just use its default duration. If the duration is ever set on this
* Animator, then we use the duration that it was set to.
*/
private long mDuration;
/**
* A flag indicating whether the duration has been set on this object. If not, we don't set
* the duration on the underlying Animator, but instead just use its default duration.
*/
private boolean mDurationSet = false;
/**
* The startDelay of the underlying Animator object. By default, we don't set the startDelay
* on the Animator and just use its default startDelay. If the startDelay is ever set on this
* Animator, then we use the startDelay that it was set to.
*/
private long mStartDelay = 0;
/**
* A flag indicating whether the startDelay has been set on this object. If not, we don't set
* the startDelay on the underlying Animator, but instead just use its default startDelay.
*/
private boolean mStartDelaySet = false;
/**
* The interpolator of the underlying Animator object. By default, we don't set the interpolator
* on the Animator and just use its default interpolator. If the interpolator is ever set on
* this Animator, then we use the interpolator that it was set to.
*/
private /*Time*/Interpolator mInterpolator;
/**
* A flag indicating whether the interpolator has been set on this object. If not, we don't set
* the interpolator on the underlying Animator, but instead just use its default interpolator.
*/
private boolean mInterpolatorSet = false;
/**
* Listener for the lifecycle events of the underlying
*/
private Animator.AnimatorListener mListener = null;
/**
* This listener is the mechanism by which the underlying Animator causes changes to the
* properties currently being animated, as well as the cleanup after an animation is
* complete.
*/
private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener();
/**
* This list holds the properties that have been asked to animate. We allow the caller to
* request several animations prior to actually starting the underlying animator. This
* enables us to run one single animator to handle several properties in parallel. Each
* property is tossed onto the pending list until the animation actually starts (which is
* done by posting it onto mView), at which time the pending list is cleared and the properties
* on that list are added to the list of properties associated with that animator.
*/
ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>();
/**
* Constants used to associate a property being requested and the mechanism used to set
* the property (this class calls directly into View to set the properties in question).
*/
private static final int NONE = 0x0000;
private static final int TRANSLATION_X = 0x0001;
private static final int TRANSLATION_Y = 0x0002;
private static final int SCALE_X = 0x0004;
private static final int SCALE_Y = 0x0008;
private static final int ROTATION = 0x0010;
private static final int ROTATION_X = 0x0020;
private static final int ROTATION_Y = 0x0040;
private static final int X = 0x0080;
private static final int Y = 0x0100;
private static final int ALPHA = 0x0200;
private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y |
ROTATION | ROTATION_X | ROTATION_Y | X | Y;
/**
* The mechanism by which the user can request several properties that are then animated
* together works by posting this Runnable to start the underlying Animator. Every time
* a property animation is requested, we cancel any previous postings of the Runnable
* and re-post it. This means that we will only ever run the Runnable (and thus start the
* underlying animator) after the caller is done setting the properties that should be
* animated together.
*/
private Runnable mAnimationStarter = new Runnable() {
@Override
public void run() {
startAnimation();
}
};
/**
* This class holds information about the overall animation being run on the set of
* properties. The mask describes which properties are being animated and the
* values holder is the list of all property/value objects.
*/
private static class PropertyBundle {
int mPropertyMask;
ArrayList<NameValuesHolder> mNameValuesHolder;
PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) {
mPropertyMask = propertyMask;
mNameValuesHolder = nameValuesHolder;
}
/**
* Removes the given property from being animated as a part of this
* PropertyBundle. If the property was a part of this bundle, it returns
* true to indicate that it was, in fact, canceled. This is an indication
* to the caller that a cancellation actually occurred.
*
* @param propertyConstant The property whose cancellation is requested.
* @return true if the given property is a part of this bundle and if it
* has therefore been canceled.
*/
boolean cancel(int propertyConstant) {
if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) {
int count = mNameValuesHolder.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i);
if (nameValuesHolder.mNameConstant == propertyConstant) {
mNameValuesHolder.remove(i);
mPropertyMask &= ~propertyConstant;
return true;
}
}
}
return false;
}
}
/**
* This list tracks the list of properties being animated by any particular animator.
* In most situations, there would only ever be one animator running at a time. But it is
* possible to request some properties to animate together, then while those properties
* are animating, to request some other properties to animate together. The way that
* works is by having this map associate the group of properties being animated with the
* animator handling the animation. On every update event for an Animator, we ask the
* map for the associated properties and set them accordingly.
*/
private HashMap<Animator, PropertyBundle> mAnimatorMap =
new HashMap<Animator, PropertyBundle>();
/**
* This is the information we need to set each property during the animation.
* mNameConstant is used to set the appropriate field in View, and the from/delta
* values are used to calculate the animated value for a given animation fraction
* during the animation.
*/
private static class NameValuesHolder {
int mNameConstant;
float mFromValue;
float mDeltaValue;
NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {
mNameConstant = nameConstant;
mFromValue = fromValue;
mDeltaValue = deltaValue;
}
}
/**
* Constructor, called by View. This is private by design, as the user should only
* get a ViewPropertyAnimator by calling View.animate().
*
* @param view The View associated with this ViewPropertyAnimator
*/
ViewPropertyAnimatorPreHC(View view) {
mView = new WeakReference<View>(view);
mProxy = AnimatorProxy.wrap(view);
}
/**
* Sets the duration for the underlying animator that animates the requested properties.
* By default, the animator uses the default value for ValueAnimator. Calling this method
* will cause the declared value to be used instead.
* @param duration The length of ensuing property animations, in milliseconds. The value
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDurationSet = true;
mDuration = duration;
return this;
}
/**
* Returns the current duration of property animations. If the duration was set on this
* object, that value is returned. Otherwise, the default value of the underlying Animator
* is returned.
*
* @see #setDuration(long)
* @return The duration of animations, in milliseconds.
*/
public long getDuration() {
if (mDurationSet) {
return mDuration;
} else {
// Just return the default from ValueAnimator, since that's what we'd get if
// the value has not been set otherwise
return new ValueAnimator().getDuration();
}
}
@Override
public long getStartDelay() {
if (mStartDelaySet) {
return mStartDelay;
} else {
// Just return the default from ValueAnimator (0), since that's what we'd get if
// the value has not been set otherwise
return 0;
}
}
@Override
public ViewPropertyAnimator setStartDelay(long startDelay) {
if (startDelay < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
startDelay);
}
mStartDelaySet = true;
mStartDelay = startDelay;
return this;
}
@Override
public ViewPropertyAnimator setInterpolator(/*Time*/Interpolator interpolator) {
mInterpolatorSet = true;
mInterpolator = interpolator;
return this;
}
@Override
public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
mListener = listener;
return this;
}
@Override
public void start() {
startAnimation();
}
@Override
public void cancel() {
if (mAnimatorMap.size() > 0) {
HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
(HashMap<Animator, PropertyBundle>)mAnimatorMap.clone();
Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
for (Animator runningAnim : animatorSet) {
runningAnim.cancel();
}
}
mPendingAnimations.clear();
View v = mView.get();
if (v != null) {
v.removeCallbacks(mAnimationStarter);
}
}
@Override
public ViewPropertyAnimator x(float value) {
animateProperty(X, value);
return this;
}
@Override
public ViewPropertyAnimator xBy(float value) {
animatePropertyBy(X, value);
return this;
}
@Override
public ViewPropertyAnimator y(float value) {
animateProperty(Y, value);
return this;
}
@Override
public ViewPropertyAnimator yBy(float value) {
animatePropertyBy(Y, value);
return this;
}
@Override
public ViewPropertyAnimator rotation(float value) {
animateProperty(ROTATION, value);
return this;
}
@Override
public ViewPropertyAnimator rotationBy(float value) {
animatePropertyBy(ROTATION, value);
return this;
}
@Override
public ViewPropertyAnimator rotationX(float value) {
animateProperty(ROTATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator rotationXBy(float value) {
animatePropertyBy(ROTATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator rotationY(float value) {
animateProperty(ROTATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator rotationYBy(float value) {
animatePropertyBy(ROTATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator translationX(float value) {
animateProperty(TRANSLATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator translationXBy(float value) {
animatePropertyBy(TRANSLATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator translationY(float value) {
animateProperty(TRANSLATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator translationYBy(float value) {
animatePropertyBy(TRANSLATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator scaleX(float value) {
animateProperty(SCALE_X, value);
return this;
}
@Override
public ViewPropertyAnimator scaleXBy(float value) {
animatePropertyBy(SCALE_X, value);
return this;
}
@Override
public ViewPropertyAnimator scaleY(float value) {
animateProperty(SCALE_Y, value);
return this;
}
@Override
public ViewPropertyAnimator scaleYBy(float value) {
animatePropertyBy(SCALE_Y, value);
return this;
}
@Override
public ViewPropertyAnimator alpha(float value) {
animateProperty(ALPHA, value);
return this;
}
@Override
public ViewPropertyAnimator alphaBy(float value) {
animatePropertyBy(ALPHA, value);
return this;
}
/**
* Starts the underlying Animator for a set of properties. We use a single animator that
* simply runs from 0 to 1, and then use that fractional value to set each property
* value accordingly.
*/
private void startAnimation() {
ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
ArrayList<NameValuesHolder> nameValueList =
(ArrayList<NameValuesHolder>) mPendingAnimations.clone();
mPendingAnimations.clear();
int propertyMask = 0;
int propertyCount = nameValueList.size();
for (int i = 0; i < propertyCount; ++i) {
NameValuesHolder nameValuesHolder = nameValueList.get(i);
propertyMask |= nameValuesHolder.mNameConstant;
}
mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
animator.addUpdateListener(mAnimatorEventListener);
animator.addListener(mAnimatorEventListener);
if (mStartDelaySet) {
animator.setStartDelay(mStartDelay);
}
if (mDurationSet) {
animator.setDuration(mDuration);
}
if (mInterpolatorSet) {
animator.setInterpolator(mInterpolator);
}
animator.start();
}
/**
* Utility function, called by the various x(), y(), etc. methods. This stores the
* constant name for the property along with the from/delta values that will be used to
* calculate and set the property during the animation. This structure is added to the
* pending animations, awaiting the eventual start() of the underlying animator. A
* Runnable is posted to start the animation, and any pending such Runnable is canceled
* (which enables us to end up starting just one animator for all of the properties
* specified at one time).
*
* @param constantName The specifier for the property being animated
* @param toValue The value to which the property will animate
*/
private void animateProperty(int constantName, float toValue) {
float fromValue = getValue(constantName);
float deltaValue = toValue - fromValue;
animatePropertyBy(constantName, fromValue, deltaValue);
}
/**
* Utility function, called by the various xBy(), yBy(), etc. methods. This method is
* just like animateProperty(), except the value is an offset from the property's
* current value, instead of an absolute "to" value.
*
* @param constantName The specifier for the property being animated
* @param byValue The amount by which the property will change
*/
private void animatePropertyBy(int constantName, float byValue) {
float fromValue = getValue(constantName);
animatePropertyBy(constantName, fromValue, byValue);
}
/**
* Utility function, called by animateProperty() and animatePropertyBy(), which handles the
* details of adding a pending animation and posting the request to start the animation.
*
* @param constantName The specifier for the property being animated
* @param startValue The starting value of the property
* @param byValue The amount by which the property will change
*/
private void animatePropertyBy(int constantName, float startValue, float byValue) {
// First, cancel any existing animations on this property
if (mAnimatorMap.size() > 0) {
Animator animatorToCancel = null;
Set<Animator> animatorSet = mAnimatorMap.keySet();
for (Animator runningAnim : animatorSet) {
PropertyBundle bundle = mAnimatorMap.get(runningAnim);
if (bundle.cancel(constantName)) {
// property was canceled - cancel the animation if it's now empty
// Note that it's safe to break out here because every new animation
// on a property will cancel a previous animation on that property, so
// there can only ever be one such animation running.
if (bundle.mPropertyMask == NONE) {
// the animation is no longer changing anything - cancel it
animatorToCancel = runningAnim;
break;
}
}
}
if (animatorToCancel != null) {
animatorToCancel.cancel();
}
}
NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
mPendingAnimations.add(nameValuePair);
View v = mView.get();
if (v != null) {
v.removeCallbacks(mAnimationStarter);
v.post(mAnimationStarter);
}
}
/**
* This method handles setting the property values directly in the View object's fields.
* propertyConstant tells it which property should be set, value is the value to set
* the property to.
*
* @param propertyConstant The property to be set
* @param value The value to set the property to
*/
private void setValue(int propertyConstant, float value) {
//final View.TransformationInfo info = mView.mTransformationInfo;
switch (propertyConstant) {
case TRANSLATION_X:
//info.mTranslationX = value;
mProxy.setTranslationX(value);
break;
case TRANSLATION_Y:
//info.mTranslationY = value;
mProxy.setTranslationY(value);
break;
case ROTATION:
//info.mRotation = value;
mProxy.setRotation(value);
break;
case ROTATION_X:
//info.mRotationX = value;
mProxy.setRotationX(value);
break;
case ROTATION_Y:
//info.mRotationY = value;
mProxy.setRotationY(value);
break;
case SCALE_X:
//info.mScaleX = value;
mProxy.setScaleX(value);
break;
case SCALE_Y:
//info.mScaleY = value;
mProxy.setScaleY(value);
break;
case X:
//info.mTranslationX = value - mView.mLeft;
mProxy.setX(value);
break;
case Y:
//info.mTranslationY = value - mView.mTop;
mProxy.setY(value);
break;
case ALPHA:
//info.mAlpha = value;
mProxy.setAlpha(value);
break;
}
}
/**
* This method gets the value of the named property from the View object.
*
* @param propertyConstant The property whose value should be returned
* @return float The value of the named property
*/
private float getValue(int propertyConstant) {
//final View.TransformationInfo info = mView.mTransformationInfo;
switch (propertyConstant) {
case TRANSLATION_X:
//return info.mTranslationX;
return mProxy.getTranslationX();
case TRANSLATION_Y:
//return info.mTranslationY;
return mProxy.getTranslationY();
case ROTATION:
//return info.mRotation;
return mProxy.getRotation();
case ROTATION_X:
//return info.mRotationX;
return mProxy.getRotationX();
case ROTATION_Y:
//return info.mRotationY;
return mProxy.getRotationY();
case SCALE_X:
//return info.mScaleX;
return mProxy.getScaleX();
case SCALE_Y:
//return info.mScaleY;
return mProxy.getScaleY();
case X:
//return mView.mLeft + info.mTranslationX;
return mProxy.getX();
case Y:
//return mView.mTop + info.mTranslationY;
return mProxy.getY();
case ALPHA:
//return info.mAlpha;
return mProxy.getAlpha();
}
return 0;
}
/**
* Utility class that handles the various Animator events. The only ones we care
* about are the end event (which we use to clean up the animator map when an animator
* finishes) and the update event (which we use to calculate the current value of each
* property and then set it on the view object).
*/
private class AnimatorEventListener
implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationStart(Animator animation) {
if (mListener != null) {
mListener.onAnimationStart(animation);
}
}
@Override
public void onAnimationCancel(Animator animation) {
if (mListener != null) {
mListener.onAnimationCancel(animation);
}
}
@Override
public void onAnimationRepeat(Animator animation) {
if (mListener != null) {
mListener.onAnimationRepeat(animation);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (mListener != null) {
mListener.onAnimationEnd(animation);
}
mAnimatorMap.remove(animation);
// If the map is empty, it means all animation are done or canceled, so the listener
// isn't needed anymore. Not nulling it would cause it to leak any objects used in
// its implementation
if (mAnimatorMap.isEmpty()) {
mListener = null;
}
}
/**
* Calculate the current value for each property and set it on the view. Invalidate
* the view object appropriately, depending on which properties are being animated.
*
* @param animation The animator associated with the properties that need to be
* set. This animator holds the animation fraction which we will use to calculate
* the current value of each property.
*/
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// alpha requires slightly different treatment than the other (transform) properties.
// The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
// logic is dependent on how the view handles an internal call to onSetAlpha().
// We track what kinds of properties are set, and how alpha is handled when it is
// set, and perform the invalidation steps appropriately.
//boolean alphaHandled = false;
//mView.invalidateParentCaches();
float fraction = animation.getAnimatedFraction();
PropertyBundle propertyBundle = mAnimatorMap.get(animation);
int propertyMask = propertyBundle.mPropertyMask;
if ((propertyMask & TRANSFORM_MASK) != 0) {
View v = mView.get();
if (v != null) {
v.invalidate(/*false*/);
}
}
ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
if (valueList != null) {
int count = valueList.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder values = valueList.get(i);
float value = values.mFromValue + fraction * values.mDeltaValue;
//if (values.mNameConstant == ALPHA) {
// alphaHandled = mView.setAlphaNoInvalidation(value);
//} else {
setValue(values.mNameConstant, value);
//}
}
}
/*if ((propertyMask & TRANSFORM_MASK) != 0) {
mView.mTransformationInfo.mMatrixDirty = true;
mView.mPrivateFlags |= View.DRAWN; // force another invalidation
}*/
// invalidate(false) in all cases except if alphaHandled gets set to true
// via the call to setAlphaNoInvalidation(), above
View v = mView.get();
if (v != null) {
v.invalidate(/*alphaHandled*/);
}
}
}
}

View File

@ -1,322 +0,0 @@
package com.nineoldandroids.view.animation;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Build;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
/**
* A proxy class to allow for modifying post-3.0 view properties on all pre-3.0
* platforms. <strong>DO NOT</strong> wrap your views with this class if you
* are using {@code ObjectAnimator} as it will handle that itself.
*/
public final class AnimatorProxy extends Animation {
/** Whether or not the current running platform needs to be proxied. */
public static final boolean NEEDS_PROXY = Integer.valueOf(Build.VERSION.SDK).intValue() < Build.VERSION_CODES.HONEYCOMB;
private static final WeakHashMap<View, AnimatorProxy> PROXIES =
new WeakHashMap<View, AnimatorProxy>();
/**
* Create a proxy to allow for modifying post-3.0 view properties on all
* pre-3.0 platforms. <strong>DO NOT</strong> wrap your views if you are
* using {@code ObjectAnimator} as it will handle that itself.
*
* @param view View to wrap.
* @return Proxy to post-3.0 properties.
*/
public static AnimatorProxy wrap(View view) {
AnimatorProxy proxy = PROXIES.get(view);
// This checks if the proxy already exists and whether it still is the animation of the given view
if (proxy == null || proxy != view.getAnimation()) {
proxy = new AnimatorProxy(view);
PROXIES.put(view, proxy);
}
return proxy;
}
private final WeakReference<View> mView;
private final Camera mCamera = new Camera();
private boolean mHasPivot;
private float mAlpha = 1;
private float mPivotX;
private float mPivotY;
private float mRotationX;
private float mRotationY;
private float mRotationZ;
private float mScaleX = 1;
private float mScaleY = 1;
private float mTranslationX;
private float mTranslationY;
private final RectF mBefore = new RectF();
private final RectF mAfter = new RectF();
private final Matrix mTempMatrix = new Matrix();
private AnimatorProxy(View view) {
setDuration(0); //perform transformation immediately
setFillAfter(true); //persist transformation beyond duration
view.setAnimation(this);
mView = new WeakReference<View>(view);
}
public float getAlpha() {
return mAlpha;
}
public void setAlpha(float alpha) {
if (mAlpha != alpha) {
mAlpha = alpha;
View view = mView.get();
if (view != null) {
view.invalidate();
}
}
}
public float getPivotX() {
return mPivotX;
}
public void setPivotX(float pivotX) {
if (!mHasPivot || mPivotX != pivotX) {
prepareForUpdate();
mHasPivot = true;
mPivotX = pivotX;
invalidateAfterUpdate();
}
}
public float getPivotY() {
return mPivotY;
}
public void setPivotY(float pivotY) {
if (!mHasPivot || mPivotY != pivotY) {
prepareForUpdate();
mHasPivot = true;
mPivotY = pivotY;
invalidateAfterUpdate();
}
}
public float getRotation() {
return mRotationZ;
}
public void setRotation(float rotation) {
if (mRotationZ != rotation) {
prepareForUpdate();
mRotationZ = rotation;
invalidateAfterUpdate();
}
}
public float getRotationX() {
return mRotationX;
}
public void setRotationX(float rotationX) {
if (mRotationX != rotationX) {
prepareForUpdate();
mRotationX = rotationX;
invalidateAfterUpdate();
}
}
public float getRotationY() {
return mRotationY;
}
public void setRotationY(float rotationY) {
if (mRotationY != rotationY) {
prepareForUpdate();
mRotationY = rotationY;
invalidateAfterUpdate();
}
}
public float getScaleX() {
return mScaleX;
}
public void setScaleX(float scaleX) {
if (mScaleX != scaleX) {
prepareForUpdate();
mScaleX = scaleX;
invalidateAfterUpdate();
}
}
public float getScaleY() {
return mScaleY;
}
public void setScaleY(float scaleY) {
if (mScaleY != scaleY) {
prepareForUpdate();
mScaleY = scaleY;
invalidateAfterUpdate();
}
}
public int getScrollX() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getScrollX();
}
public void setScrollX(int value) {
View view = mView.get();
if (view != null) {
view.scrollTo(value, view.getScrollY());
}
}
public int getScrollY() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getScrollY();
}
public void setScrollY(int value) {
View view = mView.get();
if (view != null) {
view.scrollTo(view.getScrollX(), value);
}
}
public float getTranslationX() {
return mTranslationX;
}
public void setTranslationX(float translationX) {
if (mTranslationX != translationX) {
prepareForUpdate();
mTranslationX = translationX;
invalidateAfterUpdate();
}
}
public float getTranslationY() {
return mTranslationY;
}
public void setTranslationY(float translationY) {
if (mTranslationY != translationY) {
prepareForUpdate();
mTranslationY = translationY;
invalidateAfterUpdate();
}
}
public float getX() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getLeft() + mTranslationX;
}
public void setX(float x) {
View view = mView.get();
if (view != null) {
setTranslationX(x - view.getLeft());
}
}
public float getY() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getTop() + mTranslationY;
}
public void setY(float y) {
View view = mView.get();
if (view != null) {
setTranslationY(y - view.getTop());
}
}
private void prepareForUpdate() {
View view = mView.get();
if (view != null) {
computeRect(mBefore, view);
}
}
private void invalidateAfterUpdate() {
View view = mView.get();
if (view == null || view.getParent() == null) {
return;
}
final RectF after = mAfter;
computeRect(after, view);
after.union(mBefore);
((View)view.getParent()).invalidate(
(int) Math.floor(after.left),
(int) Math.floor(after.top),
(int) Math.ceil(after.right),
(int) Math.ceil(after.bottom));
}
private void computeRect(final RectF r, View view) {
// compute current rectangle according to matrix transformation
final float w = view.getWidth();
final float h = view.getHeight();
// use a rectangle at 0,0 to make sure we don't run into issues with scaling
r.set(0, 0, w, h);
final Matrix m = mTempMatrix;
m.reset();
transformMatrix(m, view);
mTempMatrix.mapRect(r);
r.offset(view.getLeft(), view.getTop());
// Straighten coords if rotations flipped them
if (r.right < r.left) {
final float f = r.right;
r.right = r.left;
r.left = f;
}
if (r.bottom < r.top) {
final float f = r.top;
r.top = r.bottom;
r.bottom = f;
}
}
private void transformMatrix(Matrix m, View view) {
final float w = view.getWidth();
final float h = view.getHeight();
final boolean hasPivot = mHasPivot;
final float pX = hasPivot ? mPivotX : w / 2f;
final float pY = hasPivot ? mPivotY : h / 2f;
final float rX = mRotationX;
final float rY = mRotationY;
final float rZ = mRotationZ;
if ((rX != 0) || (rY != 0) || (rZ != 0)) {
final Camera camera = mCamera;
camera.save();
camera.rotateX(rX);
camera.rotateY(rY);
camera.rotateZ(-rZ);
camera.getMatrix(m);
camera.restore();
m.preTranslate(-pX, -pY);
m.postTranslate(pX, pY);
}
final float sX = mScaleX;
final float sY = mScaleY;
if ((sX != 1.0f) || (sY != 1.0f)) {
m.postScale(sX, sY);
final float sPX = -(pX / w) * ((sX * w) - w);
final float sPY = -(pY / h) * ((sY * h) - h);
m.postTranslate(sPX, sPY);
}
m.postTranslate(mTranslationX, mTranslationY);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
View view = mView.get();
if (view != null) {
t.setAlpha(mAlpha);
transformMatrix(t.getMatrix(), view);
}
}
}