Merge "Performance optimizations for animations and toolkit"
This commit is contained in:
@@ -83600,6 +83600,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="getColor"
|
||||
return="int"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="getOpacity"
|
||||
return="int"
|
||||
abstract="false"
|
||||
@@ -83624,6 +83635,19 @@
|
||||
<parameter name="alpha" type="int">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="setColor"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="color" type="int">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="setColorFilter"
|
||||
return="void"
|
||||
abstract="false"
|
||||
|
||||
@@ -28,12 +28,18 @@ class KeyframeSet {
|
||||
|
||||
private int mNumKeyframes;
|
||||
|
||||
ArrayList<Keyframe> mKeyframes;
|
||||
Keyframe mFirstKeyframe;
|
||||
Keyframe mLastKeyframe;
|
||||
TimeInterpolator mInterpolator; // only used in the 2-keyframe case
|
||||
ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
|
||||
|
||||
public KeyframeSet(Keyframe... keyframes) {
|
||||
mNumKeyframes = keyframes.length;
|
||||
mKeyframes = new ArrayList<Keyframe>();
|
||||
mKeyframes.addAll(Arrays.asList(keyframes));
|
||||
mNumKeyframes = mKeyframes.size();
|
||||
mFirstKeyframe = mKeyframes.get(0);
|
||||
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
|
||||
mInterpolator = mLastKeyframe.getInterpolator();
|
||||
}
|
||||
|
||||
public static KeyframeSet ofInt(int... values) {
|
||||
@@ -125,32 +131,40 @@ class KeyframeSet {
|
||||
* @return The animated value.
|
||||
*/
|
||||
public Object getValue(float fraction, TypeEvaluator evaluator) {
|
||||
// TODO: special-case 2-keyframe common case
|
||||
|
||||
// Special-case optimization for the common case of only two keyframes
|
||||
if (mNumKeyframes == 2) {
|
||||
if (mInterpolator != null) {
|
||||
fraction = mInterpolator.getInterpolation(fraction);
|
||||
}
|
||||
return evaluator.evaluate(fraction, mFirstKeyframe.getValue(),
|
||||
mLastKeyframe.getValue());
|
||||
}
|
||||
|
||||
if (fraction <= 0f) {
|
||||
final Keyframe prevKeyframe = mKeyframes.get(0);
|
||||
final Keyframe nextKeyframe = mKeyframes.get(1);
|
||||
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
|
||||
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
|
||||
return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
|
||||
final float prevFraction = mFirstKeyframe.getFraction();
|
||||
float intervalFraction = (fraction - prevFraction) /
|
||||
(nextKeyframe.getFraction() - prevFraction);
|
||||
return evaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
|
||||
nextKeyframe.getValue());
|
||||
} else if (fraction >= 1f) {
|
||||
final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
|
||||
final Keyframe nextKeyframe = mKeyframes.get(mNumKeyframes - 1);
|
||||
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
|
||||
final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
|
||||
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
|
||||
final float prevFraction = prevKeyframe.getFraction();
|
||||
float intervalFraction = (fraction - prevFraction) /
|
||||
(mLastKeyframe.getFraction() - prevFraction);
|
||||
return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
|
||||
nextKeyframe.getValue());
|
||||
mLastKeyframe.getValue());
|
||||
}
|
||||
Keyframe prevKeyframe = mKeyframes.get(0);
|
||||
Keyframe prevKeyframe = mFirstKeyframe;
|
||||
for (int i = 1; i < mNumKeyframes; ++i) {
|
||||
Keyframe nextKeyframe = mKeyframes.get(i);
|
||||
if (fraction < nextKeyframe.getFraction()) {
|
||||
@@ -158,14 +172,15 @@ class KeyframeSet {
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
|
||||
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
|
||||
final float prevFraction = prevKeyframe.getFraction();
|
||||
float intervalFraction = (fraction - prevFraction) /
|
||||
(nextKeyframe.getFraction() - prevFraction);
|
||||
return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
|
||||
nextKeyframe.getValue());
|
||||
}
|
||||
prevKeyframe = nextKeyframe;
|
||||
}
|
||||
// shouldn't get here
|
||||
return mKeyframes.get(mNumKeyframes - 1).getValue();
|
||||
// shouldn't reach here
|
||||
return mLastKeyframe.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,9 +376,14 @@ public final class ObjectAnimator extends ValueAnimator {
|
||||
*/
|
||||
@Override
|
||||
public void setTarget(Object target) {
|
||||
mTarget = target;
|
||||
// New property/values/target should cause re-initialization prior to starting
|
||||
mInitialized = false;
|
||||
if (mTarget != target) {
|
||||
mTarget = target;
|
||||
if (mTarget != null && target != null && mTarget.getClass() == target.getClass()) {
|
||||
return;
|
||||
}
|
||||
// New target type should cause re-initialization prior to starting
|
||||
mInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1142,7 +1142,7 @@ public class ValueAnimator extends Animator {
|
||||
switch (mPlayingState) {
|
||||
case RUNNING:
|
||||
case SEEKED:
|
||||
float fraction = (float)(currentTime - mStartTime) / mDuration;
|
||||
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
|
||||
if (fraction >= 1f) {
|
||||
if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
|
||||
// Time to repeat
|
||||
|
||||
@@ -6296,8 +6296,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
if (invalidateCache) {
|
||||
mPrivateFlags &= ~DRAWING_CACHE_VALID;
|
||||
}
|
||||
final ViewParent p = mParent;
|
||||
final AttachInfo ai = mAttachInfo;
|
||||
final ViewParent p = mParent;
|
||||
if (ai != null && ai.mHardwareAccelerated) {
|
||||
// fast-track for GL-enabled applications; just invalidate the whole hierarchy
|
||||
// with a null dirty rect, which tells the ViewRoot to redraw everything
|
||||
p.invalidateChild(this, null);
|
||||
return;
|
||||
}
|
||||
if (p != null && ai != null) {
|
||||
final Rect r = ai.mTmpInvalRect;
|
||||
r.set(0, 0, mRight - mLeft, mBottom - mTop);
|
||||
@@ -6321,7 +6327,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
*/
|
||||
@ViewDebug.ExportedProperty(category = "drawing")
|
||||
public boolean isOpaque() {
|
||||
return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK;
|
||||
return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK &&
|
||||
(mAlpha >= 1.0f - ViewConfiguration.ALPHA_THRESHOLD);
|
||||
}
|
||||
|
||||
private void computeOpaqueFlags() {
|
||||
@@ -8618,7 +8625,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
*/
|
||||
@RemotableViewMethod
|
||||
public void setBackgroundColor(int color) {
|
||||
setBackgroundDrawable(new ColorDrawable(color));
|
||||
if (mBGDrawable instanceof ColorDrawable) {
|
||||
((ColorDrawable) mBGDrawable).setColor(color);
|
||||
} else {
|
||||
setBackgroundDrawable(new ColorDrawable(color));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2101,6 +2101,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
final int cr = child.mRight;
|
||||
final int cb = child.mBottom;
|
||||
|
||||
final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
|
||||
|
||||
final int flags = mGroupFlags;
|
||||
|
||||
if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
|
||||
@@ -2182,7 +2184,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
}
|
||||
}
|
||||
|
||||
concatMatrix |= !child.hasIdentityMatrix();
|
||||
concatMatrix |= !childHasIdentityMatrix;
|
||||
|
||||
// Sets the flag as early as possible to allow draw() implementations
|
||||
// to call invalidate() successfully when doing animations
|
||||
@@ -2231,40 +2233,41 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
}
|
||||
|
||||
if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
|
||||
int transX = 0;
|
||||
int transY = 0;
|
||||
if (transformToApply != null || !childHasIdentityMatrix) {
|
||||
int transX = 0;
|
||||
int transY = 0;
|
||||
|
||||
if (hasNoCache) {
|
||||
transX = -sx;
|
||||
transY = -sy;
|
||||
}
|
||||
if (hasNoCache) {
|
||||
transX = -sx;
|
||||
transY = -sy;
|
||||
}
|
||||
|
||||
if (transformToApply != null) {
|
||||
if (concatMatrix) {
|
||||
// Undo the scroll translation, apply the transformation matrix,
|
||||
// then redo the scroll translate to get the correct result.
|
||||
if (transformToApply != null) {
|
||||
if (concatMatrix) {
|
||||
// Undo the scroll translation, apply the transformation matrix,
|
||||
// then redo the scroll translate to get the correct result.
|
||||
canvas.translate(-transX, -transY);
|
||||
canvas.concat(transformToApply.getMatrix());
|
||||
canvas.translate(transX, transY);
|
||||
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
|
||||
}
|
||||
|
||||
float transformAlpha = transformToApply.getAlpha();
|
||||
if (transformAlpha < 1.0f) {
|
||||
alpha *= transformToApply.getAlpha();
|
||||
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (!childHasIdentityMatrix) {
|
||||
canvas.translate(-transX, -transY);
|
||||
canvas.concat(transformToApply.getMatrix());
|
||||
canvas.concat(child.getMatrix());
|
||||
canvas.translate(transX, transY);
|
||||
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
|
||||
}
|
||||
|
||||
float transformAlpha = transformToApply.getAlpha();
|
||||
if (transformAlpha < 1.0f) {
|
||||
alpha *= transformToApply.getAlpha();
|
||||
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (!child.hasIdentityMatrix()) {
|
||||
canvas.translate(-transX, -transY);
|
||||
canvas.concat(child.getMatrix());
|
||||
canvas.translate(transX, transY);
|
||||
}
|
||||
|
||||
if (alpha < 1.0f) {
|
||||
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
|
||||
|
||||
if (hasNoCache) {
|
||||
final int multipliedAlpha = (int) (255 * alpha);
|
||||
if (!child.onSetAlpha(multipliedAlpha)) {
|
||||
@@ -3209,19 +3212,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
|
||||
final AttachInfo attachInfo = mAttachInfo;
|
||||
if (attachInfo != null) {
|
||||
final int[] location = attachInfo.mInvalidateChildLocation;
|
||||
location[CHILD_LEFT_INDEX] = child.mLeft;
|
||||
location[CHILD_TOP_INDEX] = child.mTop;
|
||||
Matrix childMatrix = child.getMatrix();
|
||||
if (!childMatrix.isIdentity()) {
|
||||
RectF boundingRect = attachInfo.mTmpTransformRect;
|
||||
boundingRect.set(dirty);
|
||||
childMatrix.mapRect(boundingRect);
|
||||
dirty.set((int) boundingRect.left, (int) boundingRect.top,
|
||||
(int) (boundingRect.right + 0.5f),
|
||||
(int) (boundingRect.bottom + 0.5f));
|
||||
}
|
||||
|
||||
// If the child is drawing an animation, we want to copy this flag onto
|
||||
// ourselves and the parent to make sure the invalidate request goes
|
||||
// through
|
||||
@@ -3229,45 +3219,95 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
|
||||
// Check whether the child that requests the invalidate is fully opaque
|
||||
final boolean isOpaque = child.isOpaque() && !drawAnimation &&
|
||||
child.getAnimation() != null;
|
||||
child.getAnimation() == null;
|
||||
// Mark the child as dirty, using the appropriate flag
|
||||
// Make sure we do not set both flags at the same time
|
||||
final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
|
||||
|
||||
do {
|
||||
View view = null;
|
||||
if (parent instanceof View) {
|
||||
view = (View) parent;
|
||||
if (dirty == null) {
|
||||
do {
|
||||
View view = null;
|
||||
if (parent instanceof View) {
|
||||
view = (View) parent;
|
||||
if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
|
||||
// already marked dirty - we're done
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (drawAnimation) {
|
||||
if (view != null) {
|
||||
view.mPrivateFlags |= DRAW_ANIMATION;
|
||||
} else if (parent instanceof ViewRoot) {
|
||||
((ViewRoot) parent).mIsAnimating = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (parent instanceof ViewRoot) {
|
||||
((ViewRoot) parent).invalidate();
|
||||
parent = null;
|
||||
} else if (view != null) {
|
||||
if ((mPrivateFlags & DRAWN) == DRAWN) {
|
||||
view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
|
||||
if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
|
||||
view.mPrivateFlags =
|
||||
(view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
|
||||
}
|
||||
parent = view.mParent;
|
||||
} else {
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
} while (parent != null);
|
||||
} else {
|
||||
final int[] location = attachInfo.mInvalidateChildLocation;
|
||||
location[CHILD_LEFT_INDEX] = child.mLeft;
|
||||
location[CHILD_TOP_INDEX] = child.mTop;
|
||||
Matrix childMatrix = child.getMatrix();
|
||||
if (!childMatrix.isIdentity()) {
|
||||
RectF boundingRect = attachInfo.mTmpTransformRect;
|
||||
boundingRect.set(dirty);
|
||||
childMatrix.mapRect(boundingRect);
|
||||
dirty.set((int) boundingRect.left, (int) boundingRect.top,
|
||||
(int) (boundingRect.right + 0.5f),
|
||||
(int) (boundingRect.bottom + 0.5f));
|
||||
}
|
||||
|
||||
if (drawAnimation) {
|
||||
do {
|
||||
View view = null;
|
||||
if (parent instanceof View) {
|
||||
view = (View) parent;
|
||||
}
|
||||
|
||||
if (drawAnimation) {
|
||||
if (view != null) {
|
||||
view.mPrivateFlags |= DRAW_ANIMATION;
|
||||
} else if (parent instanceof ViewRoot) {
|
||||
((ViewRoot) parent).mIsAnimating = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
|
||||
// flag coming from the child that initiated the invalidate
|
||||
if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
|
||||
view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
|
||||
}
|
||||
|
||||
parent = parent.invalidateChildInParent(location, dirty);
|
||||
if (view != null) {
|
||||
view.mPrivateFlags |= DRAW_ANIMATION;
|
||||
} else if (parent instanceof ViewRoot) {
|
||||
((ViewRoot) parent).mIsAnimating = true;
|
||||
// Account for transform on current parent
|
||||
Matrix m = view.getMatrix();
|
||||
if (!m.isIdentity()) {
|
||||
RectF boundingRect = attachInfo.mTmpTransformRect;
|
||||
boundingRect.set(dirty);
|
||||
m.mapRect(boundingRect);
|
||||
dirty.set((int) boundingRect.left, (int) boundingRect.top,
|
||||
(int) (boundingRect.right + 0.5f),
|
||||
(int) (boundingRect.bottom + 0.5f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
|
||||
// flag coming from the child that initiated the invalidate
|
||||
if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
|
||||
view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
|
||||
}
|
||||
|
||||
parent = parent.invalidateChildInParent(location, dirty);
|
||||
if (view != null) {
|
||||
// Account for transform on current parent
|
||||
Matrix m = view.getMatrix();
|
||||
if (!m.isIdentity()) {
|
||||
RectF boundingRect = attachInfo.mTmpTransformRect;
|
||||
boundingRect.set(dirty);
|
||||
m.mapRect(boundingRect);
|
||||
dirty.set((int) boundingRect.left, (int) boundingRect.top,
|
||||
(int) (boundingRect.right + 0.5f),
|
||||
(int) (boundingRect.bottom + 0.5f));
|
||||
}
|
||||
}
|
||||
} while (parent != null);
|
||||
} while (parent != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -543,6 +543,11 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
|
||||
public void invalidateChild(View child, Rect dirty) {
|
||||
checkThread();
|
||||
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
|
||||
if (dirty == null) {
|
||||
// Fast invalidation for GL-enabled applications; GL must redraw everything
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
if (mCurScrollY != 0 || mTranslator != null) {
|
||||
mTempRect.set(dirty);
|
||||
dirty = mTempRect;
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ColorDrawable extends Drawable {
|
||||
*/
|
||||
public ColorDrawable(int color) {
|
||||
this(null);
|
||||
mState.mBaseColor = mState.mUseColor = color;
|
||||
setColor(color);
|
||||
}
|
||||
|
||||
private ColorDrawable(ColorState state) {
|
||||
@@ -71,6 +71,25 @@ public class ColorDrawable extends Drawable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the drawable's color value.
|
||||
*
|
||||
* @return int The color to draw.
|
||||
*/
|
||||
public int getColor() {
|
||||
return mState.mUseColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the drawable's color value. This action will clobber the results of prior calls to
|
||||
* {@link #setAlpha(int)} on this object, which side-affected the underlying color.
|
||||
*
|
||||
* @param color The color to draw.
|
||||
*/
|
||||
public void setColor(int color) {
|
||||
mState.mBaseColor = mState.mUseColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the alpha value of this drawable's color.
|
||||
*
|
||||
@@ -131,7 +150,7 @@ public class ColorDrawable extends Drawable {
|
||||
}
|
||||
|
||||
final static class ColorState extends ConstantState {
|
||||
int mBaseColor; // initial color. never changes
|
||||
int mBaseColor; // base color, independent of setAlpha()
|
||||
int mUseColor; // basecolor modulated by setAlpha()
|
||||
int mChangingConfigurations;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user