/* * Copyright (C) 2011 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.jakewharton.nineoldandroids.sample.droidflakes; import java.util.ArrayList; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.view.View; import com.nineoldandroids.animation.ValueAnimator; import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener; import com.jakewharton.nineoldandroids.sample.R; /** * This class is the custom view where all of the Droidflakes are drawn. This class has * all of the logic for adding, subtracting, and rendering Droidflakes. */ public class FlakeView extends View { Bitmap droid; // The bitmap that all flakes use int numFlakes = 0; // Current number of flakes ArrayList<Flake> flakes = new ArrayList<Flake>(); // List of current flakes // Animator used to drive all separate flake animations. Rather than have potentially // hundreds of separate animators, we just use one and then update all flakes for each // frame of that single animation. ValueAnimator animator = ValueAnimator.ofFloat(0, 1); long startTime, prevTime; // Used to track elapsed time for animations and fps int frames = 0; // Used to track frames per second Paint textPaint; // Used for rendering fps text float fps = 0; // frames per second Matrix m = new Matrix(); // Matrix used to translate/rotate each flake during rendering String fpsString = ""; String numFlakesString = ""; /** * Constructor. Create objects used throughout the life of the View: the Paint and * the animator */ public FlakeView(Context context) { super(context); droid = BitmapFactory.decodeResource(getResources(), R.drawable.droid); textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setColor(Color.WHITE); textPaint.setTextSize(24); // This listener is where the action is for the flak animations. Every frame of the // animation, we calculate the elapsed time and update every flake's position and rotation // according to its speed. animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator arg0) { long nowTime = System.currentTimeMillis(); float secs = (float)(nowTime - prevTime) / 1000f; prevTime = nowTime; for (int i = 0; i < numFlakes; ++i) { Flake flake = flakes.get(i); flake.y += (flake.speed * secs); if (flake.y > getHeight()) { // If a flake falls off the bottom, send it back to the top flake.y = 0 - flake.height; } flake.rotation = flake.rotation + (flake.rotationSpeed * secs); } // Force a redraw to see the flakes in their new positions and orientations invalidate(); } }); animator.setRepeatCount(ValueAnimator.INFINITE); animator.setDuration(3000); } int getNumFlakes() { return numFlakes; } private void setNumFlakes(int quantity) { numFlakes = quantity; numFlakesString = "numFlakes: " + numFlakes; } /** * Add the specified number of droidflakes. */ void addFlakes(int quantity) { for (int i = 0; i < quantity; ++i) { flakes.add(Flake.createFlake(getWidth(), droid)); } setNumFlakes(numFlakes + quantity); } /** * Subtract the specified number of droidflakes. We just take them off the end of the * list, leaving the others unchanged. */ void subtractFlakes(int quantity) { for (int i = 0; i < quantity; ++i) { int index = numFlakes - i - 1; flakes.remove(index); } setNumFlakes(numFlakes - quantity); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Reset list of droidflakes, then restart it with 8 flakes flakes.clear(); numFlakes = 0; addFlakes(8); // Cancel animator in case it was already running animator.cancel(); // Set up fps tracking and start the animation startTime = System.currentTimeMillis(); prevTime = startTime; frames = 0; animator.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // For each flake: back-translate by half its size (this allows it to rotate around its center), // rotate by its current rotation, translate by its location, then draw its bitmap for (int i = 0; i < numFlakes; ++i) { Flake flake = flakes.get(i); m.setTranslate(-flake.width/2, -flake.height/2); m.postRotate(flake.rotation); m.postTranslate(flake.width/2 + flake.x, flake.height/2 + flake.y); canvas.drawBitmap(flake.bitmap, m, null); } // fps counter: count how many frames we draw and once a second calculate the // frames per second ++frames; long nowTime = System.currentTimeMillis(); long deltaTime = nowTime - startTime; if (deltaTime > 1000) { float secs = (float) deltaTime / 1000f; fps = (float) frames / secs; fpsString = "fps: " + fps; startTime = nowTime; frames = 0; } canvas.drawText(numFlakesString, getWidth() - 200, getHeight() - 50, textPaint); canvas.drawText(fpsString, getWidth() - 200, getHeight() - 80, textPaint); } public void pause() { // Make sure the animator's not spinning in the background when the activity is paused. animator.cancel(); } public void resume() { animator.start(); } }