Back to project page AndroIUT.
The source code is released under:
GNU General Public License
If you think the Android project AndroIUT listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * Copyright 2010 Sony Ericsson Mobile Communications AB * //w w w . jav a2 s.c o m * 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. */ /** * Licence Modifications : Change package name */ package com.iutdijon.androiut2.util.zoom; import java.util.Observable; import java.util.Observer; import android.os.Handler; import android.os.SystemClock; import com.iutdijon.androiut2.util.zoom.util.SpringDynamics; /** * The DynamicZoomControl is responsible for controlling a ZoomState. It makes * sure that pan movement follows the finger, that limits are satisfied and that * we can zoom into specific positions. * * In order to implement these control mechanisms access to certain content and * view state data is required which is made possible through the * ZoomContentViewState. */ public class DynamicZoomControl implements Observer { /** Minimum zoom level limit */ private static final float MIN_ZOOM = 1; /** Maximum zoom level limit */ private static final float MAX_ZOOM = 16; /** Velocity tolerance for calculating if dynamic state is resting */ private static final float REST_VELOCITY_TOLERANCE = 0.004f; /** Position tolerance for calculating if dynamic state is resting */ private static final float REST_POSITION_TOLERANCE = 0.01f; /** Target FPS when animating behavior such as fling and snap to */ private static final int FPS = 50; /** Factor applied to pan motion outside of pan snap limits. */ private static final float PAN_OUTSIDE_SNAP_FACTOR = .4f; /** Zoom state under control */ private final ZoomState mState = new ZoomState(); /** Object holding aspect quotient of view and content */ private AspectQuotient mAspectQuotient; /** * Dynamics object for creating dynamic fling and snap to behavior for pan * in x-dimension. */ private final SpringDynamics mPanDynamicsX = new SpringDynamics(); /** * Dynamics object for creating dynamic fling and snap to behavior for pan * in y-dimension. */ private final SpringDynamics mPanDynamicsY = new SpringDynamics(); /** Minimum snap to position for pan in x-dimension */ private double mPanMinX; /** Maximum snap to position for pan in x-dimension */ private double mPanMaxX; /** Minimum snap to position for pan in y-dimension */ private double mPanMinY; /** Maximum snap to position for pan in y-dimension */ private double mPanMaxY; /** Handler for posting runnables */ private final Handler mHandler = new Handler(); /** Creates new zoom control */ public DynamicZoomControl() { mPanDynamicsX.setFriction(2f); mPanDynamicsY.setFriction(2f); mPanDynamicsX.setSpring(50f, 1f); mPanDynamicsY.setSpring(50f, 1f); } /** * Set reference object holding aspect quotient * * @param aspectQuotient Object holding aspect quotient */ public void setAspectQuotient(AspectQuotient aspectQuotient) { if (mAspectQuotient != null) { mAspectQuotient.deleteObserver(this); } mAspectQuotient = aspectQuotient; mAspectQuotient.addObserver(this); } /** * Get zoom state being controlled * * @return The zoom state */ public ZoomState getZoomState() { return mState; } /** * Zoom * * @param scale Factor of zoom to apply * @param x X-coordinate of invariant position * @param y Y-coordinate of invariant position */ public void zoom(double scale, float x, float y) { final float aspectQuotient = mAspectQuotient.get(); final double prevZoomX = mState.getZoomX(aspectQuotient); final double prevZoomY = mState.getZoomY(aspectQuotient); mState.setZoom(mState.getZoom() * scale); limitZoom(); final double newZoomX = mState.getZoomX(aspectQuotient); final double newZoomY = mState.getZoomY(aspectQuotient); // Pan to keep x and y coordinate invariant mState.setPanX(mState.getPanX() + (x - .5f) * (1f / prevZoomX - 1f / newZoomX)); mState.setPanY(mState.getPanY() + (y - .5f) * (1f / prevZoomY - 1f / newZoomY)); updatePanLimits(); mState.notifyObservers(); } /** * Pan * * @param dx Amount to pan in x-dimension * @param dy Amount to pan in y-dimension */ public void pan(float dx, float dy) { final float aspectQuotient = mAspectQuotient.get(); dx /= mState.getZoomX(aspectQuotient); dy /= mState.getZoomY(aspectQuotient); if (mState.getPanX() > mPanMaxX && dx > 0 || mState.getPanX() < mPanMinX && dx < 0) { dx *= PAN_OUTSIDE_SNAP_FACTOR; } if (mState.getPanY() > mPanMaxY && dy > 0 || mState.getPanY() < mPanMinY && dy < 0) { dy *= PAN_OUTSIDE_SNAP_FACTOR; } final double newPanX = mState.getPanX() + dx; final double newPanY = mState.getPanY() + dy; mState.setPanX(newPanX); mState.setPanY(newPanY); mState.notifyObservers(); } /** * Runnable that updates dynamics state */ private final Runnable mUpdateRunnable = new Runnable() { @Override public void run() { final long startTime = SystemClock.uptimeMillis(); mPanDynamicsX.update(startTime); mPanDynamicsY.update(startTime); final boolean isAtRest = mPanDynamicsX.isAtRest(REST_VELOCITY_TOLERANCE, REST_POSITION_TOLERANCE) && mPanDynamicsY.isAtRest(REST_VELOCITY_TOLERANCE, REST_POSITION_TOLERANCE); mState.setPanX(mPanDynamicsX.getPosition()); mState.setPanY(mPanDynamicsY.getPosition()); if (!isAtRest) { final long stopTime = SystemClock.uptimeMillis(); mHandler.postDelayed(mUpdateRunnable, 1000 / FPS - (stopTime - startTime)); } mState.notifyObservers(); } }; /** * Release control and start pan fling animation * * @param vx Velocity in x-dimension * @param vy Velocity in y-dimension */ public void startFling(float vx, float vy) { final float aspectQuotient = mAspectQuotient.get(); final long now = SystemClock.uptimeMillis(); mPanDynamicsX.setState(mState.getPanX(), vx / mState.getZoomX(aspectQuotient), now); mPanDynamicsY.setState(mState.getPanY(), vy / mState.getZoomY(aspectQuotient), now); mPanDynamicsX.setMinPosition(mPanMinX); mPanDynamicsX.setMaxPosition(mPanMaxX); mPanDynamicsY.setMinPosition(mPanMinY); mPanDynamicsY.setMaxPosition(mPanMaxY); mHandler.post(mUpdateRunnable); } /** * Stop fling animation */ public void stopFling() { mHandler.removeCallbacks(mUpdateRunnable); } /** * Help function to figure out max delta of pan from center position. * * @param zoomY Zoom value * @return Max delta of pan */ private double getMaxPanDelta(double zoomY) { return Math.max(0f, .5f * ((zoomY - 1) / zoomY)); } /** * Force zoom to stay within limits */ private void limitZoom() { if (mState.getZoom() < MIN_ZOOM) { mState.setZoom(MIN_ZOOM); } else if (mState.getZoom() > MAX_ZOOM) { mState.setZoom(MAX_ZOOM); } } /** * Update limit values for pan */ private void updatePanLimits() { final float aspectQuotient = mAspectQuotient.get(); final double zoomX = mState.getZoomX(aspectQuotient); final double zoomY = mState.getZoomY(aspectQuotient); mPanMinX = .5f - getMaxPanDelta(zoomX); mPanMaxX = .5f + getMaxPanDelta(zoomX); mPanMinY = .5f - getMaxPanDelta(zoomY); mPanMaxY = .5f + getMaxPanDelta(zoomY); } // Observable interface implementation @Override public void update(Observable observable, Object data) { limitZoom(); updatePanLimits(); } }