Professional Documents
Culture Documents
Tomislav Homan Droidcon Presentation PDF
Tomislav Homan Droidcon Presentation PDF
CANVAS
Tomislav Homan, Five
calculateAxis(width, height);
calculateDataPoints(width, height);
}
Couple of advices 3 / 3 - Use onDraw only to draw
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
A bit of philosophy
Every view is a set of states
State can be represented as a point in a state space
Animation is a change of state through time
Animating custom views
Lets start with simple example - just a dot
State contains only two pieces of information, X and Y position
We change X and Y position through time
Animating custom views
The recipe
Determine important constants
Initialize paints and other expensive objects
(Re)calculate size dependent stuff on size changed
Implement main loop
Calculate state
Draw
Determine important constants
@Override
protected void onSizeChanged(final int width, final int height, final int oldWidth, final int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
.
}
Implement main loop
@Override
public void run() {
if (hasFrameToDraw()) {
invalidate();
uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL);
} else {
isAnimating = false;
}
}
};
Animating custom views
Calculate state
Create frames array
Determine step by which state changes
Increase positions by step
Calculate state
currentFrame = 0;
}
Animating custom views
Draw
Now piece of cake
Draw static stuff
Take and draw current frame
Increase the counter
Draw
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
currentFrame++;
}
Now we want to animate the graph from one state to another
Animating custom views
Now we to animate the graph from 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;
EASING IN AND OUT
Easing in and out
Easing in - accelerating from the origin
Easing out - decelerating to the destination
Accelerate, hit the inflection point, decelerate to the destination
Again - dot as an example
Easing in and out
Easing out (deceleration)
Differences while calculating frames
Replace linear trajectory with quadratic
The step that we used in first animation isnt 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;
}
A bit of high school math.
.gives us the following formula:
Xi = (- L / 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
The rest of the recipe is same:
private float calculateFunction(final float a, final float b, final int i, final float origin) {
return a * i * i + b * i + origin;
}
Easing in (acceleration)
Same approach
Different starting conditions - initial velocity zero
Renders a bit different formula
Acceleration and deceleration in the same time
Things more complicated (but not a lot)
Use cubic formula instead of quadratic
Again some high school math - sorry :(
The magic formula:
@Override
public void run() {
if (hasFrameToDraw()) {
invalidate();
uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL);
} else {
isAnimating = false;
}
}
};
Implement the main loop
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
currentFrame++;
}
Draw
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
if (isAnimating) {
updateWorld();
}
}
Calculate state
if (hitRightWall()) {
currentVelocity.x = -currentVelocity.x;
currentPosition.set(topRight.x - WALL_THICKNESS, currentPosition.y);
}
//Same for every wall
}
@Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
}
Triggered 20 times in a second
@Override
public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
calculateData(fft);
}
}, SOUND_CAPTURE_RATE * 1000, false, true);
visualizer.setEnabled(true);
}
Transforming data
currentFrame = 0;
}
Animating external input
Important!!! - interpolation
Data arrives 20 times a second
We want to draw 60 times a second
We have to make up - interpolate 3 frames
Interpolation
private PointF[][] calculateContours(final PointF[][] currentData, final int[] averagedData, final int offset, final boolean goOutwards) {
.
fillWithLinearyInterpolatedFrames(newFrames);
.
return newFrames;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentFrame++;
if (currentFrame >= NUMBER_OF_INTERPOLATED_FRAMES) {
currentFrame = NUMBER_OF_INTERPOLATED_FRAMES - 1;
}
}
All together
Visualizer onDraw