/* * Copyright 2010, 2011, 2012 mapsforge.org * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package org.mapsforge.android.maps.inputhandling; import org.mapsforge.android.maps.MapView; import org.mapsforge.android.maps.PausableThread; import android.os.SystemClock; /** * A ZoomAnimator handles the zoom-in and zoom-out animations of the corresponding MapView. It runs in a separate thread * to avoid blocking the UI thread. */ public class ZoomAnimator extends PausableThread { private static final int DEFAULT_DURATION = 250; private static final int FRAME_LENGTH_IN_MS = 15; private static final String THREAD_NAME = "ZoomAnimator"; private boolean executeAnimation; private MapView mapView; private float pivotX; private float pivotY; private float scaleFactorApplied; private long timeStart; private float zoomDifference; private float zoomEnd; private float zoomStart; /** * @param mapView * the MapView whose zoom level changes should be animated. */ public ZoomAnimator(MapView mapView) { super(); this.mapView = mapView; } /** * @return true if the ZoomAnimator is working, false otherwise. */ public boolean isExecuting() { return this.executeAnimation; } /** * Sets the parameters for the zoom animation. * * @param zoomStart * the zoom factor at the begin of the animation. * @param zoomEnd * the zoom factor at the end of the animation. * @param pivotX * the x coordinate of the animation center. * @param pivotY * the y coordinate of the animation center. */ public void setParameters(float zoomStart, float zoomEnd, float pivotX, float pivotY) { this.zoomStart = zoomStart; this.zoomEnd = zoomEnd; this.pivotX = pivotX; this.pivotY = pivotY; } /** * Starts a zoom animation with the current parameters. */ public void startAnimation() { this.zoomDifference = this.zoomEnd - this.zoomStart; this.scaleFactorApplied = this.zoomStart; this.executeAnimation = true; this.timeStart = SystemClock.uptimeMillis(); synchronized (this) { notify(); } } @Override protected void doWork() throws InterruptedException { // calculate the elapsed time long timeElapsed = SystemClock.uptimeMillis() - this.timeStart; float timeElapsedPercent = Math.min(1, timeElapsed / (float) DEFAULT_DURATION); // calculate the zoom and scale values at the current moment float currentZoom = this.zoomStart + timeElapsedPercent * this.zoomDifference; float scaleFactor = currentZoom / this.scaleFactorApplied; this.scaleFactorApplied *= scaleFactor; this.mapView.getFrameBuffer().matrixPostScale(scaleFactor, scaleFactor, this.pivotX, this.pivotY); // check if the animation time is over if (timeElapsed >= DEFAULT_DURATION) { this.executeAnimation = false; this.mapView.redrawTiles(); onZoomFinish(); } else { this.mapView.postInvalidate(); sleep(FRAME_LENGTH_IN_MS); } } @Override protected void afterRun() { this.mapView = null; } @Override protected String getThreadName() { return THREAD_NAME; } @Override protected boolean hasWork() { return this.executeAnimation; } void onZoomFinish() { //Call to requestRedraw for overlays to cancel current drawing and redraw for (int i = 0, n = this.mapView.getOverlays().size(); i < n; ++i) { this.mapView.getOverlays().get(i).requestRedraw(); } } }