Have you ever made a custom view using only canvas? Now have you ever tried to animate it? If you found that problematic this presentation will help you grasp basic stuff about animating custom drawn views.
public final class StaticGraph extends View { public StaticGraph(final Context context) { super(context); init(); } public StaticGraph(final Context context, final AttributeSet attrs) { super(context, attrs); init(); } public StaticGraph(final Context context, final AttributeSet attrs, final int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } …..
measures necessary to draw early private PointF xAxisStart; private PointF xAxisEnd; private PointF yAxisStart; private PointF yAxisEnd; …….. @Override protected void onSizeChanged(final int width, final int height, final int oldWidth, final int oldHeight) { super.onSizeChanged(width, height, oldWidth, oldHeight); calculateAxis(width, height); calculateDataPoints(width, height); }
onSizeChanged(final int width, final int height, final int oldWidth, final int oldHeight) { super.onSizeChanged(width, height, oldWidth, oldHeight); startPoint = new PointF(width / 4.0f, height * 3.0f / 4.0f); endPoint = new PointF(width * 3.0f / 4.0f, height / 4.0f); …. }
+ 1]; …. float x = animationStartPoint.x; float y = animationStartPoint.y; for (int i = 0; i < NUMBER_OF_FRAMES; i++) { frames[i] = new PointF(x, y); x += xStep; y += yStep; } frames[frames.length - 1] = new PointF(animationEndPoint.x, animationEndPoint.y); currentFrame = 0; }
one state to another Recipe is the same, state more complicated Dot state: private PointF[] frames; Graph state: private PointF[][] framesDataPoints; private float[] framesAxisZoom; private int[] framesColor;
the origin • Easing out - decelerating to the destination • Accelerate, hit the inflection point, decelerate to the destination • Again - dot as an example
calculating frames • Replace linear trajectory with quadratic • The step that we used in first animation isn’t valid anymore float x = animationStartPoint.x; float y = animationStartPoint.y; for (int i = 0; i < NUMBER_OF_FRAMES; i++) { frames[i] = new PointF(x, y); x += xStep; y += yStep; }
N^2) * i^2 + (2 * L / N) * i • Xi - position (state) for the ith frame • L - length of the dot trajectory • N - number of frames • i - order of the frame
modified to use previous formula final float aX = -pathLengthX / (NUMBER_OF_FRAMES * NUMBER_OF_FRAMES); final float bX = 2 * pathLengthX / NUMBER_OF_FRAMES; final float aY = -pathLengthY / (NUMBER_OF_FRAMES * NUMBER_OF_FRAMES); final float bY = 2 * pathLengthY / NUMBER_OF_FRAMES; for (int i = 0; i < NUMBER_OF_FRAMES; i++) { final float x = calculateFunction(aX, bX, i, animationStartPoint.x); final float y = calculateFunction(aY, bY, i, animationStartPoint.y); frames[i] = new PointF(x, y); } private float calculateFunction(final float a, final float b, final int i, final float origin) { return a * i * i + b * i + origin; }
N^3) * i^3 + (3 * L) / N^2 * i^2 • Xi - position (state) for the ith frame • L - length of the dot trajectory • N - number of frames • i - order of the frame • Same as quadratic interpolation, slightly different constants and powers
the processing done at the beginning of the animation • Everything is deterministic and known in advance • Easy to determine when to stop the animation • Con: takes more space - 94 positions in our example
state from the previous one every loop iteration • Something like a mini game engine / physics simulator • Wastes far less space • Behaviour more realistic • Con: if calculation is heavy frames could drop • Respect sacred window of 16 (or less) milliseconds
bounces off the walls • Never-ending animation - duration isn’t determined • Consequently we don’t know number of frames up- front • Perfect for using dynamic frame calculation • Twist in our recipe
the same • Initialize paints and other expensive objects - the same • (Re)calculate size dependent stuff on size changed - the same • Implement main loop - move frame calculation to drawing phase • Calculate state - different • Draw - almost the same
useful per se • Use springs to snap the objects from one position to another • Use gravity to collapse the scene • You can simulate other scene properties instead of position such as color, scale, etc...
doesn’t depend only on internal factors, but also on external • For example equalizer • Input is sound in fft (fast Fourier transform) data form • Run data through the “pipeline” of transformations to get something that you can draw • The recipe is similar to the precalculation style, but animation isn’t triggered by button push, but with new sound data arrival
State can be anything from color to position • Target 60 (or higher) fps main loop, beware of frame drops • Pre-calculate whole frameset or calculate frame by frame • Take it slow, make demo app, increment step by step • Use code examples as a starting point and inform me where are memory leaks :)
face to face • Have you measured how much does it suck life out of battery? - No, but we’ve noticed it does • Why don’t you use OpenGL or something. - It’s next level, this is first approximation • What about object animators - Same amount of code, took me same amount of time, more expensive, less flexible if you know what are you doing. Can’t apply to the equalizer. It is more OO approach though.