SlideShare a Scribd company logo
BREATHING LIFE INTO THE
CANVAS
#droidconzgTomislav Homan
Tomislav Homan, Five
INTRO
Intro
● Why custom views?
● Not everything can be solved with standard views
● We want to draw directly onto the Canvas
● Graphs and diagrams
● External data from sensors and mic (equalizer)
● ….
Couple of advices 1 / 3 - Initialize paints early
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();
}
…..
Couple of advices 1 / 3 - Initialize paints early
private void init() {
axisPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
axisPaint.setColor(Color.BLACK);
axisPaint.setStyle(Paint.Style.STROKE);
axisPaint.setStrokeWidth(4.0f);
…..
}
Couple of advices 2 / 3 - Memorize all the 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);
}
Couple of advices 3 / 3 - Use onDraw only to draw
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(xAxisStart.x, xAxisStart.y, xAxisEnd.x, xAxisEnd.y, axisPaint);
canvas.drawLine(yAxisStart.x, yAxisStart.y, yAxisEnd.x, yAxisEnd.y, axisPaint);
….
canvas.drawPath(graphPath, graphPaint);
}
ANIMATING CUSTOM VIEWS
● 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
A bit of philosophy
Animating custom views
Animating custom views
Let’s 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
private static final long UI_REFRESH_RATE = 60L; // fps
private static final long ANIMATION_REFRESHING_INTERVAL = TimeUnit.SECONDS.toMillis(1L) / UI_REFRESH_RATE; //
millis
private static final long ANIMATION_DURATION_IN_MILLIS = 1500L; // millis
private static final long NUMBER_OF_FRAMES = ANIMATION_DURATION_IN_MILLIS /
ANIMATION_REFRESHING_INTERVAL;
Animating custom views
Determine important constants
● For animation that lasts 1500 milliseconds in framerate of 60 fps...
● We should refresh the screen every cca 16 milliseconds
● And we have cca 94 frames
Initialize paints and other expensive objects - business as usual
private void init() {
dotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dotPaint.setColor(Color.RED);
dotPaint.setStyle(Paint.Style.FILL);
dotPaint.setStrokeWidth(1.0f);
endPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
endPointPaint.setColor(Color.GREEN);
endPointPaint.setStyle(Paint.Style.FILL);
endPointPaint.setStrokeWidth(1.0f);
}
(Re)calculate size dependent stuff on size changed
@Override
protected void 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);
….
}
Implement main loop
private final Handler uiHandler = new Handler(Looper.getMainLooper());
private void startAnimating() {
calculateFrames();
uiHandler.post(invalidateUI);
}
private void stopAnimating() {
uiHandler.removeCallbacks(invalidateUI);
}
Implement main loop
private Runnable invalidateUI = new Runnable() {
@Override
public void run() {
if (hasFrameToDraw()) {
invalidate();
uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL);
} else {
isAnimating = false;
}
}
};
Calculate state
● Create frames array
● Determine step by which state changes
● Increase positions by step
Animating custom views
Calculate state
private void calculateFrames() {
frames = new PointF[NUMBER_OF_FRAMES + 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;
}
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);
drawDot(canvas, startPoint, endPointPaint);
drawDot(canvas, endPoint, endPointPaint);
final PointF currentPoint = frames[currentFrame];
drawDot(canvas, currentPoint, dotPaint);
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 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;
}
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:
● Calculation phase 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;
}
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:
Xi = (- 2 * L / 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
Easing in and out
Graph example
● Again: Use same formulas, but on a more complicated state
DYNAMIC FRAME CALCULATION
Dynamic frame calculation
Actually 2 approaches for calculating state
● Pre-calculate all the frames (states) - we did this
● Calculate the next frame from the current one dynamically
Dynamic frame calculation
Pre-calculate all the frames (states)
● All 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
Dynamic frame calculation
Dynamic state calculation
● Calculate the new 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
Dynamic frame calculation
● First example - a dot that 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
Dynamic frame calculation
The recipe
● Determine important constants - 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
Implement the main loop
private final Handler uiHandler = new Handler(Looper.getMainLooper());
private void startAnimating() {
calculateFrames();
uiHandler.post(invalidateUI);
}
private void stopAnimating() {
uiHandler.removeCallbacks(invalidateUI);
}
Implement the main loop
private Runnable invalidateUI = new Runnable() {
@Override
public void run() {
if (hasFrameToDraw()) {
invalidate();
uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL);
} else {
isAnimating = false;
}
}
};
Implement the main loop
private void startAnimating() {
uiHandler.post(invalidateUI);
}
private void stopAnimating() {
uiHandler.removeCallbacks(invalidateUI);
}
private Runnable invalidateUI = new Runnable() {
@Override
public void run() {
invalidate();
uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL);
}
};
Draw
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
drawDot(canvas, startPoint, endPointPaint);
drawDot(canvas, endPoint, endPointPaint);
final PointF currentPoint = frames[currentFrame];
drawDot(canvas, currentPoint, dotPaint);
currentFrame++;
}
Draw
private PointF currentPosition;
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y, wallsPaint);
drawDot(canvas, currentPosition, dotPaint);
if (isAnimating) {
updateWorld();
}
}
Calculate state
private void updateWorld() {
final float dx = currentVelocity.x; // * dt
final float dy = currentVelocity.y; // * dt
currentPosition.set(currentPosition.x + dx, currentPosition.y + dy);
if (hitRightWall()) {
currentVelocity.x = -currentVelocity.x;
currentPosition.set(topRight.x - WALL_THICKNESS, currentPosition.y);
}
//Same for every wall
}
private boolean hitRightWall() {
return currentPosition.x >= topRight.x - WALL_THICKNESS;
}
Dynamic frame calculation
Add gravity to previous example
● Just a couple of lines more
private void updateWorld() {
final float dvx = GRAVITY.x;
final float dvy = GRAVITY.y;
currentVelocity.set(currentVelocity.x + dvx, currentVelocity.y + dvy);
final float dx = currentVelocity.x; // * dt
final float dy = currentVelocity.y; // * dt
currentPosition.set(currentPosition.x + dx, currentPosition.y + dy);
…..
}
Springs
● Define a circle of given radius
● Define couple of control points with random
distance from the circle
● Let control points spring around the circle
private void updateWorld() {
final int angleStep = 360 / NUMBER_OD_CONTROL_POINTS;
for (int i = 0; i < controlPoints.length; i++) {
final PointF point = controlPoints[i];
final PointF velocity = controlPointsVelocities[i];
final PointF springCenter = CoordinateUtils.fromPolar((int) radius, i * angleStep, centerPosition);
final float forceX = -SPRING_CONSTANT * (point.x - springCenter.x);
final float forceY = -SPRING_CONSTANT * (point.y - springCenter.y);
final float dvx = forceX;
final float dvy = forceY;
velocity.set(velocity.x + dvx, velocity.y + dvy);
final float dx = velocity.x;
final float dy = velocity.y;
point.set(point.x + dx, point.y + dy);
}
}
Dynamic frame calculation
Usefulness of those animations
● Not very 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...
ANIMATING EXTERNAL INPUT
Animating external input
● It sometimes happens that your state 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
Main loop - just invalidating in 60 fps
private void startAnimating() {
uiHandler.post(invalidateUI);
}
private void stopAnimating() {
uiHandler.removeCallbacks(invalidateUI);
}
private Runnable invalidateUI = new Runnable() {
@Override
public void run() {
invalidate();
uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL);
}
};
Data input
private static final int SOUND_CAPTURE_RATE = 20; // Hz
private void startCapturingAudioSamples(int audioSessionId) {
visualizer = new Visualizer(audioSessionId);
visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
visualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
@Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
}
@Override
public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
calculateData(fft);
}
}, SOUND_CAPTURE_RATE * 1000, false, true);
visualizer.setEnabled(true);
}
Triggered 20 times in a second
Transforming data
private void calculateData(byte[] bytes) {
final int[] truncatedData = truncateData(bytes);
final int[] magnitudes = calculateMagnitudes(truncatedData);
final int[] outerScaledData = scaleData(magnitudes, OUTER_SCALE_TARGET);
final int[] innerScaledData = scaleData(magnitudes, INNER_SCALE_TARGET);
final int[] outerAveragedData = averageData(outerScaledData);
final int[] innerAveragedData = averageData(innerScaledData);
this.outerPoints = calculateContours(outerPoints, outerAveragedData, OUTER_OFFSET, true);
this.innerPoints = calculateContours(innerPoints, innerAveragedData, INNER_OFFSET, false);
currentFrame = 0;
}
This is now drawable
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;
}
private void fillWithLinearyInterpolatedFrames(final PointF[][] data) {
for (int j = 0; j < NUMBER_OF_SAMPLES; j++) {
final PointF targetPoint = data[NUMBER_OF_INTERPOLATED_FRAMES - 1][j];
final PointF originPoint = data[0][j];
final double deltaX = (targetPoint.x - originPoint.x) / NUMBER_OF_INTERPOLATED_FRAMES;
final double deltaY = (targetPoint.y - originPoint.y) / NUMBER_OF_INTERPOLATED_FRAMES;
for (int i = 1; i < NUMBER_OF_INTERPOLATED_FRAMES - 1; i++) {
data[i][j] = new PointF((float) (originPoint.x + i * deltaX), (float) (originPoint.y + i * deltaY));
}
}
for (int i = 1; i < NUMBER_OF_INTERPOLATED_FRAMES - 1; i++) {
data[i][NUMBER_OF_SAMPLES] = data[i][0];
}
}
Drawing - nothing unusual
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawContour(canvas, outerPoints, currentFrame, outerPaint);
canvas.drawCircle(center.x, center.y, radius, middlePaint);
drawContour(canvas, innerPoints, currentFrame, innerPaint);
currentFrame++;
if (currentFrame >= NUMBER_OF_INTERPOLATED_FRAMES) {
currentFrame = NUMBER_OF_INTERPOLATED_FRAMES - 1;
}
}
All together
20 Hz
Visualizer
Average Scale Filter Interpolate 60 Hz
onDraw
CONCLUSION
Conclusion
● Animation is change of state through time
● 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 :)
QA
QA
To save myself from having to answer embarrassing questions 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.
Presentation: SpeakerDeck
Assets
Source code: https://bitbucket.org/fiveminutes/homan-demo/
http://five.agency/about/careers/
Breathing the life into the canvas

More Related Content

What's hot

Petri Niemi Qt Advanced Part 2
Petri Niemi Qt Advanced Part 2Petri Niemi Qt Advanced Part 2
Petri Niemi Qt Advanced Part 2
NokiaAppForum
 
Node js
Node jsNode js
Node js
LearningTech
 
Multi qubit entanglement
Multi qubit entanglementMulti qubit entanglement
Multi qubit entanglement
Vijayananda Mohire
 
Options and trade offs for parallelism and concurrency in Modern C++
Options and trade offs for parallelism and concurrency in Modern C++Options and trade offs for parallelism and concurrency in Modern C++
Options and trade offs for parallelism and concurrency in Modern C++
Satalia
 
OpenGL L06-Performance
OpenGL L06-PerformanceOpenGL L06-Performance
OpenGL L06-Performance
Mohammad Shaker
 
Readme
ReadmeReadme
Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019
Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019 Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019
Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019
Unity Technologies
 
OpenGL L03-Utilities
OpenGL L03-UtilitiesOpenGL L03-Utilities
OpenGL L03-Utilities
Mohammad Shaker
 
(5) cpp dynamic memory_arrays_and_c-strings
(5) cpp dynamic memory_arrays_and_c-strings(5) cpp dynamic memory_arrays_and_c-strings
(5) cpp dynamic memory_arrays_and_c-strings
Nico Ludwig
 
OpenGL L07-Skybox and Terrian
OpenGL L07-Skybox and TerrianOpenGL L07-Skybox and Terrian
OpenGL L07-Skybox and Terrian
Mohammad Shaker
 
libGDX: User Input and Frame by Frame Animation
libGDX: User Input and Frame by Frame AnimationlibGDX: User Input and Frame by Frame Animation
libGDX: User Input and Frame by Frame Animation
Jussi Pohjolainen
 
Scmad Chapter07
Scmad Chapter07Scmad Chapter07
Scmad Chapter07
Marcel Caraciolo
 
Special Effects with Qt Graphics View
Special Effects with Qt Graphics ViewSpecial Effects with Qt Graphics View
Special Effects with Qt Graphics View
account inactive
 

What's hot (13)

Petri Niemi Qt Advanced Part 2
Petri Niemi Qt Advanced Part 2Petri Niemi Qt Advanced Part 2
Petri Niemi Qt Advanced Part 2
 
Node js
Node jsNode js
Node js
 
Multi qubit entanglement
Multi qubit entanglementMulti qubit entanglement
Multi qubit entanglement
 
Options and trade offs for parallelism and concurrency in Modern C++
Options and trade offs for parallelism and concurrency in Modern C++Options and trade offs for parallelism and concurrency in Modern C++
Options and trade offs for parallelism and concurrency in Modern C++
 
OpenGL L06-Performance
OpenGL L06-PerformanceOpenGL L06-Performance
OpenGL L06-Performance
 
Readme
ReadmeReadme
Readme
 
Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019
Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019 Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019
Intrinsics: Low-level engine development with Burst - Unite Copenhagen 2019
 
OpenGL L03-Utilities
OpenGL L03-UtilitiesOpenGL L03-Utilities
OpenGL L03-Utilities
 
(5) cpp dynamic memory_arrays_and_c-strings
(5) cpp dynamic memory_arrays_and_c-strings(5) cpp dynamic memory_arrays_and_c-strings
(5) cpp dynamic memory_arrays_and_c-strings
 
OpenGL L07-Skybox and Terrian
OpenGL L07-Skybox and TerrianOpenGL L07-Skybox and Terrian
OpenGL L07-Skybox and Terrian
 
libGDX: User Input and Frame by Frame Animation
libGDX: User Input and Frame by Frame AnimationlibGDX: User Input and Frame by Frame Animation
libGDX: User Input and Frame by Frame Animation
 
Scmad Chapter07
Scmad Chapter07Scmad Chapter07
Scmad Chapter07
 
Special Effects with Qt Graphics View
Special Effects with Qt Graphics ViewSpecial Effects with Qt Graphics View
Special Effects with Qt Graphics View
 

Viewers also liked

Value network analysis of malawian legume systems - 2015
Value network analysis of malawian legume systems - 2015Value network analysis of malawian legume systems - 2015
Value network analysis of malawian legume systems - 2015
Gcazo14
 
Cert001
Cert001Cert001
INCLUSIVE DESIGN: Going beyond Accessibility
INCLUSIVE DESIGN: Going beyond AccessibilityINCLUSIVE DESIGN: Going beyond Accessibility
INCLUSIVE DESIGN: Going beyond Accessibility
Michael Miles
 
Ficha Curso de Iniciación a la Radiodifusión
Ficha Curso de Iniciación a la RadiodifusiónFicha Curso de Iniciación a la Radiodifusión
Ficha Curso de Iniciación a la Radiodifusión
SEGUROS RED - Escuela de Seguros Campus Asegurador
 
Toxic Plastic Trash Drift in the North Pacific Subtropical Gyre Final Paper
Toxic Plastic Trash Drift in the North Pacific Subtropical Gyre Final PaperToxic Plastic Trash Drift in the North Pacific Subtropical Gyre Final Paper
Toxic Plastic Trash Drift in the North Pacific Subtropical Gyre Final Paper
Kameron Johnson
 
MERS: emergence of a novel human coronavirus
MERS: emergence of a novel human coronavirusMERS: emergence of a novel human coronavirus
MERS: emergence of a novel human coronavirus
靖軒 黃
 
Tyre Marketing - Linkedin
Tyre Marketing - LinkedinTyre Marketing - Linkedin
Tyre Marketing - Linkedin
Gary Potter
 
How To Get Pregnant Naturally
How To Get Pregnant NaturallyHow To Get Pregnant Naturally
How To Get Pregnant Naturally
mtviva
 
Emission Reductions from Electric Cars Will Increase Every Year – Isn’t that ...
Emission Reductions from Electric Cars Will Increase Every Year – Isn’t that ...Emission Reductions from Electric Cars Will Increase Every Year – Isn’t that ...
Emission Reductions from Electric Cars Will Increase Every Year – Isn’t that ...
Katy Kidwell
 
Lesson six
Lesson sixLesson six
Lesson six
Chris Earl
 
Historia de México I: Bloque I.- Categorías teórico-metodológicas para el est...
Historia de México I: Bloque I.- Categorías teórico-metodológicas para el est...Historia de México I: Bloque I.- Categorías teórico-metodológicas para el est...
Historia de México I: Bloque I.- Categorías teórico-metodológicas para el est...
Moishef HerCo
 

Viewers also liked (11)

Value network analysis of malawian legume systems - 2015
Value network analysis of malawian legume systems - 2015Value network analysis of malawian legume systems - 2015
Value network analysis of malawian legume systems - 2015
 
Cert001
Cert001Cert001
Cert001
 
INCLUSIVE DESIGN: Going beyond Accessibility
INCLUSIVE DESIGN: Going beyond AccessibilityINCLUSIVE DESIGN: Going beyond Accessibility
INCLUSIVE DESIGN: Going beyond Accessibility
 
Ficha Curso de Iniciación a la Radiodifusión
Ficha Curso de Iniciación a la RadiodifusiónFicha Curso de Iniciación a la Radiodifusión
Ficha Curso de Iniciación a la Radiodifusión
 
Toxic Plastic Trash Drift in the North Pacific Subtropical Gyre Final Paper
Toxic Plastic Trash Drift in the North Pacific Subtropical Gyre Final PaperToxic Plastic Trash Drift in the North Pacific Subtropical Gyre Final Paper
Toxic Plastic Trash Drift in the North Pacific Subtropical Gyre Final Paper
 
MERS: emergence of a novel human coronavirus
MERS: emergence of a novel human coronavirusMERS: emergence of a novel human coronavirus
MERS: emergence of a novel human coronavirus
 
Tyre Marketing - Linkedin
Tyre Marketing - LinkedinTyre Marketing - Linkedin
Tyre Marketing - Linkedin
 
How To Get Pregnant Naturally
How To Get Pregnant NaturallyHow To Get Pregnant Naturally
How To Get Pregnant Naturally
 
Emission Reductions from Electric Cars Will Increase Every Year – Isn’t that ...
Emission Reductions from Electric Cars Will Increase Every Year – Isn’t that ...Emission Reductions from Electric Cars Will Increase Every Year – Isn’t that ...
Emission Reductions from Electric Cars Will Increase Every Year – Isn’t that ...
 
Lesson six
Lesson sixLesson six
Lesson six
 
Historia de México I: Bloque I.- Categorías teórico-metodológicas para el est...
Historia de México I: Bloque I.- Categorías teórico-metodológicas para el est...Historia de México I: Bloque I.- Categorías teórico-metodológicas para el est...
Historia de México I: Bloque I.- Categorías teórico-metodológicas para el est...
 

Similar to Breathing the life into the canvas

Enhancing UI/UX using Java animations
Enhancing UI/UX using Java animationsEnhancing UI/UX using Java animations
Enhancing UI/UX using Java animations
Naman Dwivedi
 
Creating an Uber Clone - Part IV - Transcript.pdf
Creating an Uber Clone - Part IV - Transcript.pdfCreating an Uber Clone - Part IV - Transcript.pdf
Creating an Uber Clone - Part IV - Transcript.pdf
ShaiAlmog1
 
I wanted to change the cloudsrectangles into an actuall image it do.pdf
I wanted to change the cloudsrectangles into an actuall image it do.pdfI wanted to change the cloudsrectangles into an actuall image it do.pdf
I wanted to change the cloudsrectangles into an actuall image it do.pdf
feelinggifts
 
ITT 2014 - Chris Eidhof - Practical Concurrent Programming
ITT 2014 - Chris Eidhof - Practical Concurrent ProgrammingITT 2014 - Chris Eidhof - Practical Concurrent Programming
ITT 2014 - Chris Eidhof - Practical Concurrent Programming
Istanbul Tech Talks
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
DroidConTLV
 
Computer graphics
Computer graphicsComputer graphics
Computer graphics
snelkoli
 
Day 1
Day 1Day 1
14multithreaded Graphics
14multithreaded Graphics14multithreaded Graphics
14multithreaded Graphics
Adil Jafri
 
Android App Development - 12 animations
Android App Development - 12 animationsAndroid App Development - 12 animations
Android App Development - 12 animations
Diego Grancini
 
Java 8
Java 8Java 8
Streaming Data Pipelines With Apache Beam
Streaming Data Pipelines With Apache BeamStreaming Data Pipelines With Apache Beam
Streaming Data Pipelines With Apache Beam
All Things Open
 
Tools for developing Android Games
 Tools for developing Android Games Tools for developing Android Games
Tools for developing Android Games
Platty Soft
 
Unit 11. Graphics
Unit 11. GraphicsUnit 11. Graphics
Unit 11. Graphics
Ashim Lamichhane
 
Rotoscope inthebrowserppt billy
Rotoscope inthebrowserppt billyRotoscope inthebrowserppt billy
Rotoscope inthebrowserppt billy
nimbleltd
 
Chapter-3.pdf
Chapter-3.pdfChapter-3.pdf
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
AOE
 
NDC Sydney 2019 - Async Demystified -- Karel Zikmund
NDC Sydney 2019 - Async Demystified -- Karel ZikmundNDC Sydney 2019 - Async Demystified -- Karel Zikmund
NDC Sydney 2019 - Async Demystified -- Karel Zikmund
Karel Zikmund
 
Trident International Graphics Workshop 2014 1/5
Trident International Graphics Workshop 2014 1/5Trident International Graphics Workshop 2014 1/5
Trident International Graphics Workshop 2014 1/5
Takao Wada
 
Copy Your Favourite Nokia App with Qt
Copy Your Favourite Nokia App with QtCopy Your Favourite Nokia App with Qt
Copy Your Favourite Nokia App with Qt
account inactive
 
C Graphics Functions
C Graphics FunctionsC Graphics Functions
C Graphics Functions
SHAKOOR AB
 

Similar to Breathing the life into the canvas (20)

Enhancing UI/UX using Java animations
Enhancing UI/UX using Java animationsEnhancing UI/UX using Java animations
Enhancing UI/UX using Java animations
 
Creating an Uber Clone - Part IV - Transcript.pdf
Creating an Uber Clone - Part IV - Transcript.pdfCreating an Uber Clone - Part IV - Transcript.pdf
Creating an Uber Clone - Part IV - Transcript.pdf
 
I wanted to change the cloudsrectangles into an actuall image it do.pdf
I wanted to change the cloudsrectangles into an actuall image it do.pdfI wanted to change the cloudsrectangles into an actuall image it do.pdf
I wanted to change the cloudsrectangles into an actuall image it do.pdf
 
ITT 2014 - Chris Eidhof - Practical Concurrent Programming
ITT 2014 - Chris Eidhof - Practical Concurrent ProgrammingITT 2014 - Chris Eidhof - Practical Concurrent Programming
ITT 2014 - Chris Eidhof - Practical Concurrent Programming
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
 
Computer graphics
Computer graphicsComputer graphics
Computer graphics
 
Day 1
Day 1Day 1
Day 1
 
14multithreaded Graphics
14multithreaded Graphics14multithreaded Graphics
14multithreaded Graphics
 
Android App Development - 12 animations
Android App Development - 12 animationsAndroid App Development - 12 animations
Android App Development - 12 animations
 
Java 8
Java 8Java 8
Java 8
 
Streaming Data Pipelines With Apache Beam
Streaming Data Pipelines With Apache BeamStreaming Data Pipelines With Apache Beam
Streaming Data Pipelines With Apache Beam
 
Tools for developing Android Games
 Tools for developing Android Games Tools for developing Android Games
Tools for developing Android Games
 
Unit 11. Graphics
Unit 11. GraphicsUnit 11. Graphics
Unit 11. Graphics
 
Rotoscope inthebrowserppt billy
Rotoscope inthebrowserppt billyRotoscope inthebrowserppt billy
Rotoscope inthebrowserppt billy
 
Chapter-3.pdf
Chapter-3.pdfChapter-3.pdf
Chapter-3.pdf
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
 
NDC Sydney 2019 - Async Demystified -- Karel Zikmund
NDC Sydney 2019 - Async Demystified -- Karel ZikmundNDC Sydney 2019 - Async Demystified -- Karel Zikmund
NDC Sydney 2019 - Async Demystified -- Karel Zikmund
 
Trident International Graphics Workshop 2014 1/5
Trident International Graphics Workshop 2014 1/5Trident International Graphics Workshop 2014 1/5
Trident International Graphics Workshop 2014 1/5
 
Copy Your Favourite Nokia App with Qt
Copy Your Favourite Nokia App with QtCopy Your Favourite Nokia App with Qt
Copy Your Favourite Nokia App with Qt
 
C Graphics Functions
C Graphics FunctionsC Graphics Functions
C Graphics Functions
 

Recently uploaded

Engine Lubrication performance System.pdf
Engine Lubrication performance System.pdfEngine Lubrication performance System.pdf
Engine Lubrication performance System.pdf
mamamaam477
 
Harnessing WebAssembly for Real-time Stateless Streaming Pipelines
Harnessing WebAssembly for Real-time Stateless Streaming PipelinesHarnessing WebAssembly for Real-time Stateless Streaming Pipelines
Harnessing WebAssembly for Real-time Stateless Streaming Pipelines
Christina Lin
 
Unit-III-ELECTROCHEMICAL STORAGE DEVICES.ppt
Unit-III-ELECTROCHEMICAL STORAGE DEVICES.pptUnit-III-ELECTROCHEMICAL STORAGE DEVICES.ppt
Unit-III-ELECTROCHEMICAL STORAGE DEVICES.ppt
KrishnaveniKrishnara1
 
Question paper of renewable energy sources
Question paper of renewable energy sourcesQuestion paper of renewable energy sources
Question paper of renewable energy sources
mahammadsalmanmech
 
ISPM 15 Heat Treated Wood Stamps and why your shipping must have one
ISPM 15 Heat Treated Wood Stamps and why your shipping must have oneISPM 15 Heat Treated Wood Stamps and why your shipping must have one
ISPM 15 Heat Treated Wood Stamps and why your shipping must have one
Las Vegas Warehouse
 
Comparative analysis between traditional aquaponics and reconstructed aquapon...
Comparative analysis between traditional aquaponics and reconstructed aquapon...Comparative analysis between traditional aquaponics and reconstructed aquapon...
Comparative analysis between traditional aquaponics and reconstructed aquapon...
bijceesjournal
 
A review on techniques and modelling methodologies used for checking electrom...
A review on techniques and modelling methodologies used for checking electrom...A review on techniques and modelling methodologies used for checking electrom...
A review on techniques and modelling methodologies used for checking electrom...
nooriasukmaningtyas
 
Engineering Drawings Lecture Detail Drawings 2014.pdf
Engineering Drawings Lecture Detail Drawings 2014.pdfEngineering Drawings Lecture Detail Drawings 2014.pdf
Engineering Drawings Lecture Detail Drawings 2014.pdf
abbyasa1014
 
The Python for beginners. This is an advance computer language.
The Python for beginners. This is an advance computer language.The Python for beginners. This is an advance computer language.
The Python for beginners. This is an advance computer language.
sachin chaurasia
 
spirit beverages ppt without graphics.pptx
spirit beverages ppt without graphics.pptxspirit beverages ppt without graphics.pptx
spirit beverages ppt without graphics.pptx
Madan Karki
 
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
insn4465
 
CSM Cloud Service Management Presentarion
CSM Cloud Service Management PresentarionCSM Cloud Service Management Presentarion
CSM Cloud Service Management Presentarion
rpskprasana
 
KuberTENes Birthday Bash Guadalajara - K8sGPT first impressions
KuberTENes Birthday Bash Guadalajara - K8sGPT first impressionsKuberTENes Birthday Bash Guadalajara - K8sGPT first impressions
KuberTENes Birthday Bash Guadalajara - K8sGPT first impressions
Victor Morales
 
CHINA’S GEO-ECONOMIC OUTREACH IN CENTRAL ASIAN COUNTRIES AND FUTURE PROSPECT
CHINA’S GEO-ECONOMIC OUTREACH IN CENTRAL ASIAN COUNTRIES AND FUTURE PROSPECTCHINA’S GEO-ECONOMIC OUTREACH IN CENTRAL ASIAN COUNTRIES AND FUTURE PROSPECT
CHINA’S GEO-ECONOMIC OUTREACH IN CENTRAL ASIAN COUNTRIES AND FUTURE PROSPECT
jpsjournal1
 
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball playEric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
enizeyimana36
 
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Sinan KOZAK
 
International Conference on NLP, Artificial Intelligence, Machine Learning an...
International Conference on NLP, Artificial Intelligence, Machine Learning an...International Conference on NLP, Artificial Intelligence, Machine Learning an...
International Conference on NLP, Artificial Intelligence, Machine Learning an...
gerogepatton
 
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODELDEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
gerogepatton
 
Generative AI leverages algorithms to create various forms of content
Generative AI leverages algorithms to create various forms of contentGenerative AI leverages algorithms to create various forms of content
Generative AI leverages algorithms to create various forms of content
Hitesh Mohapatra
 
Recycled Concrete Aggregate in Construction Part III
Recycled Concrete Aggregate in Construction Part IIIRecycled Concrete Aggregate in Construction Part III
Recycled Concrete Aggregate in Construction Part III
Aditya Rajan Patra
 

Recently uploaded (20)

Engine Lubrication performance System.pdf
Engine Lubrication performance System.pdfEngine Lubrication performance System.pdf
Engine Lubrication performance System.pdf
 
Harnessing WebAssembly for Real-time Stateless Streaming Pipelines
Harnessing WebAssembly for Real-time Stateless Streaming PipelinesHarnessing WebAssembly for Real-time Stateless Streaming Pipelines
Harnessing WebAssembly for Real-time Stateless Streaming Pipelines
 
Unit-III-ELECTROCHEMICAL STORAGE DEVICES.ppt
Unit-III-ELECTROCHEMICAL STORAGE DEVICES.pptUnit-III-ELECTROCHEMICAL STORAGE DEVICES.ppt
Unit-III-ELECTROCHEMICAL STORAGE DEVICES.ppt
 
Question paper of renewable energy sources
Question paper of renewable energy sourcesQuestion paper of renewable energy sources
Question paper of renewable energy sources
 
ISPM 15 Heat Treated Wood Stamps and why your shipping must have one
ISPM 15 Heat Treated Wood Stamps and why your shipping must have oneISPM 15 Heat Treated Wood Stamps and why your shipping must have one
ISPM 15 Heat Treated Wood Stamps and why your shipping must have one
 
Comparative analysis between traditional aquaponics and reconstructed aquapon...
Comparative analysis between traditional aquaponics and reconstructed aquapon...Comparative analysis between traditional aquaponics and reconstructed aquapon...
Comparative analysis between traditional aquaponics and reconstructed aquapon...
 
A review on techniques and modelling methodologies used for checking electrom...
A review on techniques and modelling methodologies used for checking electrom...A review on techniques and modelling methodologies used for checking electrom...
A review on techniques and modelling methodologies used for checking electrom...
 
Engineering Drawings Lecture Detail Drawings 2014.pdf
Engineering Drawings Lecture Detail Drawings 2014.pdfEngineering Drawings Lecture Detail Drawings 2014.pdf
Engineering Drawings Lecture Detail Drawings 2014.pdf
 
The Python for beginners. This is an advance computer language.
The Python for beginners. This is an advance computer language.The Python for beginners. This is an advance computer language.
The Python for beginners. This is an advance computer language.
 
spirit beverages ppt without graphics.pptx
spirit beverages ppt without graphics.pptxspirit beverages ppt without graphics.pptx
spirit beverages ppt without graphics.pptx
 
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
哪里办理(csu毕业证书)查尔斯特大学毕业证硕士学历原版一模一样
 
CSM Cloud Service Management Presentarion
CSM Cloud Service Management PresentarionCSM Cloud Service Management Presentarion
CSM Cloud Service Management Presentarion
 
KuberTENes Birthday Bash Guadalajara - K8sGPT first impressions
KuberTENes Birthday Bash Guadalajara - K8sGPT first impressionsKuberTENes Birthday Bash Guadalajara - K8sGPT first impressions
KuberTENes Birthday Bash Guadalajara - K8sGPT first impressions
 
CHINA’S GEO-ECONOMIC OUTREACH IN CENTRAL ASIAN COUNTRIES AND FUTURE PROSPECT
CHINA’S GEO-ECONOMIC OUTREACH IN CENTRAL ASIAN COUNTRIES AND FUTURE PROSPECTCHINA’S GEO-ECONOMIC OUTREACH IN CENTRAL ASIAN COUNTRIES AND FUTURE PROSPECT
CHINA’S GEO-ECONOMIC OUTREACH IN CENTRAL ASIAN COUNTRIES AND FUTURE PROSPECT
 
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball playEric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
Eric Nizeyimana's document 2006 from gicumbi to ttc nyamata handball play
 
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
Optimizing Gradle Builds - Gradle DPE Tour Berlin 2024
 
International Conference on NLP, Artificial Intelligence, Machine Learning an...
International Conference on NLP, Artificial Intelligence, Machine Learning an...International Conference on NLP, Artificial Intelligence, Machine Learning an...
International Conference on NLP, Artificial Intelligence, Machine Learning an...
 
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODELDEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
DEEP LEARNING FOR SMART GRID INTRUSION DETECTION: A HYBRID CNN-LSTM-BASED MODEL
 
Generative AI leverages algorithms to create various forms of content
Generative AI leverages algorithms to create various forms of contentGenerative AI leverages algorithms to create various forms of content
Generative AI leverages algorithms to create various forms of content
 
Recycled Concrete Aggregate in Construction Part III
Recycled Concrete Aggregate in Construction Part IIIRecycled Concrete Aggregate in Construction Part III
Recycled Concrete Aggregate in Construction Part III
 

Breathing the life into the canvas

  • 1. BREATHING LIFE INTO THE CANVAS #droidconzgTomislav Homan Tomislav Homan, Five
  • 3. Intro ● Why custom views? ● Not everything can be solved with standard views ● We want to draw directly onto the Canvas ● Graphs and diagrams ● External data from sensors and mic (equalizer) ● ….
  • 4.
  • 5. Couple of advices 1 / 3 - Initialize paints early 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(); } …..
  • 6. Couple of advices 1 / 3 - Initialize paints early private void init() { axisPaint = new Paint(Paint.ANTI_ALIAS_FLAG); axisPaint.setColor(Color.BLACK); axisPaint.setStyle(Paint.Style.STROKE); axisPaint.setStrokeWidth(4.0f); ….. }
  • 7. Couple of advices 2 / 3 - Memorize all the 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); }
  • 8. Couple of advices 3 / 3 - Use onDraw only to draw @Override protected void onDraw(final Canvas canvas) { super.onDraw(canvas); canvas.drawLine(xAxisStart.x, xAxisStart.y, xAxisEnd.x, xAxisEnd.y, axisPaint); canvas.drawLine(yAxisStart.x, yAxisStart.y, yAxisEnd.x, yAxisEnd.y, axisPaint); …. canvas.drawPath(graphPath, graphPaint); }
  • 10. ● 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 A bit of philosophy Animating custom views
  • 11. Animating custom views Let’s 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
  • 12.
  • 13. 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
  • 14. Determine important constants private static final long UI_REFRESH_RATE = 60L; // fps private static final long ANIMATION_REFRESHING_INTERVAL = TimeUnit.SECONDS.toMillis(1L) / UI_REFRESH_RATE; // millis private static final long ANIMATION_DURATION_IN_MILLIS = 1500L; // millis private static final long NUMBER_OF_FRAMES = ANIMATION_DURATION_IN_MILLIS / ANIMATION_REFRESHING_INTERVAL;
  • 15. Animating custom views Determine important constants ● For animation that lasts 1500 milliseconds in framerate of 60 fps... ● We should refresh the screen every cca 16 milliseconds ● And we have cca 94 frames
  • 16. Initialize paints and other expensive objects - business as usual private void init() { dotPaint = new Paint(Paint.ANTI_ALIAS_FLAG); dotPaint.setColor(Color.RED); dotPaint.setStyle(Paint.Style.FILL); dotPaint.setStrokeWidth(1.0f); endPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG); endPointPaint.setColor(Color.GREEN); endPointPaint.setStyle(Paint.Style.FILL); endPointPaint.setStrokeWidth(1.0f); }
  • 17. (Re)calculate size dependent stuff on size changed @Override protected void 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); …. }
  • 18. Implement main loop private final Handler uiHandler = new Handler(Looper.getMainLooper()); private void startAnimating() { calculateFrames(); uiHandler.post(invalidateUI); } private void stopAnimating() { uiHandler.removeCallbacks(invalidateUI); }
  • 19. Implement main loop private Runnable invalidateUI = new Runnable() { @Override public void run() { if (hasFrameToDraw()) { invalidate(); uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL); } else { isAnimating = false; } } };
  • 20. Calculate state ● Create frames array ● Determine step by which state changes ● Increase positions by step Animating custom views
  • 21. Calculate state private void calculateFrames() { frames = new PointF[NUMBER_OF_FRAMES + 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; }
  • 22. Animating custom views Draw ● Now piece of cake ● Draw static stuff ● Take and draw current frame ● Increase the counter
  • 23. Draw @Override protected void onDraw(final Canvas canvas) { super.onDraw(canvas); drawDot(canvas, startPoint, endPointPaint); drawDot(canvas, endPoint, endPointPaint); final PointF currentPoint = frames[currentFrame]; drawDot(canvas, currentPoint, dotPaint); currentFrame++; }
  • 24. Now we want to animate the graph from one state to another
  • 25. 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;
  • 27. 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
  • 28. Easing in and out Easing out (deceleration) ● Differences while 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; }
  • 29. A bit of high school math….
  • 30. ….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
  • 31. The rest of the recipe is same: ● Calculation phase 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; }
  • 32. Easing in (acceleration) ● Same approach ● Different starting conditions - initial velocity zero ● Renders a bit different formula
  • 33. 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 :(
  • 34. The magic formula: Xi = (- 2 * L / 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
  • 35. Easing in and out Graph example ● Again: Use same formulas, but on a more complicated state
  • 37. Dynamic frame calculation Actually 2 approaches for calculating state ● Pre-calculate all the frames (states) - we did this ● Calculate the next frame from the current one dynamically
  • 38. Dynamic frame calculation Pre-calculate all the frames (states) ● All 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
  • 39. Dynamic frame calculation Dynamic state calculation ● Calculate the new 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
  • 40. Dynamic frame calculation ● First example - a dot that 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
  • 41. Dynamic frame calculation The recipe ● Determine important constants - 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
  • 42. Implement the main loop private final Handler uiHandler = new Handler(Looper.getMainLooper()); private void startAnimating() { calculateFrames(); uiHandler.post(invalidateUI); } private void stopAnimating() { uiHandler.removeCallbacks(invalidateUI); }
  • 43. Implement the main loop private Runnable invalidateUI = new Runnable() { @Override public void run() { if (hasFrameToDraw()) { invalidate(); uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL); } else { isAnimating = false; } } };
  • 44. Implement the main loop private void startAnimating() { uiHandler.post(invalidateUI); } private void stopAnimating() { uiHandler.removeCallbacks(invalidateUI); } private Runnable invalidateUI = new Runnable() { @Override public void run() { invalidate(); uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL); } };
  • 45. Draw @Override protected void onDraw(final Canvas canvas) { super.onDraw(canvas); drawDot(canvas, startPoint, endPointPaint); drawDot(canvas, endPoint, endPointPaint); final PointF currentPoint = frames[currentFrame]; drawDot(canvas, currentPoint, dotPaint); currentFrame++; }
  • 46. Draw private PointF currentPosition; @Override protected void onDraw(final Canvas canvas) { super.onDraw(canvas); canvas.drawRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y, wallsPaint); drawDot(canvas, currentPosition, dotPaint); if (isAnimating) { updateWorld(); } }
  • 47. Calculate state private void updateWorld() { final float dx = currentVelocity.x; // * dt final float dy = currentVelocity.y; // * dt currentPosition.set(currentPosition.x + dx, currentPosition.y + dy); if (hitRightWall()) { currentVelocity.x = -currentVelocity.x; currentPosition.set(topRight.x - WALL_THICKNESS, currentPosition.y); } //Same for every wall } private boolean hitRightWall() { return currentPosition.x >= topRight.x - WALL_THICKNESS; }
  • 48. Dynamic frame calculation Add gravity to previous example ● Just a couple of lines more private void updateWorld() { final float dvx = GRAVITY.x; final float dvy = GRAVITY.y; currentVelocity.set(currentVelocity.x + dvx, currentVelocity.y + dvy); final float dx = currentVelocity.x; // * dt final float dy = currentVelocity.y; // * dt currentPosition.set(currentPosition.x + dx, currentPosition.y + dy); ….. }
  • 49. Springs ● Define a circle of given radius ● Define couple of control points with random distance from the circle ● Let control points spring around the circle
  • 50. private void updateWorld() { final int angleStep = 360 / NUMBER_OD_CONTROL_POINTS; for (int i = 0; i < controlPoints.length; i++) { final PointF point = controlPoints[i]; final PointF velocity = controlPointsVelocities[i]; final PointF springCenter = CoordinateUtils.fromPolar((int) radius, i * angleStep, centerPosition); final float forceX = -SPRING_CONSTANT * (point.x - springCenter.x); final float forceY = -SPRING_CONSTANT * (point.y - springCenter.y); final float dvx = forceX; final float dvy = forceY; velocity.set(velocity.x + dvx, velocity.y + dvy); final float dx = velocity.x; final float dy = velocity.y; point.set(point.x + dx, point.y + dy); } }
  • 51. Dynamic frame calculation Usefulness of those animations ● Not very 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...
  • 53. Animating external input ● It sometimes happens that your state 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
  • 54. Main loop - just invalidating in 60 fps private void startAnimating() { uiHandler.post(invalidateUI); } private void stopAnimating() { uiHandler.removeCallbacks(invalidateUI); } private Runnable invalidateUI = new Runnable() { @Override public void run() { invalidate(); uiHandler.postDelayed(this, ANIMATION_REFRESHING_INTERVAL); } };
  • 55. Data input private static final int SOUND_CAPTURE_RATE = 20; // Hz private void startCapturingAudioSamples(int audioSessionId) { visualizer = new Visualizer(audioSessionId); visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]); visualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() { @Override public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) { } @Override public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) { calculateData(fft); } }, SOUND_CAPTURE_RATE * 1000, false, true); visualizer.setEnabled(true); } Triggered 20 times in a second
  • 56. Transforming data private void calculateData(byte[] bytes) { final int[] truncatedData = truncateData(bytes); final int[] magnitudes = calculateMagnitudes(truncatedData); final int[] outerScaledData = scaleData(magnitudes, OUTER_SCALE_TARGET); final int[] innerScaledData = scaleData(magnitudes, INNER_SCALE_TARGET); final int[] outerAveragedData = averageData(outerScaledData); final int[] innerAveragedData = averageData(innerScaledData); this.outerPoints = calculateContours(outerPoints, outerAveragedData, OUTER_OFFSET, true); this.innerPoints = calculateContours(innerPoints, innerAveragedData, INNER_OFFSET, false); currentFrame = 0; } This is now drawable
  • 57. 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
  • 58. Interpolation private PointF[][] calculateContours(final PointF[][] currentData, final int[] averagedData, final int offset, final boolean goOutwards) { ……. fillWithLinearyInterpolatedFrames(newFrames); ……. return newFrames; } private void fillWithLinearyInterpolatedFrames(final PointF[][] data) { for (int j = 0; j < NUMBER_OF_SAMPLES; j++) { final PointF targetPoint = data[NUMBER_OF_INTERPOLATED_FRAMES - 1][j]; final PointF originPoint = data[0][j]; final double deltaX = (targetPoint.x - originPoint.x) / NUMBER_OF_INTERPOLATED_FRAMES; final double deltaY = (targetPoint.y - originPoint.y) / NUMBER_OF_INTERPOLATED_FRAMES; for (int i = 1; i < NUMBER_OF_INTERPOLATED_FRAMES - 1; i++) { data[i][j] = new PointF((float) (originPoint.x + i * deltaX), (float) (originPoint.y + i * deltaY)); } } for (int i = 1; i < NUMBER_OF_INTERPOLATED_FRAMES - 1; i++) { data[i][NUMBER_OF_SAMPLES] = data[i][0]; } }
  • 59. Drawing - nothing unusual @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawContour(canvas, outerPoints, currentFrame, outerPaint); canvas.drawCircle(center.x, center.y, radius, middlePaint); drawContour(canvas, innerPoints, currentFrame, innerPaint); currentFrame++; if (currentFrame >= NUMBER_OF_INTERPOLATED_FRAMES) { currentFrame = NUMBER_OF_INTERPOLATED_FRAMES - 1; } }
  • 60. All together 20 Hz Visualizer Average Scale Filter Interpolate 60 Hz onDraw
  • 62. Conclusion ● Animation is change of state through time ● 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 :)
  • 63. QA
  • 64. QA To save myself from having to answer embarrassing questions 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.
  • 65. Presentation: SpeakerDeck Assets Source code: https://bitbucket.org/fiveminutes/homan-demo/