1. 程式人生 > >gallery相簿,拖動放大縮小

gallery相簿,拖動放大縮小

package com.cn.gallery;
import java.util.ArrayList;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.FloatMath;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnTouchListener;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.Gallery;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import com.cn.activity.R;
import com.cn.tools.MyActivity;
public class ActivityImageBrowse extends MyActivity implements OnTouchListener {
	// 螢幕寬度
	/*public static int screenWidth;
	// 螢幕高度
	public static int screenHeight;*/
	private MyGallery gallery;
	float beforeLenght = 0.0f; // 兩觸點距離
	float afterLenght = 0.0f; // 兩觸點距離
	boolean isScale = false;
	float currentScale = 1.0f;// 當前圖片的縮放比率
	ArrayList<String> photos;
	GalleryAdapter  galleryAdapter;
	private String code;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
		setContentView(R.layout.picturebrowse);
		
		//獲取意圖傳過來的引數
		Intent intent = this.getIntent();//啟用意圖
		
		try {
			photos = intent.getExtras().getStringArrayList("photos");
			code = intent.getExtras().getString("code");
			/*String images[] = new String[photos.size()];
			for(int i=0;i<photos.size();i++)
			{
				images[i] = photos.get(i);
			}*/
			gallery = (MyGallery) findViewById(R.id.gallery);
			gallery.setVerticalFadingEdgeEnabled(false);// 取消豎直漸變邊框
			gallery.setHorizontalFadingEdgeEnabled(false);// 取消水平漸變邊框
			galleryAdapter =  new GalleryAdapter(this,photos);
			gallery.setAdapter(galleryAdapter);
			gallery.setOnItemClickListener(new OnItemClickListener() {

				@Override
				public void onItemClick(AdapterView<?> arg0, View arg1,
						int position, long arg3) {
					// TODO Auto-generated method stub
					final int index = position;
					AlertDialog.Builder alert = new AlertDialog.Builder(ActivityImageBrowse.this);
					alert.setTitle("刪除圖片");
					alert.setMessage("是否要刪除該圖片?");
					alert.setPositiveButton("確定",new DialogInterface.OnClickListener() {
						
						@Override
						public void onClick(DialogInterface dialog, int which) {
							// TODO Auto-generated method stub
							photos.remove(index);
							galleryAdapter.setPhotos(photos);
							galleryAdapter.notifyDataSetChanged();//重新整理
							if(photos.size()<1)
							{
								finishImageBrowseActivity();//關閉Activity
							}
						}
					});
					alert.setNegativeButton("取消", null);
					alert.show();
					
				}
			});
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		/* gallery.setOnTouchListener(this);
		 gallery.setOnItemSelectedListener(new GalleryChangeListener());
		 FrameLayout.LayoutParams params=(FrameLayout.LayoutParams)
		 gallery.getLayoutParams();
		 params.height=400;
		 params.width=300;*/

		/*screenWidth = getWindow().getWindowManager().getDefaultDisplay().getWidth();
		screenHeight = getWindow().getWindowManager().getDefaultDisplay().getHeight();
*/
	}

	
	/*
	private class GalleryChangeListener implements OnItemSelectedListener {

		@Override
		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
			currentScale = 1.0f;
			isScale = false;
			beforeLenght = 0.0f;
			afterLenght = 0.0f;

		}

		@Override
		public void onNothingSelected(AdapterView<?> arg0) {
			// TODO Auto-generated method stub

		}

	}*/

	@Override
	public boolean onTouch(View v, MotionEvent event) {

		// Log.i("","touched---------------");
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_POINTER_DOWN:// 多點縮放
			beforeLenght = spacing(event);
			if (beforeLenght > 5f) {
				isScale = true;
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if (isScale) {
				afterLenght = spacing(event);
				if (afterLenght < 5f)
					break;
				float gapLenght = afterLenght - beforeLenght;
				if (gapLenght == 0) {
					break;
				} else if (Math.abs(gapLenght) > 5f) {
					// FrameLayout.LayoutParams params =
					// (FrameLayout.LayoutParams) gallery.getLayoutParams();
					float scaleRate = gapLenght / 854;// 縮放比例
					// Log.i("",
					// "scaleRate:"+scaleRate+" currentScale:"+currentScale);
					// Log.i("", "縮放比例:" +
					// scaleRate+" 當前圖片的縮放比例:"+currentScale);
					// params.height=(int)(800*(scaleRate+1));
					// params.width=(int)(480*(scaleRate+1));
					// params.height = 400;
					// params.width = 300;
					// gallery.getChildAt(0).setLayoutParams(new
					// Gallery.LayoutParams(300, 300));
					Animation myAnimation_Scale = new ScaleAnimation(currentScale, currentScale + scaleRate, currentScale, currentScale + scaleRate, Animation.RELATIVE_TO_SELF, 0.5f,
							Animation.RELATIVE_TO_SELF, 0.5f);
					// Animation myAnimation_Scale = new
					// ScaleAnimation(currentScale, 1+scaleRate, currentScale,
					// 1+scaleRate);
					myAnimation_Scale.setDuration(100);
					myAnimation_Scale.setFillAfter(true);
					myAnimation_Scale.setFillEnabled(true);
					// gallery.getChildAt(0).startAnimation(myAnimation_Scale);

					// gallery.startAnimation(myAnimation_Scale);
					currentScale = currentScale + scaleRate;
					// gallery.getSelectedView().setLayoutParams(new
					// Gallery.LayoutParams((int)(480), (int)(800)));
					// Log.i("",
					// "===========:::"+gallery.getSelectedView().getLayoutParams().height);
					// gallery.getSelectedView().getLayoutParams().height=(int)(800*(currentScale));
					// gallery.getSelectedView().getLayoutParams().width=(int)(480*(currentScale));
					gallery.getSelectedView().setLayoutParams(new Gallery.LayoutParams((int) (480 * (currentScale)), (int) (854 * (currentScale))));
					// gallery.getSelectedView().setLayoutParams(new
					// Gallery.LayoutParams((int)(320*(scaleRate+1)),
					// (int)(480*(scaleRate+1))));
					// gallery.getSelectedView().startAnimation(myAnimation_Scale);
					// isScale = false;
					beforeLenght = afterLenght;
				}
				return true;
			}
			break;
		case MotionEvent.ACTION_POINTER_UP:
			isScale = false;
			break;
		}

		return false;
	}

	/**
	 * 就算兩點間的距離
	 */
	private float spacing(MotionEvent event) {
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}
	
	
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		// TODO Auto-generated method stub
		
		if(keyCode == KeyEvent.KEYCODE_BACK)
		{
			finishImageBrowseActivity();
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}
	
	
	public void finishImageBrowseActivity()
	{
		Intent intent = new Intent();//資料使用Intent返回
		Bundle bundler = new Bundle();
		bundler.putString("code", code);
		bundler.putStringArrayList("result", photos);
		intent.putExtras(bundler);
		this.setResult(4,intent);
		this.finish();//關閉Activity
	}
	
}



/**  
 * GalleryAdapter.java
 * @version 1.0
 * @author Haven
 * @createTime 2011-12-9 下午05:04:34
 */
package com.cn.gallery;

import java.util.ArrayList;

import com.cn.tools.FileManager;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.Gallery;

public class GalleryAdapter extends BaseAdapter {

	private Context context;
	ArrayList<String> photos;
	
	
	public void setPhotos(ArrayList<String> photos) {
		this.photos = photos;
	}

	public GalleryAdapter(Context context,ArrayList<String> photos) {
		this.context = context;
		this.photos = photos;
	}

	@Override
	public int getCount() {
		return photos.size();
	}

	@Override
	public Object getItem(int position) {
		return null;
	}

	@Override
	public long getItemId(int position) {
		return 0;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		//Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), images[position]);
		MyImageView view = null;
		Bitmap bmp=null;
		try {
			String imagePath = FileManager.getApplicaionPath() + photos.get(position);
			bmp = BitmapFactory.decodeFile(imagePath);
			view = new MyImageView(context, bmp.getWidth(), bmp.getHeight());
			view.setLayoutParams(new Gallery.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
			view.setImageBitmap(bmp);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			if(bmp != null && !bmp.isRecycled()){
				bmp = null;//回收圖片所佔的記憶體 
			}
		}
		return view;
	}

}
/**  
 * MyGallery.java
 * @version 1.0
 * @author Haven
 * @createTime 2011-12-9 下午03:42:53
 * android.widget.Gallery的子函式。此類很重要。建議仔細看
 */
package com.cn.gallery;

import android.app.Activity;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.widget.Gallery;

public class MyGallery extends Gallery {
	private GestureDetector gestureScanner;
	private MyImageView imageView;
	public int screenWidth;
	public int screenHeight;
	public MyGallery(Context context) {
		super(context);
		screenWidth = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getWidth();
		screenHeight = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getHeight();
	}

	public MyGallery(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		screenWidth = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getWidth();
		screenHeight = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getHeight();
	}

	public MyGallery(Context context, AttributeSet attrs) {
		super(context, attrs);
		screenWidth = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getWidth();
		screenHeight = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getHeight();
		gestureScanner = new GestureDetector(new MySimpleGesture());
		this.setOnTouchListener(new OnTouchListener() {

			float baseValue;
			float originalScale;

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				View view = MyGallery.this.getSelectedView();
				if (view instanceof MyImageView) {
					imageView = (MyImageView) view;

					if (event.getAction() == MotionEvent.ACTION_DOWN) {
						baseValue = 0;
						originalScale = imageView.getScale();
					}
					if (event.getAction() == MotionEvent.ACTION_MOVE) {
						if (event.getPointerCount() == 2) {
							float x = event.getX(0) - event.getX(1);
							float y = event.getY(0) - event.getY(1);
							float value = (float) Math.sqrt(x * x + y * y);// 計算兩點的距離
							// System.out.println("value:" + value);
							if (baseValue == 0) {
								baseValue = value;
							} else {
								float scale = value / baseValue;// 當前兩點間的距離除以手指落下時兩點間的距離就是需要縮放的比例。
								// scale the image
								imageView.zoomTo(originalScale * scale, x + event.getX(1), y + event.getY(1));

							}
						}
					}
				}
				return false;
			}

		});
	}
	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
		View view = MyGallery.this.getSelectedView();
		if (view instanceof MyImageView) {
			imageView = (MyImageView) view;

			float v[] = new float[9];
			Matrix m = imageView.getImageMatrix();
			m.getValues(v);
			// 圖片實時的上下左右座標
			float left, right;
			// 圖片的實時寬,高
			float width, height;
			width = imageView.getScale() * imageView.getImageWidth();
			height = imageView.getScale() * imageView.getImageHeight();
			// 一下邏輯為移動圖片和滑動gallery換屏的邏輯。如果沒對整個框架了解的非常清晰,改動以下的程式碼前請三思!!!!!!
			if ((int) width <= screenWidth && (int) height <= screenHeight)// 如果圖片當前大小<螢幕大小,直接處理滑屏事件
			{
				super.onScroll(e1, e2, distanceX, distanceY);
			} else {
				left = v[Matrix.MTRANS_X];
				right = left + width;
				Rect r = new Rect();
				imageView.getGlobalVisibleRect(r);

				if (distanceX > 0)// 向左滑動
				{
					if (r.left > 0) {// 判斷當前ImageView是否顯示完全
						super.onScroll(e1, e2, distanceX, distanceY);
					} else if (right < screenWidth) {
						super.onScroll(e1, e2, distanceX, distanceY);
					} else {
						imageView.postTranslate(-distanceX, -distanceY);
					}
				} else if (distanceX < 0)// 向右滑動
				{
					if (r.right < screenWidth) {
						super.onScroll(e1, e2, distanceX, distanceY);
					} else if (left > 0) {
						super.onScroll(e1, e2, distanceX, distanceY);
					} else {
						imageView.postTranslate(-distanceX, -distanceY);
					}
				}

			}

		} else {
			super.onScroll(e1, e2, distanceX, distanceY);
		}
		return false;
	}
	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
		return false;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		gestureScanner.onTouchEvent(event);
		switch (event.getAction()) {
		case MotionEvent.ACTION_UP:
			// 判斷上下邊界是否越界
			View view = MyGallery.this.getSelectedView();
			if (view instanceof MyImageView) {
				imageView = (MyImageView) view;
				float width = imageView.getScale() * imageView.getImageWidth();
				float height = imageView.getScale() * imageView.getImageHeight();
				if ((int) width <= screenWidth && (int) height <= screenHeight)// 如果圖片當前大小<螢幕大小,判斷邊界
				{
					break;
				}
				float v[] = new float[9];
				Matrix m = imageView.getImageMatrix();
				m.getValues(v);
				float top = v[Matrix.MTRANS_Y];
				float bottom = top + height;
				if (top > 0) {
					imageView.postTranslateDur(-top, 200f);
				}
				Log.i("manga", "bottom:" + bottom);
				if (bottom < screenHeight) {
					imageView.postTranslateDur(screenHeight - bottom, 200f);
				}

			}
			break;
		}
		return super.onTouchEvent(event);
	}

	private class MySimpleGesture extends SimpleOnGestureListener {
		// 按兩下的第二下Touch down時觸發
		public boolean onDoubleTap(MotionEvent e) {
			View view = MyGallery.this.getSelectedView();
			if (view instanceof MyImageView) {
				imageView = (MyImageView) view;
				if (imageView.getScale() > imageView.getScaleRate()) {
					imageView.zoomTo(imageView.getScaleRate(), screenWidth / 2, screenHeight / 2, 200f);
					// imageView.layoutToCenter();
				} else {
					imageView.zoomTo(1.0f, screenWidth / 2, screenHeight / 2, 200f);
				}

			} else {

			}
			// return super.onDoubleTap(e);
			return true;
		}
	}
}
/**  
 * MyImageView.java
 * @version 1.0
 * @author Haven
 * @createTime 2011-12-9 下午03:12:30
 * 此類程式碼是根據android系統自帶的ImageViewTouchBase程式碼修改
 */
package com.cn.gallery;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.ImageView;

public class MyImageView extends ImageView {
	@SuppressWarnings("unused")
	private static final String TAG = "ImageViewTouchBase";
	public int screenWidth ;
	public int screenHeight ;
	// This is the base transformation which is used to show the image
	// initially. The current computation for this shows the image in
	// it's entirety, letterboxing as needed. One could choose to
	// show the image as cropped instead.
	//
	// This matrix is recomputed when we go from the thumbnail image to
	// the full size image.
	protected Matrix mBaseMatrix = new Matrix();

	// This is the supplementary transformation which reflects what
	// the user has done in terms of zooming and panning.
	//
	// This matrix remains the same when we go from the thumbnail image
	// to the full size image.
	protected Matrix mSuppMatrix = new Matrix();

	// This is the final matrix which is computed as the concatentation
	// of the base matrix and the supplementary matrix.
	private final Matrix mDisplayMatrix = new Matrix();

	// Temporary buffer used for getting the values out of a matrix.
	private final float[] mMatrixValues = new float[9];

	// The current bitmap being displayed.
	// protected final RotateBitmap mBitmapDisplayed = new RotateBitmap(null);
	protected Bitmap image = null;

	int mThisWidth = -1, mThisHeight = -1;

	float mMaxZoom = 2.0f;// 最大縮放比例
	float mMinZoom ;// 最小縮放比例

	private int imageWidth;// 圖片的原始寬度
	private int imageHeight;// 圖片的原始高度

	private float scaleRate;// 圖片適應螢幕的縮放比例

	public MyImageView(Context context, int imageWidth, int imageHeight) {
		super(context);
		this.imageHeight = imageHeight;
		this.imageWidth = imageWidth;
		init();
		screenWidth = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getWidth();
		screenHeight = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getHeight();

	}

	public MyImageView(Context context, AttributeSet attrs, int imageWidth, int imageHeight) {
		super(context, attrs);
		this.imageHeight = imageHeight;
		this.imageWidth = imageWidth;
		init();
		screenWidth = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getWidth();
		screenHeight = ((Activity) context).getWindow().getWindowManager().getDefaultDisplay().getHeight();

	}

	/**
	 * 計算圖片要適應螢幕需要縮放的比例
	 */
	private void arithScaleRate() {
		float scaleWidth = screenWidth / (float) imageWidth;
		float scaleHeight = screenHeight / (float) imageHeight;
		scaleRate = Math.min(scaleWidth, scaleHeight);
	}

	public float getScaleRate() {
		return scaleRate;
	}

	public int getImageWidth() {
		return imageWidth;
	}

	public void setImageWidth(int imageWidth) {
		this.imageWidth = imageWidth;
	}

	public int getImageHeight() {
		return imageHeight;
	}

	public void setImageHeight(int imageHeight) {
		this.imageHeight = imageHeight;
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
			event.startTracking();
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking() && !event.isCanceled()) {
			if (getScale() > 1.0f) {
				// If we're zoomed in, pressing Back jumps out to show the
				// entire image, otherwise Back returns the user to the gallery.
				zoomTo(1.0f);
				return true;
			}
		}
		return super.onKeyUp(keyCode, event);
	}

	protected Handler mHandler = new Handler();

	@Override
	public void setImageBitmap(Bitmap bitmap) {
		super.setImageBitmap(bitmap);
		image = bitmap;
		// 計算適應螢幕的比例
		arithScaleRate();
		//縮放到螢幕大小
		zoomTo(scaleRate,screenWidth / 2f, screenHeight / 2f);
		
		//居中
		layoutToCenter();
		
//		imageView.zoomTo(scaleRate, screenWidth / 2, screenHeight / 2
//		center(true, true);
	}

	// Center as much as possible in one or both axis. Centering is
	// defined as follows: if the image is scaled down below the
	// view's dimensions then center it (literally). If the image
	// is scaled larger than the view and is translated out of view
	// then translate it back into view (i.e. eliminate black bars).
	protected void center(boolean horizontal, boolean vertical) {
		// if (mBitmapDisplayed.getBitmap() == null) {
		// return;
		// }
		if (image == null) {
			return;
		}

		Matrix m = getImageViewMatrix();

		RectF rect = new RectF(0, 0, image.getWidth(), image.getHeight());
//		RectF rect = new RectF(0, 0, imageWidth*getScale(), imageHeight*getScale());

		m.mapRect(rect);

		float height = rect.height();
		float width = rect.width();

		float deltaX = 0, deltaY = 0;

		if (vertical) {
			int viewHeight = getHeight();
			if (height < viewHeight) {
				deltaY = (viewHeight - height) / 2 - rect.top;
			} else if (rect.top > 0) {
				deltaY = -rect.top;
			} else if (rect.bottom < viewHeight) {
				deltaY = getHeight() - rect.bottom;
			}
		}

		if (horizontal) {
			int viewWidth = getWidth();
			if (width < viewWidth) {
				deltaX = (viewWidth - width) / 2 - rect.left;
			} else if (rect.left > 0) {
				deltaX = -rect.left;
			} else if (rect.right < viewWidth) {
				deltaX = viewWidth - rect.right;
			}
		}

		postTranslate(deltaX, deltaY);
		setImageMatrix(getImageViewMatrix());
	}

	private void init() {
		setScaleType(ImageView.ScaleType.MATRIX);
	}
	
	/**
	 * 設定圖片居中顯示
	 */
	public void layoutToCenter()
	{
		//正在顯示的圖片實際寬高
		float width = imageWidth*getScale();
		float height = imageHeight*getScale();
		
		//空白區域寬高
		float fill_width = screenWidth - width;
		float fill_height = screenHeight - height;
		
		//需要移動的距離
		float tran_width = 0f;
		float tran_height = 0f;
		
		if(fill_width>0)
			tran_width = fill_width/2;
		if(fill_height>0)
			tran_height = fill_height/2;
			
		
		postTranslate(tran_width, tran_height);
		setImageMatrix(getImageViewMatrix());
	}

	protected float getValue(Matrix matrix, int whichValue) {
		matrix.getValues(mMatrixValues);
		mMinZoom =( screenWidth/2f)/imageWidth;
		
		return mMatrixValues[whichValue];
	}

	// Get the scale factor out of the matrix.
	protected float getScale(Matrix matrix) {
		return getValue(matrix, Matrix.MSCALE_X);
	}

	protected float getScale() {
		return getScale(mSuppMatrix);
	}

	// Combine the base matrix and the supp matrix to make the final matrix.
	protected Matrix getImageViewMatrix() {
		// The final matrix is computed as the concatentation of the base matrix
		// and the supplementary matrix.
		mDisplayMatrix.set(mBaseMatrix);
		mDisplayMatrix.postConcat(mSuppMatrix);
		return mDisplayMatrix;
	}

	static final float SCALE_RATE = 1.25F;

	// Sets the maximum zoom, which is a scale relative to the base matrix. It
	// is calculated to show the image at 400% zoom regardless of screen or
	// image orientation. If in the future we decode the full 3 megapixel image,
	// rather than the current 1024x768, this should be changed down to 200%.
	protected float maxZoom() {
		if (image == null) {
			return 1F;
		}

		float fw = (float) image.getWidth() / (float) mThisWidth;
		float fh = (float) image.getHeight() / (float) mThisHeight;
		float max = Math.max(fw, fh) * 4;
		return max;
	}

	protected void zoomTo(float scale, float centerX, float centerY) {
		if (scale > mMaxZoom) {
			scale = mMaxZoom;
		} else if (scale < mMinZoom) {
			scale = mMinZoom;
		}

		float oldScale = getScale();
		float deltaScale = scale / oldScale;

		mSuppMatrix.postScale(deltaScale, deltaScale, centerX, centerY);
		setImageMatrix(getImageViewMatrix());
		center(true, true);
	}

	protected void zoomTo(final float scale, final float centerX, final float centerY, final float durationMs) {
		final float incrementPerMs = (scale - getScale()) / durationMs;
		final float oldScale = getScale();
		final long startTime = System.currentTimeMillis();

		mHandler.post(new Runnable() {
			public void run() {
				long now = System.currentTimeMillis();
				float currentMs = Math.min(durationMs, now - startTime);
				float target = oldScale + (incrementPerMs * currentMs);
				zoomTo(target, centerX, centerY);
				if (currentMs < durationMs) {
					mHandler.post(this);
				}
			}
		});
	}
	

	protected void zoomTo(float scale) {
		float cx = getWidth() / 2F;
		float cy = getHeight() / 2F;

		zoomTo(scale, cx, cy);
	}

	protected void zoomToPoint(float scale, float pointX, float pointY) {
		float cx = getWidth() / 2F;
		float cy = getHeight() / 2F;

		panBy(cx - pointX, cy - pointY);
		zoomTo(scale, cx, cy);
	}

	protected void zoomIn() {
		zoomIn(SCALE_RATE);
	}

	protected void zoomOut() {
		zoomOut(SCALE_RATE);
	}

	protected void zoomIn(float rate) {
		if (getScale() >= mMaxZoom) {
			return; // Don't let the user zoom into the molecular level.
		} else if (getScale() <= mMinZoom) {
			return;
		}
		if (image == null) {
			return;
		}

		float cx = getWidth() / 2F;
		float cy = getHeight() / 2F;

		mSuppMatrix.postScale(rate, rate, cx, cy);
		setImageMatrix(getImageViewMatrix());
	}

	protected void zoomOut(float rate) {
		if (image == null) {
			return;
		}

		float cx = getWidth() / 2F;
		float cy = getHeight() / 2F;

		// Zoom out to at most 1x.
		Matrix tmp = new Matrix(mSuppMatrix);
		tmp.postScale(1F / rate, 1F / rate, cx, cy);

		if (getScale(tmp) < 1F) {
			mSuppMatrix.setScale(1F, 1F, cx, cy);
		} else {
			mSuppMatrix.postScale(1F / rate, 1F / rate, cx, cy);
		}
		setImageMatrix(getImageViewMatrix());
		center(true, true);
	}

	public void postTranslate(float dx, float dy) {
		mSuppMatrix.postTranslate(dx, dy);
		setImageMatrix(getImageViewMatrix());
	}
	float _dy=0.0f;
	protected void postTranslateDur( final float dy, final float durationMs) {
		_dy=0.0f;
		final float incrementPerMs = dy / durationMs;
		final long startTime = System.currentTimeMillis();
		mHandler.post(new Runnable() {
			public void run() {
				long now = System.currentTimeMillis();
				float currentMs = Math.min(durationMs, now - startTime);
				
				postTranslate(0, incrementPerMs*currentMs-_dy);
				_dy=incrementPerMs*currentMs;

				if (currentMs < durationMs) {
					mHandler.post(this);
				}
			}
		});
	}

	protected void panBy(float dx, float dy) {
		postTranslate(dx, dy);
		setImageMatrix(getImageViewMatrix());
	}
}