Merge "Add CanvasProperty for drawCircle"
This commit is contained in:
@@ -18,6 +18,7 @@ package android.view;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.CanvasProperty;
|
||||
import android.graphics.DrawFilter;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.NinePatch;
|
||||
@@ -888,6 +889,16 @@ class GLES20Canvas extends HardwareCanvas {
|
||||
private static native void nDrawCircle(long renderer, float cx, float cy,
|
||||
float radius, long paint);
|
||||
|
||||
@Override
|
||||
public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
|
||||
CanvasProperty<Float> radius, CanvasProperty<Paint> paint) {
|
||||
nDrawCircle(mRenderer, cx.getNativeContainer(), cy.getNativeContainer(),
|
||||
radius.getNativeContainer(), paint.getNativeContainer());
|
||||
}
|
||||
|
||||
private static native void nDrawCircle(long renderer, long propCx,
|
||||
long propCy, long propRadius, long propPaint);
|
||||
|
||||
@Override
|
||||
public void drawColor(int color) {
|
||||
drawColor(color, PorterDuff.Mode.SRC_OVER);
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.view;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.CanvasProperty;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
|
||||
@@ -189,4 +190,7 @@ public abstract class HardwareCanvas extends Canvas {
|
||||
* @hide
|
||||
*/
|
||||
abstract void clearLayerUpdates();
|
||||
|
||||
public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
|
||||
CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.graphics.CanvasProperty;
|
||||
import android.graphics.Paint;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -26,18 +28,22 @@ import java.lang.ref.WeakReference;
|
||||
public final class RenderNodeAnimator {
|
||||
|
||||
// Keep in sync with enum RenderProperty in Animator.h
|
||||
private static final int TRANSLATION_X = 0;
|
||||
private static final int TRANSLATION_Y = 1;
|
||||
private static final int TRANSLATION_Z = 2;
|
||||
private static final int SCALE_X = 3;
|
||||
private static final int SCALE_Y = 4;
|
||||
private static final int ROTATION = 5;
|
||||
private static final int ROTATION_X = 6;
|
||||
private static final int ROTATION_Y = 7;
|
||||
private static final int X = 8;
|
||||
private static final int Y = 9;
|
||||
private static final int Z = 10;
|
||||
private static final int ALPHA = 11;
|
||||
public static final int TRANSLATION_X = 0;
|
||||
public static final int TRANSLATION_Y = 1;
|
||||
public static final int TRANSLATION_Z = 2;
|
||||
public static final int SCALE_X = 3;
|
||||
public static final int SCALE_Y = 4;
|
||||
public static final int ROTATION = 5;
|
||||
public static final int ROTATION_X = 6;
|
||||
public static final int ROTATION_Y = 7;
|
||||
public static final int X = 8;
|
||||
public static final int Y = 9;
|
||||
public static final int Z = 10;
|
||||
public static final int ALPHA = 11;
|
||||
|
||||
// Keep in sync with enum PaintFields in Animator.h
|
||||
public static final int PAINT_STROKE_WIDTH = 0;
|
||||
public static final int PAINT_ALPHA = 1;
|
||||
|
||||
// ViewPropertyAnimator uses a mask for its values, we need to remap them
|
||||
// to the enum values here. RenderPropertyAnimator can't use the mask values
|
||||
@@ -59,8 +65,8 @@ public final class RenderNodeAnimator {
|
||||
}};
|
||||
|
||||
// Keep in sync DeltaValueType in Animator.h
|
||||
private static final int DELTA_TYPE_ABSOLUTE = 0;
|
||||
private static final int DELTA_TYPE_DELTA = 1;
|
||||
public static final int DELTA_TYPE_ABSOLUTE = 0;
|
||||
public static final int DELTA_TYPE_DELTA = 1;
|
||||
|
||||
private RenderNode mTarget;
|
||||
private long mNativePtr;
|
||||
@@ -74,6 +80,19 @@ public final class RenderNodeAnimator {
|
||||
property, deltaType, deltaValue);
|
||||
}
|
||||
|
||||
public RenderNodeAnimator(CanvasProperty<Float> property, int deltaType, float deltaValue) {
|
||||
mNativePtr = nCreateCanvasPropertyFloatAnimator(
|
||||
new WeakReference<RenderNodeAnimator>(this),
|
||||
property.getNativeContainer(), deltaType, deltaValue);
|
||||
}
|
||||
|
||||
public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField,
|
||||
int deltaType, float deltaValue) {
|
||||
mNativePtr = nCreateCanvasPropertyPaintAnimator(
|
||||
new WeakReference<RenderNodeAnimator>(this),
|
||||
property.getNativeContainer(), paintField, deltaType, deltaValue);
|
||||
}
|
||||
|
||||
public void start(View target) {
|
||||
mTarget = target.mRenderNode;
|
||||
mTarget.addAnimator(this);
|
||||
@@ -117,6 +136,10 @@ public final class RenderNodeAnimator {
|
||||
|
||||
private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
|
||||
int property, int deltaValueType, float deltaValue);
|
||||
private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis,
|
||||
long canvasProperty, int deltaValueType, float deltaValue);
|
||||
private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis,
|
||||
long canvasProperty, int paintField, int deltaValueType, float deltaValue);
|
||||
private static native void nSetDuration(long nativePtr, int duration);
|
||||
private static native void nUnref(long nativePtr);
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ LOCAL_SRC_FILES:= \
|
||||
android/graphics/BitmapFactory.cpp \
|
||||
android/graphics/Camera.cpp \
|
||||
android/graphics/Canvas.cpp \
|
||||
android/graphics/CanvasProperty.cpp \
|
||||
android/graphics/ColorFilter.cpp \
|
||||
android/graphics/DrawFilter.cpp \
|
||||
android/graphics/CreateJavaOutputStreamAdaptor.cpp \
|
||||
|
||||
@@ -105,6 +105,7 @@ extern int register_android_content_StringBlock(JNIEnv* env);
|
||||
extern int register_android_content_XmlBlock(JNIEnv* env);
|
||||
extern int register_android_emoji_EmojiFactory(JNIEnv* env);
|
||||
extern int register_android_graphics_Canvas(JNIEnv* env);
|
||||
extern int register_android_graphics_CanvasProperty(JNIEnv* env);
|
||||
extern int register_android_graphics_ColorFilter(JNIEnv* env);
|
||||
extern int register_android_graphics_DrawFilter(JNIEnv* env);
|
||||
extern int register_android_graphics_Matrix(JNIEnv* env);
|
||||
@@ -1221,6 +1222,7 @@ static const RegJNIRec gRegJNI[] = {
|
||||
REG_JNI(register_android_graphics_Camera),
|
||||
REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
|
||||
REG_JNI(register_android_graphics_Canvas),
|
||||
REG_JNI(register_android_graphics_CanvasProperty),
|
||||
REG_JNI(register_android_graphics_ColorFilter),
|
||||
REG_JNI(register_android_graphics_DrawFilter),
|
||||
REG_JNI(register_android_graphics_Interpolator),
|
||||
|
||||
68
core/jni/android/graphics/CanvasProperty.cpp
Normal file
68
core/jni/android/graphics/CanvasProperty.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 20014 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.
|
||||
*/
|
||||
|
||||
#include "jni.h"
|
||||
#include "GraphicsJNI.h"
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
|
||||
#include <utils/VirtualLightRefBase.h>
|
||||
#include <CanvasProperty.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
using namespace uirenderer;
|
||||
|
||||
#ifdef USE_OPENGL_RENDERER
|
||||
|
||||
static jlong incRef(VirtualLightRefBase* ptr) {
|
||||
ptr->incStrong(0);
|
||||
return reinterpret_cast<jlong>(ptr);
|
||||
}
|
||||
|
||||
static jlong createFloat(JNIEnv* env, jobject clazz, jfloat initialValue) {
|
||||
return incRef(new CanvasPropertyPrimitive(initialValue));
|
||||
}
|
||||
|
||||
static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) {
|
||||
const SkPaint* paint = reinterpret_cast<const SkPaint*>(paintPtr);
|
||||
return incRef(new CanvasPropertyPaint(*paint));
|
||||
}
|
||||
|
||||
static void unref(JNIEnv* env, jobject clazz, jlong containerPtr) {
|
||||
reinterpret_cast<VirtualLightRefBase*>(containerPtr)->decStrong(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// JNI Glue
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const char* const kClassPathName = "android/graphics/CanvasProperty";
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
#ifdef USE_OPENGL_RENDERER
|
||||
{ "nCreateFloat", "(F)J", (void*) createFloat },
|
||||
{ "nCreatePaint", "(J)J", (void*) createPaint },
|
||||
{ "nUnref", "(J)V", (void*) unref },
|
||||
#endif
|
||||
};
|
||||
|
||||
int register_android_graphics_CanvasProperty(JNIEnv* env) {
|
||||
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
|
||||
}
|
||||
|
||||
}; // namespace android
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <Stencil.h>
|
||||
#include <Rect.h>
|
||||
#include <RenderNode.h>
|
||||
#include <CanvasProperty.h>
|
||||
|
||||
#include <TextLayout.h>
|
||||
#include <TextLayoutCache.h>
|
||||
@@ -544,6 +545,16 @@ static void android_view_GLES20Canvas_drawCircle(JNIEnv* env, jobject clazz,
|
||||
renderer->drawCircle(x, y, radius, paint);
|
||||
}
|
||||
|
||||
static void android_view_GLES20Canvas_drawCircleProps(JNIEnv* env, jobject clazz,
|
||||
jlong rendererPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
|
||||
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
|
||||
CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
|
||||
CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
|
||||
CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
|
||||
CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
|
||||
renderer->drawCircle(xProp, yProp, radiusProp, paintProp);
|
||||
}
|
||||
|
||||
static void android_view_GLES20Canvas_drawOval(JNIEnv* env, jobject clazz,
|
||||
jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
|
||||
jlong paintPtr) {
|
||||
@@ -1041,6 +1052,7 @@ static JNINativeMethod gMethods[] = {
|
||||
{ "nDrawRects", "(J[FIJ)V", (void*) android_view_GLES20Canvas_drawRects },
|
||||
{ "nDrawRoundRect", "(JFFFFFFJ)V", (void*) android_view_GLES20Canvas_drawRoundRect },
|
||||
{ "nDrawCircle", "(JFFFJ)V", (void*) android_view_GLES20Canvas_drawCircle },
|
||||
{ "nDrawCircle", "(JJJJJ)V", (void*) android_view_GLES20Canvas_drawCircleProps },
|
||||
{ "nDrawOval", "(JFFFFJ)V", (void*) android_view_GLES20Canvas_drawOval },
|
||||
{ "nDrawArc", "(JFFFFFFZJ)V", (void*) android_view_GLES20Canvas_drawArc },
|
||||
{ "nDrawPoints", "(J[FIIJ)V", (void*) android_view_GLES20Canvas_drawPoints },
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#define LOG_TAG "OpenGLRenderer"
|
||||
|
||||
#include "android_view_RenderNodeAnimator.h"
|
||||
|
||||
#include "jni.h"
|
||||
#include "GraphicsJNI.h"
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
@@ -47,46 +45,93 @@ static JNIEnv* getEnv(JavaVM* vm) {
|
||||
return env;
|
||||
}
|
||||
|
||||
RenderNodeAnimator::RenderNodeAnimator(JNIEnv* env, jobject weakThis,
|
||||
RenderProperty property, DeltaValueType deltaType, float delta)
|
||||
: RenderPropertyAnimator(property, deltaType, delta) {
|
||||
mWeakThis = env->NewGlobalRef(weakThis);
|
||||
env->GetJavaVM(&mJvm);
|
||||
}
|
||||
class AnimationListenerBridge : public AnimationListener {
|
||||
public:
|
||||
// This holds a strong reference to a Java WeakReference<T> object. This avoids
|
||||
// cyclic-references-of-doom. If you think "I know, just use NewWeakGlobalRef!"
|
||||
// then you end up with basically a PhantomReference, which is totally not
|
||||
// what we want.
|
||||
AnimationListenerBridge(JNIEnv* env, jobject weakThis) {
|
||||
mWeakThis = env->NewGlobalRef(weakThis);
|
||||
env->GetJavaVM(&mJvm);
|
||||
}
|
||||
|
||||
RenderNodeAnimator::~RenderNodeAnimator() {
|
||||
JNIEnv* env = getEnv(mJvm);
|
||||
env->DeleteGlobalRef(mWeakThis);
|
||||
mWeakThis = NULL;
|
||||
}
|
||||
virtual ~AnimationListenerBridge() {
|
||||
JNIEnv* env = getEnv(mJvm);
|
||||
env->DeleteGlobalRef(mWeakThis);
|
||||
mWeakThis = NULL;
|
||||
}
|
||||
|
||||
void RenderNodeAnimator::callOnFinished() {
|
||||
JNIEnv* env = getEnv(mJvm);
|
||||
env->CallStaticVoidMethod(
|
||||
gRenderNodeAnimatorClassInfo.clazz,
|
||||
gRenderNodeAnimatorClassInfo.callOnFinished,
|
||||
mWeakThis);
|
||||
}
|
||||
virtual void onAnimationFinished(BaseAnimator*) {
|
||||
JNIEnv* env = getEnv(mJvm);
|
||||
env->CallStaticVoidMethod(
|
||||
gRenderNodeAnimatorClassInfo.clazz,
|
||||
gRenderNodeAnimatorClassInfo.callOnFinished,
|
||||
mWeakThis);
|
||||
}
|
||||
|
||||
static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis,
|
||||
jint property, jint deltaType, jfloat deltaValue) {
|
||||
LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderNodeAnimator::ALPHA,
|
||||
private:
|
||||
JavaVM* mJvm;
|
||||
jobject mWeakThis;
|
||||
};
|
||||
|
||||
static inline RenderPropertyAnimator::RenderProperty toRenderProperty(jint property) {
|
||||
LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderPropertyAnimator::ALPHA,
|
||||
"Invalid property %d", property);
|
||||
return static_cast<RenderPropertyAnimator::RenderProperty>(property);
|
||||
}
|
||||
|
||||
static inline RenderPropertyAnimator::DeltaValueType toDeltaType(jint deltaType) {
|
||||
LOG_ALWAYS_FATAL_IF(deltaType != RenderPropertyAnimator::DELTA
|
||||
&& deltaType != RenderPropertyAnimator::ABSOLUTE,
|
||||
"Invalid delta type %d", deltaType);
|
||||
return static_cast<RenderPropertyAnimator::DeltaValueType>(deltaType);
|
||||
}
|
||||
|
||||
RenderNodeAnimator* animator = new RenderNodeAnimator(env, weakThis,
|
||||
static_cast<RenderPropertyAnimator::RenderProperty>(property),
|
||||
static_cast<RenderPropertyAnimator::DeltaValueType>(deltaType),
|
||||
deltaValue);
|
||||
static inline CanvasPropertyPaintAnimator::PaintField toPaintField(jint field) {
|
||||
LOG_ALWAYS_FATAL_IF(field < 0
|
||||
|| field > CanvasPropertyPaintAnimator::ALPHA,
|
||||
"Invalid paint field %d", field);
|
||||
return static_cast<CanvasPropertyPaintAnimator::PaintField>(field);
|
||||
}
|
||||
|
||||
static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis,
|
||||
jint propertyRaw, jint deltaTypeRaw, jfloat deltaValue) {
|
||||
RenderPropertyAnimator::RenderProperty property = toRenderProperty(propertyRaw);
|
||||
RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
|
||||
|
||||
BaseAnimator* animator = new RenderPropertyAnimator(property, deltaType, deltaValue);
|
||||
animator->incStrong(0);
|
||||
animator->setListener(new AnimationListenerBridge(env, weakThis));
|
||||
return reinterpret_cast<jlong>( animator );
|
||||
}
|
||||
|
||||
static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz,
|
||||
jobject weakThis, jlong canvasPropertyPtr, jint deltaTypeRaw, jfloat deltaValue) {
|
||||
RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
|
||||
CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr);
|
||||
BaseAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, deltaType, deltaValue);
|
||||
animator->incStrong(0);
|
||||
animator->setListener(new AnimationListenerBridge(env, weakThis));
|
||||
return reinterpret_cast<jlong>( animator );
|
||||
}
|
||||
|
||||
static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz,
|
||||
jobject weakThis, jlong canvasPropertyPtr, jint paintFieldRaw,
|
||||
jint deltaTypeRaw, jfloat deltaValue) {
|
||||
RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
|
||||
CanvasPropertyPaint* canvasProperty = reinterpret_cast<CanvasPropertyPaint*>(canvasPropertyPtr);
|
||||
CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw);
|
||||
BaseAnimator* animator = new CanvasPropertyPaintAnimator(
|
||||
canvasProperty, paintField, deltaType, deltaValue);
|
||||
animator->incStrong(0);
|
||||
animator->setListener(new AnimationListenerBridge(env, weakThis));
|
||||
return reinterpret_cast<jlong>( animator );
|
||||
}
|
||||
|
||||
static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jint duration) {
|
||||
LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative");
|
||||
RenderNodeAnimator* animator = reinterpret_cast<RenderNodeAnimator*>(animatorPtr);
|
||||
BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr);
|
||||
animator->setDuration(duration);
|
||||
}
|
||||
|
||||
@@ -106,6 +151,8 @@ const char* const kClassPathName = "android/view/RenderNodeAnimator";
|
||||
static JNINativeMethod gMethods[] = {
|
||||
#ifdef USE_OPENGL_RENDERER
|
||||
{ "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IIF)J", (void*) createAnimator },
|
||||
{ "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyFloatAnimator },
|
||||
{ "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIIF)J", (void*) createCanvasPropertyPaintAnimator },
|
||||
{ "nSetDuration", "(JI)V", (void*) setDuration },
|
||||
{ "nUnref", "(J)V", (void*) unref },
|
||||
#endif
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <android_runtime/android_view_Surface.h>
|
||||
#include <system/window.h>
|
||||
|
||||
#include "android_view_RenderNodeAnimator.h"
|
||||
#include <Animator.h>
|
||||
#include <RenderNode.h>
|
||||
#include <renderthread/RenderProxy.h>
|
||||
#include <renderthread/RenderTask.h>
|
||||
@@ -67,26 +67,34 @@ private:
|
||||
jobject mRunnable;
|
||||
};
|
||||
|
||||
class OnFinishedEvent {
|
||||
public:
|
||||
OnFinishedEvent(BaseAnimator* animator, AnimationListener* listener)
|
||||
: animator(animator), listener(listener) {}
|
||||
sp<BaseAnimator> animator;
|
||||
sp<AnimationListener> listener;
|
||||
};
|
||||
|
||||
class InvokeAnimationListeners : public MessageHandler {
|
||||
public:
|
||||
InvokeAnimationListeners(std::vector< sp<RenderNodeAnimator> >& animators) {
|
||||
mAnimators.swap(animators);
|
||||
InvokeAnimationListeners(std::vector<OnFinishedEvent>& events) {
|
||||
mOnFinishedEvents.swap(events);
|
||||
}
|
||||
|
||||
static void callOnFinished(const sp<RenderNodeAnimator>& animator) {
|
||||
animator->callOnFinished();
|
||||
static void callOnFinished(OnFinishedEvent& event) {
|
||||
event.listener->onAnimationFinished(event.animator.get());
|
||||
}
|
||||
|
||||
virtual void handleMessage(const Message& message) {
|
||||
std::for_each(mAnimators.begin(), mAnimators.end(), callOnFinished);
|
||||
mAnimators.clear();
|
||||
std::for_each(mOnFinishedEvents.begin(), mOnFinishedEvents.end(), callOnFinished);
|
||||
mOnFinishedEvents.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector< sp<RenderNodeAnimator> > mAnimators;
|
||||
std::vector<OnFinishedEvent> mOnFinishedEvents;
|
||||
};
|
||||
|
||||
class RootRenderNode : public RenderNode, public AnimationListener {
|
||||
class RootRenderNode : public RenderNode, public AnimationHook {
|
||||
public:
|
||||
RootRenderNode() : RenderNode() {
|
||||
mLooper = Looper::getForThread();
|
||||
@@ -96,27 +104,27 @@ public:
|
||||
|
||||
virtual ~RootRenderNode() {}
|
||||
|
||||
void onAnimationFinished(const sp<RenderPropertyAnimator>& animator) {
|
||||
mFinishedAnimators.push_back(
|
||||
reinterpret_cast<RenderNodeAnimator*>(animator.get()));
|
||||
virtual void callOnFinished(BaseAnimator* animator, AnimationListener* listener) {
|
||||
OnFinishedEvent event(animator, listener);
|
||||
mOnFinishedEvents.push_back(event);
|
||||
}
|
||||
|
||||
virtual void prepareTree(TreeInfo& info) {
|
||||
info.animationListener = this;
|
||||
info.animationHook = this;
|
||||
RenderNode::prepareTree(info);
|
||||
info.animationListener = NULL;
|
||||
info.animationHook = NULL;
|
||||
|
||||
// post all the finished stuff
|
||||
if (mFinishedAnimators.size()) {
|
||||
if (mOnFinishedEvents.size()) {
|
||||
sp<InvokeAnimationListeners> message
|
||||
= new InvokeAnimationListeners(mFinishedAnimators);
|
||||
= new InvokeAnimationListeners(mOnFinishedEvents);
|
||||
mLooper->sendMessage(message, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
sp<Looper> mLooper;
|
||||
std::vector< sp<RenderNodeAnimator> > mFinishedAnimators;
|
||||
std::vector<OnFinishedEvent> mOnFinishedEvents;
|
||||
};
|
||||
|
||||
static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz,
|
||||
|
||||
56
graphics/java/android/graphics/CanvasProperty.java
Normal file
56
graphics/java/android/graphics/CanvasProperty.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 android.graphics;
|
||||
|
||||
/**
|
||||
* TODO: Make public?
|
||||
* @hide
|
||||
*/
|
||||
public final class CanvasProperty<T> {
|
||||
private long mNativeContainer;
|
||||
|
||||
public static CanvasProperty<Float> createFloat(float initialValue) {
|
||||
return new CanvasProperty<Float>(nCreateFloat(initialValue));
|
||||
}
|
||||
|
||||
public static CanvasProperty<Paint> createPaint(Paint initialValue) {
|
||||
return new CanvasProperty<Paint>(nCreatePaint(initialValue.mNativePaint));
|
||||
}
|
||||
|
||||
private CanvasProperty(long nativeContainer) {
|
||||
mNativeContainer = nativeContainer;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public long getNativeContainer() {
|
||||
return mNativeContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
nUnref(mNativeContainer);
|
||||
mNativeContainer = 0;
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
private static native long nCreateFloat(float initialValue);
|
||||
private static native long nCreatePaint(long initialValuePaintPtr);
|
||||
private static native void nUnref(long ptr);
|
||||
}
|
||||
@@ -20,129 +20,12 @@
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "RenderNode.h"
|
||||
#include "RenderProperties.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
/************************************************************
|
||||
* Private header
|
||||
************************************************************/
|
||||
|
||||
typedef void (RenderProperties::*SetFloatProperty)(float value);
|
||||
typedef float (RenderProperties::*GetFloatProperty)() const;
|
||||
|
||||
struct PropertyAccessors {
|
||||
GetFloatProperty getter;
|
||||
SetFloatProperty setter;
|
||||
};
|
||||
|
||||
// Maps RenderProperty enum to accessors
|
||||
static const PropertyAccessors PROPERTY_ACCESSOR_LUT[] = {
|
||||
{&RenderProperties::getTranslationX, &RenderProperties::setTranslationX },
|
||||
{&RenderProperties::getTranslationY, &RenderProperties::setTranslationY },
|
||||
{&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
|
||||
{&RenderProperties::getScaleX, &RenderProperties::setScaleX },
|
||||
{&RenderProperties::getScaleY, &RenderProperties::setScaleY },
|
||||
{&RenderProperties::getRotation, &RenderProperties::setRotation },
|
||||
{&RenderProperties::getRotationX, &RenderProperties::setRotationX },
|
||||
{&RenderProperties::getRotationY, &RenderProperties::setRotationY },
|
||||
{&RenderProperties::getX, &RenderProperties::setX },
|
||||
{&RenderProperties::getY, &RenderProperties::setY },
|
||||
{&RenderProperties::getZ, &RenderProperties::setZ },
|
||||
{&RenderProperties::getAlpha, &RenderProperties::setAlpha },
|
||||
};
|
||||
|
||||
// Helper class to contain generic animator helpers
|
||||
class BaseAnimator {
|
||||
public:
|
||||
BaseAnimator();
|
||||
virtual ~BaseAnimator();
|
||||
|
||||
void setInterpolator(Interpolator* interpolator);
|
||||
void setDuration(nsecs_t durationInMs);
|
||||
|
||||
bool isFinished() { return mPlayState == FINISHED; }
|
||||
|
||||
protected:
|
||||
// This is the main animation entrypoint that subclasses should call
|
||||
// to generate the onAnimation* lifecycle events
|
||||
// Returns true if the animation has finished, false otherwise
|
||||
bool animateFrame(nsecs_t frameTime);
|
||||
|
||||
// Called when PlayState switches from PENDING to RUNNING
|
||||
virtual void onAnimationStarted() {}
|
||||
virtual void onAnimationUpdated(float fraction) = 0;
|
||||
virtual void onAnimationFinished() {}
|
||||
|
||||
private:
|
||||
enum PlayState {
|
||||
PENDING,
|
||||
RUNNING,
|
||||
FINISHED,
|
||||
};
|
||||
|
||||
Interpolator* mInterpolator;
|
||||
PlayState mPlayState;
|
||||
long mStartTime;
|
||||
long mDuration;
|
||||
};
|
||||
|
||||
// Hide the base classes & private bits from the exported RenderPropertyAnimator
|
||||
// in this Impl class so that subclasses of RenderPropertyAnimator don't require
|
||||
// knowledge of the inner guts but only the public virtual methods.
|
||||
// Animates a single property
|
||||
class RenderPropertyAnimatorImpl : public BaseAnimator {
|
||||
public:
|
||||
RenderPropertyAnimatorImpl(GetFloatProperty getter, SetFloatProperty setter,
|
||||
RenderPropertyAnimator::DeltaValueType deltaType, float delta);
|
||||
~RenderPropertyAnimatorImpl();
|
||||
|
||||
bool animate(RenderProperties* target, TreeInfo& info);
|
||||
|
||||
protected:
|
||||
virtual void onAnimationStarted();
|
||||
virtual void onAnimationUpdated(float fraction);
|
||||
|
||||
private:
|
||||
// mTarget is only valid inside animate()
|
||||
RenderProperties* mTarget;
|
||||
GetFloatProperty mGetter;
|
||||
SetFloatProperty mSetter;
|
||||
|
||||
RenderPropertyAnimator::DeltaValueType mDeltaValueType;
|
||||
float mDeltaValue;
|
||||
float mFromValue;
|
||||
};
|
||||
|
||||
RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property,
|
||||
DeltaValueType deltaType, float deltaValue) {
|
||||
PropertyAccessors pa = PROPERTY_ACCESSOR_LUT[property];
|
||||
mImpl = new RenderPropertyAnimatorImpl(pa.getter, pa.setter, deltaType, deltaValue);
|
||||
}
|
||||
|
||||
RenderPropertyAnimator::~RenderPropertyAnimator() {
|
||||
delete mImpl;
|
||||
mImpl = NULL;
|
||||
}
|
||||
|
||||
void RenderPropertyAnimator::setInterpolator(Interpolator* interpolator) {
|
||||
mImpl->setInterpolator(interpolator);
|
||||
}
|
||||
|
||||
void RenderPropertyAnimator::setDuration(nsecs_t durationInMs) {
|
||||
mImpl->setDuration(durationInMs);
|
||||
}
|
||||
|
||||
bool RenderPropertyAnimator::isFinished() {
|
||||
return mImpl->isFinished();
|
||||
}
|
||||
|
||||
bool RenderPropertyAnimator::animate(RenderProperties* target, TreeInfo& info) {
|
||||
return mImpl->animate(target, info);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
* Base animator
|
||||
************************************************************/
|
||||
@@ -168,10 +51,10 @@ void BaseAnimator::setDuration(nsecs_t duration) {
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
bool BaseAnimator::animateFrame(nsecs_t frameTime) {
|
||||
bool BaseAnimator::animateFrame(TreeInfo& info) {
|
||||
if (mPlayState == PENDING) {
|
||||
mPlayState = RUNNING;
|
||||
mStartTime = frameTime;
|
||||
mStartTime = info.frameTimeMs;
|
||||
// No interpolator was set, use the default
|
||||
if (!mInterpolator) {
|
||||
setInterpolator(Interpolator::createDefaultInterpolator());
|
||||
@@ -181,7 +64,7 @@ bool BaseAnimator::animateFrame(nsecs_t frameTime) {
|
||||
|
||||
float fraction = 1.0f;
|
||||
if (mPlayState == RUNNING) {
|
||||
fraction = mDuration > 0 ? (float)(frameTime - mStartTime) / mDuration : 1.0f;
|
||||
fraction = mDuration > 0 ? (float)(info.frameTimeMs - mStartTime) / mDuration : 1.0f;
|
||||
if (fraction >= 1.0f) {
|
||||
fraction = 1.0f;
|
||||
mPlayState = FINISHED;
|
||||
@@ -192,48 +75,140 @@ bool BaseAnimator::animateFrame(nsecs_t frameTime) {
|
||||
|
||||
if (mPlayState == FINISHED) {
|
||||
onAnimationFinished();
|
||||
callOnFinishedListener(info);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BaseAnimator::callOnFinishedListener(TreeInfo& info) {
|
||||
if (mListener.get()) {
|
||||
if (!info.animationHook) {
|
||||
mListener->onAnimationFinished(this);
|
||||
} else {
|
||||
info.animationHook->callOnFinished(this, mListener.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* BaseRenderNodeAnimator
|
||||
************************************************************/
|
||||
|
||||
BaseRenderNodeAnimator::BaseRenderNodeAnimator(
|
||||
BaseRenderNodeAnimator::DeltaValueType deltaType, float delta)
|
||||
: mTarget(0)
|
||||
, mDeltaValueType(deltaType)
|
||||
, mDeltaValue(delta)
|
||||
, mFromValue(-1) {
|
||||
}
|
||||
|
||||
bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) {
|
||||
mTarget = target;
|
||||
bool finished = animateFrame(info);
|
||||
mTarget = NULL;
|
||||
return finished;
|
||||
}
|
||||
|
||||
void BaseRenderNodeAnimator::onAnimationStarted() {
|
||||
mFromValue = getValue();
|
||||
|
||||
if (mDeltaValueType == BaseRenderNodeAnimator::ABSOLUTE) {
|
||||
mDeltaValue = (mDeltaValue - mFromValue);
|
||||
mDeltaValueType = BaseRenderNodeAnimator::DELTA;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseRenderNodeAnimator::onAnimationUpdated(float fraction) {
|
||||
float value = mFromValue + (mDeltaValue * fraction);
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* RenderPropertyAnimator
|
||||
************************************************************/
|
||||
|
||||
RenderPropertyAnimatorImpl::RenderPropertyAnimatorImpl(
|
||||
GetFloatProperty getter, SetFloatProperty setter,
|
||||
RenderPropertyAnimator::DeltaValueType deltaType, float delta)
|
||||
: mTarget(0)
|
||||
, mGetter(getter)
|
||||
, mSetter(setter)
|
||||
, mDeltaValueType(deltaType)
|
||||
, mDeltaValue(delta)
|
||||
, mFromValue(-1) {
|
||||
// Maps RenderProperty enum to accessors
|
||||
const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = {
|
||||
{&RenderProperties::getTranslationX, &RenderProperties::setTranslationX },
|
||||
{&RenderProperties::getTranslationY, &RenderProperties::setTranslationY },
|
||||
{&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
|
||||
{&RenderProperties::getScaleX, &RenderProperties::setScaleX },
|
||||
{&RenderProperties::getScaleY, &RenderProperties::setScaleY },
|
||||
{&RenderProperties::getRotation, &RenderProperties::setRotation },
|
||||
{&RenderProperties::getRotationX, &RenderProperties::setRotationX },
|
||||
{&RenderProperties::getRotationY, &RenderProperties::setRotationY },
|
||||
{&RenderProperties::getX, &RenderProperties::setX },
|
||||
{&RenderProperties::getY, &RenderProperties::setY },
|
||||
{&RenderProperties::getZ, &RenderProperties::setZ },
|
||||
{&RenderProperties::getAlpha, &RenderProperties::setAlpha },
|
||||
};
|
||||
|
||||
RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property,
|
||||
DeltaValueType deltaType, float deltaValue)
|
||||
: BaseRenderNodeAnimator(deltaType, deltaValue)
|
||||
, mPropertyAccess(PROPERTY_ACCESSOR_LUT[property]) {
|
||||
}
|
||||
|
||||
RenderPropertyAnimatorImpl::~RenderPropertyAnimatorImpl() {
|
||||
float RenderPropertyAnimator::getValue() const {
|
||||
return (target()->animatorProperties().*mPropertyAccess.getter)();
|
||||
}
|
||||
|
||||
bool RenderPropertyAnimatorImpl::animate(RenderProperties* target, TreeInfo& info) {
|
||||
mTarget = target;
|
||||
bool finished = animateFrame(info.frameTimeMs);
|
||||
mTarget = NULL;
|
||||
return finished;
|
||||
void RenderPropertyAnimator::setValue(float value) {
|
||||
(target()->animatorProperties().*mPropertyAccess.setter)(value);
|
||||
}
|
||||
|
||||
void RenderPropertyAnimatorImpl::onAnimationStarted() {
|
||||
mFromValue = (mTarget->*mGetter)();
|
||||
/************************************************************
|
||||
* CanvasPropertyPrimitiveAnimator
|
||||
************************************************************/
|
||||
|
||||
if (mDeltaValueType == RenderPropertyAnimator::ABSOLUTE) {
|
||||
mDeltaValue = (mDeltaValue - mFromValue);
|
||||
mDeltaValueType = RenderPropertyAnimator::DELTA;
|
||||
CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator(
|
||||
CanvasPropertyPrimitive* property, DeltaValueType deltaType, float deltaValue)
|
||||
: BaseRenderNodeAnimator(deltaType, deltaValue)
|
||||
, mProperty(property) {
|
||||
}
|
||||
|
||||
float CanvasPropertyPrimitiveAnimator::getValue() const {
|
||||
return mProperty->value;
|
||||
}
|
||||
|
||||
void CanvasPropertyPrimitiveAnimator::setValue(float value) {
|
||||
mProperty->value = value;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* CanvasPropertySkPaintAnimator
|
||||
************************************************************/
|
||||
|
||||
CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator(
|
||||
CanvasPropertyPaint* property, PaintField field,
|
||||
DeltaValueType deltaType, float deltaValue)
|
||||
: BaseRenderNodeAnimator(deltaType, deltaValue)
|
||||
, mProperty(property)
|
||||
, mField(field) {
|
||||
}
|
||||
|
||||
float CanvasPropertyPaintAnimator::getValue() const {
|
||||
switch (mField) {
|
||||
case STROKE_WIDTH:
|
||||
return mProperty->value.getStrokeWidth();
|
||||
case ALPHA:
|
||||
return mProperty->value.getAlpha();
|
||||
}
|
||||
LOG_ALWAYS_FATAL("Unknown field %d", (int) mField);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void RenderPropertyAnimatorImpl::onAnimationUpdated(float fraction) {
|
||||
float value = mFromValue + (mDeltaValue * fraction);
|
||||
(mTarget->*mSetter)(value);
|
||||
void CanvasPropertyPaintAnimator::setValue(float value) {
|
||||
switch (mField) {
|
||||
case STROKE_WIDTH:
|
||||
mProperty->value.setStrokeWidth(value);
|
||||
return;
|
||||
case ALPHA:
|
||||
mProperty->value.setAlpha(value);
|
||||
return;
|
||||
}
|
||||
LOG_ALWAYS_FATAL("Unknown field %d", (int) mField);
|
||||
}
|
||||
|
||||
} /* namespace uirenderer */
|
||||
|
||||
@@ -17,18 +17,72 @@
|
||||
#define ANIMATOR_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
#include "CanvasProperty.h"
|
||||
#include "Interpolator.h"
|
||||
#include "TreeInfo.h"
|
||||
#include "utils/Macros.h"
|
||||
#include "utils/VirtualLightRefBase.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
class RenderNode;
|
||||
class RenderProperties;
|
||||
class RenderPropertyAnimatorImpl;
|
||||
|
||||
class RenderPropertyAnimator : public VirtualLightRefBase {
|
||||
class AnimationListener : public VirtualLightRefBase {
|
||||
public:
|
||||
ANDROID_API virtual void onAnimationFinished(BaseAnimator*) = 0;
|
||||
protected:
|
||||
ANDROID_API virtual ~AnimationListener() {}
|
||||
};
|
||||
|
||||
// Helper class to contain generic animator helpers
|
||||
class BaseAnimator : public VirtualLightRefBase {
|
||||
PREVENT_COPY_AND_ASSIGN(BaseAnimator);
|
||||
public:
|
||||
|
||||
ANDROID_API void setInterpolator(Interpolator* interpolator);
|
||||
ANDROID_API void setDuration(nsecs_t durationInMs);
|
||||
ANDROID_API void setListener(AnimationListener* listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
bool isFinished() { return mPlayState == FINISHED; }
|
||||
|
||||
protected:
|
||||
BaseAnimator();
|
||||
virtual ~BaseAnimator();
|
||||
|
||||
// This is the main animation entrypoint that subclasses should call
|
||||
// to generate the onAnimation* lifecycle events
|
||||
// Returns true if the animation has finished, false otherwise
|
||||
bool animateFrame(TreeInfo& info);
|
||||
|
||||
// Called when PlayState switches from PENDING to RUNNING
|
||||
virtual void onAnimationStarted() {}
|
||||
virtual void onAnimationUpdated(float fraction) = 0;
|
||||
virtual void onAnimationFinished() {}
|
||||
|
||||
private:
|
||||
void callOnFinishedListener(TreeInfo& info);
|
||||
|
||||
enum PlayState {
|
||||
PENDING,
|
||||
RUNNING,
|
||||
FINISHED,
|
||||
};
|
||||
|
||||
Interpolator* mInterpolator;
|
||||
PlayState mPlayState;
|
||||
long mStartTime;
|
||||
long mDuration;
|
||||
|
||||
sp<AnimationListener> mListener;
|
||||
};
|
||||
|
||||
class BaseRenderNodeAnimator : public BaseAnimator {
|
||||
public:
|
||||
// Since the UI thread doesn't necessarily know what the current values
|
||||
// actually are and thus can't do the calculations, this is used to inform
|
||||
@@ -43,6 +97,29 @@ public:
|
||||
DELTA,
|
||||
};
|
||||
|
||||
bool animate(RenderNode* target, TreeInfo& info);
|
||||
|
||||
protected:
|
||||
BaseRenderNodeAnimator(DeltaValueType deltaType, float deltaValue);
|
||||
|
||||
RenderNode* target() const { return mTarget; }
|
||||
virtual float getValue() const = 0;
|
||||
virtual void setValue(float value) = 0;
|
||||
|
||||
private:
|
||||
virtual void onAnimationStarted();
|
||||
virtual void onAnimationUpdated(float fraction);
|
||||
|
||||
// mTarget is only valid inside animate()
|
||||
RenderNode* mTarget;
|
||||
|
||||
BaseRenderNodeAnimator::DeltaValueType mDeltaValueType;
|
||||
float mDeltaValue;
|
||||
float mFromValue;
|
||||
};
|
||||
|
||||
class RenderPropertyAnimator : public BaseRenderNodeAnimator {
|
||||
public:
|
||||
enum RenderProperty {
|
||||
TRANSLATION_X = 0,
|
||||
TRANSLATION_Y,
|
||||
@@ -58,19 +135,53 @@ public:
|
||||
ALPHA,
|
||||
};
|
||||
|
||||
ANDROID_API void setInterpolator(Interpolator* interpolator);
|
||||
ANDROID_API void setDuration(nsecs_t durationInMs);
|
||||
ANDROID_API bool isFinished();
|
||||
|
||||
bool animate(RenderProperties* target, TreeInfo& info);
|
||||
ANDROID_API RenderPropertyAnimator(RenderProperty property,
|
||||
DeltaValueType deltaType, float deltaValue);
|
||||
|
||||
protected:
|
||||
ANDROID_API RenderPropertyAnimator(RenderProperty property, DeltaValueType deltaType,
|
||||
float deltaValue);
|
||||
ANDROID_API virtual ~RenderPropertyAnimator();
|
||||
ANDROID_API virtual float getValue() const;
|
||||
ANDROID_API virtual void setValue(float value);
|
||||
|
||||
private:
|
||||
RenderPropertyAnimatorImpl* mImpl;
|
||||
typedef void (RenderProperties::*SetFloatProperty)(float value);
|
||||
typedef float (RenderProperties::*GetFloatProperty)() const;
|
||||
|
||||
struct PropertyAccessors {
|
||||
GetFloatProperty getter;
|
||||
SetFloatProperty setter;
|
||||
};
|
||||
|
||||
PropertyAccessors mPropertyAccess;
|
||||
|
||||
static const PropertyAccessors PROPERTY_ACCESSOR_LUT[];
|
||||
};
|
||||
|
||||
class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator {
|
||||
public:
|
||||
ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property,
|
||||
DeltaValueType deltaType, float deltaValue);
|
||||
protected:
|
||||
ANDROID_API virtual float getValue() const;
|
||||
ANDROID_API virtual void setValue(float value);
|
||||
private:
|
||||
sp<CanvasPropertyPrimitive> mProperty;
|
||||
};
|
||||
|
||||
class CanvasPropertyPaintAnimator : public BaseRenderNodeAnimator {
|
||||
public:
|
||||
enum PaintField {
|
||||
STROKE_WIDTH = 0,
|
||||
ALPHA,
|
||||
};
|
||||
|
||||
ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property,
|
||||
PaintField field, DeltaValueType deltaType, float deltaValue);
|
||||
protected:
|
||||
ANDROID_API virtual float getValue() const;
|
||||
ANDROID_API virtual void setValue(float value);
|
||||
private:
|
||||
sp<CanvasPropertyPaint> mProperty;
|
||||
PaintField mField;
|
||||
};
|
||||
|
||||
} /* namespace uirenderer */
|
||||
|
||||
46
libs/hwui/CanvasProperty.h
Normal file
46
libs/hwui/CanvasProperty.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
#ifndef CANVASPROPERTY_H
|
||||
#define CANVASPROPERTY_H
|
||||
|
||||
#include "utils/Macros.h"
|
||||
#include "utils/VirtualLightRefBase.h"
|
||||
|
||||
#include <SkPaint.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
class CanvasPropertyPrimitive : public VirtualLightRefBase {
|
||||
PREVENT_COPY_AND_ASSIGN(CanvasPropertyPrimitive);
|
||||
public:
|
||||
CanvasPropertyPrimitive(float initialValue) : value(initialValue) {}
|
||||
|
||||
float value;
|
||||
};
|
||||
|
||||
class CanvasPropertyPaint : public VirtualLightRefBase {
|
||||
PREVENT_COPY_AND_ASSIGN(CanvasPropertyPaint);
|
||||
public:
|
||||
CanvasPropertyPaint(const SkPaint& initialValue) : value(initialValue) {}
|
||||
|
||||
SkPaint value;
|
||||
};
|
||||
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
#endif /* CANVASPROPERTY_H */
|
||||
@@ -140,6 +140,14 @@ public:
|
||||
void addChild(DrawDisplayListOp* childOp);
|
||||
const Vector<DrawDisplayListOp*>& children() { return mChildren; }
|
||||
|
||||
void refProperty(CanvasPropertyPrimitive* prop) {
|
||||
mReferenceHolders.push(prop);
|
||||
}
|
||||
|
||||
void refProperty(CanvasPropertyPaint* prop) {
|
||||
mReferenceHolders.push(prop);
|
||||
}
|
||||
|
||||
private:
|
||||
Vector< sp<VirtualLightRefBase> > mReferenceHolders;
|
||||
|
||||
|
||||
@@ -1198,6 +1198,27 @@ private:
|
||||
float mRadius;
|
||||
};
|
||||
|
||||
class DrawCirclePropsOp : public DrawOp {
|
||||
public:
|
||||
DrawCirclePropsOp(float* x, float* y, float* radius, const SkPaint* paint)
|
||||
: DrawOp(paint), mX(x), mY(y), mRadius(radius) {}
|
||||
|
||||
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
|
||||
return renderer.drawCircle(*mX, *mY, *mRadius, getPaint(renderer));
|
||||
}
|
||||
|
||||
virtual void output(int level, uint32_t logFlags) const {
|
||||
OP_LOG("Draw Circle Props x %p, y %p, r %p", mX, mY, mRadius);
|
||||
}
|
||||
|
||||
virtual const char* name() { return "DrawCircleProps"; }
|
||||
|
||||
private:
|
||||
float* mX;
|
||||
float* mY;
|
||||
float* mRadius;
|
||||
};
|
||||
|
||||
class DrawOvalOp : public DrawStrokableOp {
|
||||
public:
|
||||
DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint)
|
||||
|
||||
@@ -299,6 +299,17 @@ status_t DisplayListRenderer::drawCircle(float x, float y, float radius, const S
|
||||
return DrawGlInfo::kStatusDone;
|
||||
}
|
||||
|
||||
status_t DisplayListRenderer::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
|
||||
CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
|
||||
mDisplayListData->refProperty(x);
|
||||
mDisplayListData->refProperty(y);
|
||||
mDisplayListData->refProperty(radius);
|
||||
mDisplayListData->refProperty(paint);
|
||||
addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
|
||||
&radius->value, &paint->value));
|
||||
return DrawGlInfo::kStatusDone;
|
||||
}
|
||||
|
||||
status_t DisplayListRenderer::drawOval(float left, float top, float right, float bottom,
|
||||
const SkPaint* paint) {
|
||||
paint = refPaint(paint);
|
||||
|
||||
@@ -135,6 +135,8 @@ public:
|
||||
virtual status_t drawRoundRect(float left, float top, float right, float bottom,
|
||||
float rx, float ry, const SkPaint* paint);
|
||||
virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint);
|
||||
virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
|
||||
CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint);
|
||||
virtual status_t drawOval(float left, float top, float right, float bottom,
|
||||
const SkPaint* paint);
|
||||
virtual status_t drawArc(float left, float top, float right, float bottom,
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "UvMapper.h"
|
||||
#include "Vertex.h"
|
||||
#include "Caches.h"
|
||||
#include "CanvasProperty.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -200,6 +201,12 @@ public:
|
||||
virtual status_t drawRoundRect(float left, float top, float right, float bottom,
|
||||
float rx, float ry, const SkPaint* paint);
|
||||
virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint);
|
||||
virtual status_t drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
|
||||
CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
|
||||
// TODO: Remove once android_view_GLES20Canvas uses DisplayListRenderer
|
||||
// directly
|
||||
return drawCircle(x->value, y->value, radius->value, &paint->value);
|
||||
}
|
||||
virtual status_t drawOval(float left, float top, float right, float bottom,
|
||||
const SkPaint* paint);
|
||||
virtual status_t drawArc(float left, float top, float right, float bottom,
|
||||
|
||||
@@ -109,7 +109,7 @@ void RenderNode::prepareTreeImpl(TreeInfo& info) {
|
||||
prepareSubTree(info, mDisplayListData);
|
||||
}
|
||||
|
||||
static bool is_finished(const sp<RenderPropertyAnimator>& animator) {
|
||||
static bool is_finished(const sp<BaseRenderNodeAnimator>& animator) {
|
||||
return animator->isFinished();
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ void RenderNode::pushStagingChanges(TreeInfo& info) {
|
||||
}
|
||||
if (mNeedsAnimatorsSync) {
|
||||
mAnimators.resize(mStagingAnimators.size());
|
||||
std::vector< sp<RenderPropertyAnimator> >::iterator it;
|
||||
std::vector< sp<BaseRenderNodeAnimator> >::iterator it;
|
||||
// hint: this means copy_if_not()
|
||||
it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(),
|
||||
mAnimators.begin(), is_finished);
|
||||
@@ -141,26 +141,22 @@ void RenderNode::pushStagingChanges(TreeInfo& info) {
|
||||
|
||||
class AnimateFunctor {
|
||||
public:
|
||||
AnimateFunctor(RenderProperties* target, TreeInfo& info)
|
||||
AnimateFunctor(RenderNode* target, TreeInfo& info)
|
||||
: mTarget(target), mInfo(info) {}
|
||||
|
||||
bool operator() (sp<RenderPropertyAnimator>& animator) {
|
||||
bool finished = animator->animate(mTarget, mInfo);
|
||||
if (finished && mInfo.animationListener) {
|
||||
mInfo.animationListener->onAnimationFinished(animator);
|
||||
}
|
||||
return finished;
|
||||
bool operator() (sp<BaseRenderNodeAnimator>& animator) {
|
||||
return animator->animate(mTarget, mInfo);
|
||||
}
|
||||
private:
|
||||
RenderProperties* mTarget;
|
||||
RenderNode* mTarget;
|
||||
TreeInfo& mInfo;
|
||||
};
|
||||
|
||||
void RenderNode::evaluateAnimations(TreeInfo& info) {
|
||||
if (!mAnimators.size()) return;
|
||||
|
||||
AnimateFunctor functor(&mProperties, info);
|
||||
std::vector< sp<RenderPropertyAnimator> >::iterator newEnd;
|
||||
AnimateFunctor functor(this, info);
|
||||
std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd;
|
||||
newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
|
||||
mAnimators.erase(newEnd, mAnimators.end());
|
||||
mProperties.updateMatrix();
|
||||
|
||||
@@ -128,6 +128,10 @@ public:
|
||||
return mProperties;
|
||||
}
|
||||
|
||||
RenderProperties& animatorProperties() {
|
||||
return mProperties;
|
||||
}
|
||||
|
||||
const RenderProperties& stagingProperties() {
|
||||
return mStagingProperties;
|
||||
}
|
||||
@@ -148,13 +152,13 @@ public:
|
||||
ANDROID_API virtual void prepareTree(TreeInfo& info);
|
||||
|
||||
// UI thread only!
|
||||
ANDROID_API void addAnimator(const sp<RenderPropertyAnimator>& animator) {
|
||||
ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
|
||||
mStagingAnimators.insert(animator);
|
||||
mNeedsAnimatorsSync = true;
|
||||
}
|
||||
|
||||
// UI thread only!
|
||||
ANDROID_API void removeAnimator(const sp<RenderPropertyAnimator>& animator) {
|
||||
ANDROID_API void removeAnimator(const sp<BaseRenderNodeAnimator>& animator) {
|
||||
mStagingAnimators.erase(animator);
|
||||
mNeedsAnimatorsSync = true;
|
||||
}
|
||||
@@ -233,8 +237,8 @@ private:
|
||||
DisplayListData* mStagingDisplayListData;
|
||||
|
||||
bool mNeedsAnimatorsSync;
|
||||
std::set< sp<RenderPropertyAnimator> > mStagingAnimators;
|
||||
std::vector< sp<RenderPropertyAnimator> > mAnimators;
|
||||
std::set< sp<BaseRenderNodeAnimator> > mStagingAnimators;
|
||||
std::vector< sp<BaseRenderNodeAnimator> > mAnimators;
|
||||
|
||||
/**
|
||||
* Draw time state - these properties are only set and used during rendering
|
||||
|
||||
@@ -16,20 +16,19 @@
|
||||
#ifndef TREEINFO_H
|
||||
#define TREEINFO_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
class RenderPropertyAnimator;
|
||||
class BaseAnimator;
|
||||
class AnimationListener;
|
||||
|
||||
class AnimationListener {
|
||||
class AnimationHook {
|
||||
public:
|
||||
ANDROID_API virtual void onAnimationFinished(const sp<RenderPropertyAnimator>&) = 0;
|
||||
virtual void callOnFinished(BaseAnimator* animator, AnimationListener* listener) = 0;
|
||||
protected:
|
||||
ANDROID_API virtual ~AnimationListener() {}
|
||||
~AnimationHook() {}
|
||||
};
|
||||
|
||||
struct TreeInfo {
|
||||
@@ -41,7 +40,7 @@ struct TreeInfo {
|
||||
, frameTimeMs(0)
|
||||
, evaluateAnimations(false)
|
||||
, hasAnimations(false)
|
||||
, animationListener(0)
|
||||
, animationHook(0)
|
||||
{}
|
||||
|
||||
bool hasFunctors;
|
||||
@@ -53,7 +52,7 @@ struct TreeInfo {
|
||||
bool evaluateAnimations;
|
||||
// This is only updated if evaluateAnimations is true
|
||||
bool hasAnimations;
|
||||
AnimationListener* animationListener;
|
||||
AnimationHook* animationHook;
|
||||
|
||||
// TODO: Damage calculations
|
||||
};
|
||||
|
||||
@@ -112,6 +112,10 @@ bool DrawFrameTask::syncFrameState() {
|
||||
initTreeInfo(info);
|
||||
mContext->processLayerUpdates(&mLayers, info);
|
||||
mContext->prepareTree(info);
|
||||
if (info.hasAnimations) {
|
||||
// TODO: dirty calculations, for now just do a full-screen inval
|
||||
mDirty.setEmpty();
|
||||
}
|
||||
// If prepareTextures is false, we ran out of texture cache space
|
||||
return !info.hasFunctors && info.prepareTextures;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
* Copyright (C) 2014 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.
|
||||
@@ -13,28 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef MACROS_H
|
||||
#define MACROS_H
|
||||
|
||||
#include "jni.h"
|
||||
#define PREVENT_COPY_AND_ASSIGN(Type) \
|
||||
private: \
|
||||
Type(const Type&); \
|
||||
void operator=(const Type&)
|
||||
|
||||
#ifdef USE_OPENGL_RENDERER
|
||||
|
||||
#include <Animator.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class RenderNodeAnimator : public uirenderer::RenderPropertyAnimator {
|
||||
public:
|
||||
RenderNodeAnimator(JNIEnv* env, jobject callbackObject,
|
||||
RenderProperty property, DeltaValueType deltaType, float delta);
|
||||
virtual ~RenderNodeAnimator();
|
||||
|
||||
void callOnFinished();
|
||||
|
||||
private:
|
||||
JavaVM* mJvm;
|
||||
jobject mWeakThis;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* MACROS_H */
|
||||
@@ -287,6 +287,15 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="CirclePropActivity"
|
||||
android:label="Draw/Circle Props">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="com.android.test.hwui.TEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="ClearActivity"
|
||||
android:label="Window/Clear">
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.android.test.hwui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.CanvasProperty;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.os.Bundle;
|
||||
import android.os.Trace;
|
||||
import android.view.HardwareCanvas;
|
||||
import android.view.RenderNodeAnimator;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CirclePropActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final LinearLayout layout = new LinearLayout(this);
|
||||
layout.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge);
|
||||
layout.addView(spinner, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
|
||||
|
||||
layout.addView(new CircleView(this),
|
||||
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
|
||||
setContentView(layout);
|
||||
}
|
||||
|
||||
static class CircleView extends View {
|
||||
static final int DURATION = 500;
|
||||
|
||||
private boolean mToggle = false;
|
||||
ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<RenderNodeAnimator>();
|
||||
|
||||
CanvasProperty<Float> mX;
|
||||
CanvasProperty<Float> mY;
|
||||
CanvasProperty<Float> mRadius;
|
||||
CanvasProperty<Paint> mPaint;
|
||||
|
||||
CircleView(Context c) {
|
||||
super(c);
|
||||
setClickable(true);
|
||||
|
||||
mX = CanvasProperty.createFloat(200.0f);
|
||||
mY = CanvasProperty.createFloat(200.0f);
|
||||
mRadius = CanvasProperty.createFloat(150.0f);
|
||||
|
||||
Paint p = new Paint();
|
||||
p.setAntiAlias(true);
|
||||
p.setColor(0xFFFF0000);
|
||||
p.setStyle(Style.STROKE);
|
||||
p.setStrokeWidth(60.0f);
|
||||
mPaint = CanvasProperty.createPaint(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
if (canvas.isHardwareAccelerated()) {
|
||||
HardwareCanvas hwcanvas = (HardwareCanvas) canvas;
|
||||
hwcanvas.drawCircle(mX, mY, mRadius, mPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performClick() {
|
||||
for (int i = 0; i < mRunningAnimations.size(); i++) {
|
||||
mRunningAnimations.get(i).cancel();
|
||||
}
|
||||
mRunningAnimations.clear();
|
||||
|
||||
mToggle = !mToggle;
|
||||
|
||||
mRunningAnimations.add(new RenderNodeAnimator(
|
||||
mX, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 400.0f : 200.0f));
|
||||
|
||||
mRunningAnimations.add(new RenderNodeAnimator(
|
||||
mY, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 600.0f : 200.0f));
|
||||
|
||||
mRunningAnimations.add(new RenderNodeAnimator(
|
||||
mRadius, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 250.0f : 150.0f));
|
||||
|
||||
mRunningAnimations.add(new RenderNodeAnimator(
|
||||
mPaint, RenderNodeAnimator.PAINT_ALPHA,
|
||||
RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 64.0f : 255.0f));
|
||||
|
||||
mRunningAnimations.add(new RenderNodeAnimator(
|
||||
mPaint, RenderNodeAnimator.PAINT_STROKE_WIDTH,
|
||||
RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 5.0f : 60.0f));
|
||||
|
||||
for (int i = 0; i < mRunningAnimations.size(); i++) {
|
||||
mRunningAnimations.get(i).start(this);
|
||||
}
|
||||
|
||||
if (mToggle) {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "pretendBusy");
|
||||
try {
|
||||
Thread.sleep(DURATION);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class SmallCircleActivity extends Activity {
|
||||
|
||||
mPaint = new Paint();
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setColor(0xffffffff);
|
||||
mPaint.setColor(0xff00ffff);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,8 +19,6 @@ import java.util.Map;
|
||||
|
||||
public class MainActivity extends Activity implements OnItemClickListener {
|
||||
|
||||
static final int TRANSLATION_Y = 1;
|
||||
static final int DELTA_TYPE_DELTA = 1;
|
||||
static final int DURATION = 400;
|
||||
|
||||
static final String KEY_NAME = "name";
|
||||
@@ -75,7 +73,7 @@ public class MainActivity extends Activity implements OnItemClickListener {
|
||||
float delta = (pos - clickedPosition) * 1.1f;
|
||||
if (delta == 0) delta = -1;
|
||||
RenderNodeAnimator animator = new RenderNodeAnimator(
|
||||
TRANSLATION_Y, DELTA_TYPE_DELTA, dy * delta);
|
||||
RenderNodeAnimator.TRANSLATION_Y, RenderNodeAnimator.DELTA_TYPE_DELTA, dy * delta);
|
||||
animator.setDuration(DURATION);
|
||||
animator.start(child);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user