学习安卓两点触摸滑动缩放图片

1.布局文件如下main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" >
<!-- 引用自定义控件 --> <com.ymw.zoomimage.ZoomImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" > </com.ymw.zoomimage.ZoomImageView></LinearLayout>

2.自定义缩放图片控件ZoomImageView.java代码:

package com.ymw.zoomimage; import java.util.Observable; import java.util.Observer; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; public class ZoomImageView extends View implements Observer { /** Paint object used when drawing bitmap. */    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); /** Rectangle used (and re-used) for cropping source image. */    private final Rect mRectSrc = new Rect(); /** Rectangle used (and re-used) for specifying drawing area on canvas. */    private final Rect mRectDst = new Rect(); /** Object holding aspect quotient */    private final AspectQuotient mAspectQuotient = new AspectQuotient(); /** The bitmap that we're zooming in, and drawing on the screen. */    private Bitmap mBitmap; /** State of the zoom. */    private ZoomState mState; private BasicZoomControl mZoomControl; private BasicZoomListener mZoomListener; public ZoomImageView(Context context, AttributeSet attrs) { super(context, attrs); mZoomControl = new BasicZoomControl(); mZoomListener = new BasicZoomListener(); mZoomListener.setZoomControl(mZoomControl); setZoomState(mZoomControl.getZoomState()); setOnTouchListener(mZoomListener); mZoomControl.setAspectQuotient(getAspectQuotient()); } public void zoomImage(float f, float x, float y) { mZoomControl.zoom(f, x, y); } public void setImage(Bitmap bitmap) { mBitmap = bitmap; mAspectQuotient.updateAspectQuotient(getWidth(), getHeight(), mBitmap.getWidth(), mBitmap.getHeight()); mAspectQuotient.notifyObservers(); invalidate(); } private void setZoomState(ZoomState state) { if (mState != null) { mState.deleteObserver(this); } mState = state; mState.addObserver(this); invalidate(); } private AspectQuotient getAspectQuotient() { return mAspectQuotient; } @Override protected void onDraw(Canvas canvas) { if (mBitmap != null && mState != null) { Log.d("ZoomImageView", "OnDraw"); final float aspectQuotient = mAspectQuotient.get(); final int viewWidth = getWidth(); final int viewHeight = getHeight(); final int bitmapWidth = mBitmap.getWidth(); final int bitmapHeight = mBitmap.getHeight(); Log.d("ZoomImageView", "viewWidth = " + viewWidth); Log.d("ZoomImageView", "viewHeight = " + viewHeight); Log.d("ZoomImageView", "bitmapWidth = " + bitmapWidth); Log.d("ZoomImageView", "bitmapHeight = " + bitmapHeight); final float panX = mState.getPanX(); final float panY = mState.getPanY(); final float zoomX = mState.getZoomX(aspectQuotient) * viewWidth / bitmapWidth; final float zoomY = mState.getZoomY(aspectQuotient) * viewHeight / bitmapHeight; // Setup source and destination rectangles            mRectSrc.left = (int) (panX * bitmapWidth - viewWidth / (zoomX * 2)); mRectSrc.top = (int) (panY * bitmapHeight - viewHeight / (zoomY * 2)); mRectSrc.right = (int) (mRectSrc.left + viewWidth / zoomX); mRectSrc.bottom = (int) (mRectSrc.top + viewHeight / zoomY); // mRectDst.left = getLeft();            mRectDst.left = 0; mRectDst.top = 0; // mRectDst.right = getRight();            mRectDst.right = getWidth(); mRectDst.bottom = getHeight(); // Adjust source rectangle so that it fits within the source image.            if (mRectSrc.left < 0) { mRectDst.left += -mRectSrc.left * zoomX; mRectSrc.left = 0; } if (mRectSrc.right > bitmapWidth) { mRectDst.right -= (mRectSrc.right - bitmapWidth) * zoomX; mRectSrc.right = bitmapWidth; } if (mRectSrc.top < 0) { mRectDst.top += -mRectSrc.top * zoomY; mRectSrc.top = 0; } if (mRectSrc.bottom > bitmapHeight) { mRectDst.bottom -= (mRectSrc.bottom - bitmapHeight) * zoomY; mRectSrc.bottom = bitmapHeight; } mRectDst.left = 0; mRectDst.top = 0; mRectDst.right = viewWidth; mRectDst.bottom = viewHeight; Log.d("ZoomImageView", "mRectSrc.top" + mRectSrc.top); Log.d("ZoomImageView", "mRectSrc.bottom" + mRectSrc.bottom); Log.d("ZoomImageView", "mRectSrc.left" + mRectSrc.left); Log.d("ZoomImageView", "mRectSrc.right" + mRectSrc.right); Log.d("ZoomImageView", "mRectDst.top" + mRectDst.top); Log.d("ZoomImageView", "mRectDst.bottom" + mRectDst.bottom); Log.d("ZoomImageView", "mRectDst.left" + mRectDst.left); Log.d("ZoomImageView", "mRectDst.right" + mRectDst.right); canvas.drawBitmap(mBitmap, mRectSrc, mRectDst, mPaint); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mAspectQuotient.updateAspectQuotient(right - left, bottom - top, mBitmap.getWidth(), mBitmap.getHeight()); mAspectQuotient.notifyObservers(); } @Override public void update(Observable observable, Object data) { invalidate(); } private class BasicZoomListener implements View.OnTouchListener { /** Zoom control to manipulate */        private BasicZoomControl mZoomControl; private float mFirstX = -1; private float mFirstY = -1; private float mSecondX = -1; private float mSecondY = -1; private int mOldCounts = 0; /** * Sets the zoom control to manipulate * * @param control * Zoom control */        public void setZoomControl(BasicZoomControl control) { mZoomControl = control; } public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mOldCounts = 1; mFirstX = event.getX(); mFirstY = event.getY(); break; case MotionEvent.ACTION_MOVE: { float fFirstX = event.getX(); float fFirstY = event.getY(); int nCounts = event.getPointerCount(); if (1 == nCounts) { mOldCounts = 1; float dx = (fFirstX - mFirstX) / v.getWidth(); float dy = (fFirstY - mFirstY) / v.getHeight(); mZoomControl.pan(-dx, -dy); } else if (1 == mOldCounts) { mSecondX = event.getX(event.getPointerId(nCounts - 1)); mSecondY = event.getY(event.getPointerId(nCounts - 1)); mOldCounts = nCounts; } else { float fSecondX = event .getX(event.getPointerId(nCounts - 1)); float fSecondY = event .getY(event.getPointerId(nCounts - 1)); double nLengthOld = getLength(mFirstX, mFirstY, mSecondX, mSecondY); double nLengthNow = getLength(fFirstX, fFirstY, fSecondX, fSecondY); float d = (float) ((nLengthNow - nLengthOld) / v.getWidth()); mZoomControl.zoom((float) Math.pow(20, d), ((fFirstX + fSecondX) / 2 / v.getWidth()), ((fFirstY + fSecondY) / 2 / v.getHeight())); mSecondX = fSecondX; mSecondY = fSecondY; } mFirstX = fFirstX; mFirstY = fFirstY; break; } } return true; } private double getLength(float x1, float y1, float x2, float y2) { return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); } } private class BasicZoomControl implements Observer { /** Minimum zoom level limit */        private static final float MIN_ZOOM = 1; /** Maximum zoom level limit */        private static final float MAX_ZOOM = 16; /** Zoom state under control */        private final ZoomState mState = new ZoomState(); /** Object holding aspect quotient of view and content */        private AspectQuotient mAspectQuotient; /** * 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 f * Factor of zoom to apply * @param x * X-coordinate of invariant position * @param y * Y-coordinate of invariant position */        public void zoom(float f, float x, float y) { // Log.d("Zoom", "zoom f = " + f);            final float aspectQuotient = mAspectQuotient.get(); final float prevZoomX = mState.getZoomX(aspectQuotient); final float prevZoomY = mState.getZoomY(aspectQuotient); mState.setZoom(mState.getZoom() * f); limitZoom(); final float newZoomX = mState.getZoomX(aspectQuotient); final float 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)); limitPan(); 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(); mState.setPanX(mState.getPanX() + dx / mState.getZoomX(aspectQuotient)); mState.setPanY(mState.getPanY() + dy / mState.getZoomY(aspectQuotient)); limitPan(); mState.notifyObservers(); } /** * Help function to figure out max delta of pan from center position. * * @param zoom * Zoom value * @return Max delta of pan */        private float getMaxPanDelta(float zoom) { return Math.max(0f, .5f * ((zoom - 1) / zoom)); } /** * 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); } } /** * Force pan to stay within limits */        private void limitPan() { final float aspectQuotient = mAspectQuotient.get(); final float zoomX = mState.getZoomX(aspectQuotient); final float zoomY = mState.getZoomY(aspectQuotient); final float panMinX = .5f - getMaxPanDelta(zoomX); final float panMaxX = .5f + getMaxPanDelta(zoomX); final float panMinY = .5f - getMaxPanDelta(zoomY); final float panMaxY = .5f + getMaxPanDelta(zoomY); if (mState.getPanX() < panMinX) { mState.setPanX(panMinX); } if (mState.getPanX() > panMaxX) { mState.setPanX(panMaxX); } if (mState.getPanY() < panMinY) { mState.setPanY(panMinY); } if (mState.getPanY() > panMaxY) { mState.setPanY(panMaxY); } } // Observable interface implementation        public void update(Observable observable, Object data) { limitZoom(); limitPan(); } } private class AspectQuotient extends Observable { /** * Aspect quotient */        private float mAspectQuotient; // Public methods        /** * Gets aspect quotient * * @return The aspect quotient */        public float get() { return mAspectQuotient; } /** * Updates and recalculates aspect quotient based on supplied view and * content dimensions. * * @param viewWidth * Width of view * @param viewHeight * Height of view * @param contentWidth * Width of content * @param contentHeight * Height of content */        public void updateAspectQuotient(float viewWidth, float viewHeight, float contentWidth, float contentHeight) { final float aspectQuotient = (contentWidth / contentHeight) / (viewWidth / viewHeight); if (aspectQuotient != mAspectQuotient) { mAspectQuotient = aspectQuotient; setChanged(); } } } private class ZoomState extends Observable { /** * Zoom level A value of 1.0 means the content fits the view. */        private float mZoom; /** * Pan position x-coordinate X-coordinate of zoom window center * position, relative to the width of the content. */        private float mPanX; /** * Pan position y-coordinate Y-coordinate of zoom window center * position, relative to the height of the content. */        private float mPanY; // Public methods        /** * Get current x-pan * * @return current x-pan */        public float getPanX() { return mPanX; } /** * Get current y-pan * * @return Current y-pan */        public float getPanY() { return mPanY; } /** * Get current zoom value * * @return Current zoom value */        public float getZoom() { return mZoom; } /** * Help function for calculating current zoom value in x-dimension * * @param aspectQuotient * (Aspect ratio content) / (Aspect ratio view) * @return Current zoom value in x-dimension */        public float getZoomX(float aspectQuotient) { return Math.min(mZoom, mZoom * aspectQuotient); } /** * Help function for calculating current zoom value in y-dimension * * @param aspectQuotient * (Aspect ratio content) / (Aspect ratio view) * @return Current zoom value in y-dimension */        public float getZoomY(float aspectQuotient) { return Math.min(mZoom, mZoom / aspectQuotient); } /** * Set pan-x * * @param panX * Pan-x value to set */        public void setPanX(float panX) { if (panX != mPanX) { mPanX = panX; setChanged(); } } /** * Set pan-y * * @param panY * Pan-y value to set */        public void setPanY(float panY) { if (panY != mPanY) { mPanY = panY; setChanged(); } } /** * Set zoom * * @param zoom * Zoom value to set */        public void setZoom(float zoom) { if (zoom != mZoom) { mZoom = zoom; setChanged(); } } } }
View Code

3.工程主文件MainActivity.java代码:

package com.ymw.zoomimage; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; public class MainActivity extends Activity { private ZoomImageView zoomImg; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); zoomImg = (ZoomImageView) findViewById(R.id.image); Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.a); zoomImg.setImage(bitmap); } }

4.实例项目链接如下:

http://pan.baidu.com/s/1pJ7HWSj

更多相关文章

  1. Android 各种基础控件布局
  2. Android 控件之DatePicker,TimePicker,Calender
  3. Android基础之基本控件
  4. android 获取控件真实高度
  5. android LinearLayout布局子空间没有填充父控件的问题
  6. Button、选择框、日期、时间控件
  7. 关于Linearlayout中控件设置为其底部的问题,android:layout_grav
  8. 控件的间距为0
  9. 系出名门Android(8) - 控件(View)之TextSwitcher, Gallery, Imag

随机推荐

  1. 【Android】安卓环境变量配置
  2. EditText的使用体验
  3. Android(安卓)-- 使用OKhttp获取response
  4. Android系统文件目录结构
  5. Cocos2d-x微信登陆Demo
  6. java中采用Pull解析器对XML文件进行解析
  7. 2012-4-13更新:lwxshow站点博客持续更新
  8. Android(安卓)开发佳站
  9. android中UDP编程的注意事项
  10. Android远程连接SQL Server 2008数据库