package com.mooc.view;import android.content.Context;import android.graphics.Matrix;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.ScaleGestureDetector.OnScaleGestureListener;import android.view.View;import android.view.View.OnTouchListener;import android.view.ViewConfiguration;import android.view.ViewTreeObserver.OnGlobalLayoutListener;import android.widget.ImageView;/** *  * @author Administrator *  */public class ZoomImageView extends ImageView implements OnGlobalLayoutListener,OnScaleGestureListener, OnTouchListener {/** * 只进行一次的图片缩放处理 */private boolean mOnce;/** * 初始化时缩放的值 */private float mInitScale;/** * 双击放大达到的值 */private float mMidScale; // 缩放的值在mInitScale和mMaxScale之间/** * 放大的极限 */private float mMaxScale;/** * 控制缩放位移的矩阵 */private Matrix mScaleMatrix;// --------------------------------/** * 捕获用户多指触控时缩放的比例 */private ScaleGestureDetector mScaleGestureDetector;// -------------------->自由移动------------------/** * 记录上一次多点触控的数量 */private int mLastPointCount;// 最后触控的中心点位置private float mLastX;private float mLastY;/** * 一个值,用于判断是否是移动的 */private int mTouchSlop;/** * 是否可以移动 */private boolean isCanDrag;private boolean isCheckLeftAndRight;private boolean isCheckTopAndBottom;// ----------------------双击放大缩小--------private GestureDetector mGestureDetector;/** * 用于判断当前是否处于缩放状态。防止用户重复双击 */private boolean isAutoScale;public ZoomImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// initmScaleMatrix = new Matrix();setScaleType(ScaleType.MATRIX);mScaleGestureDetector = new ScaleGestureDetector(context, this);// 设置监听事件setOnTouchListener(this);mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();mGestureDetector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onDoubleTap(MotionEvent e) {if (isAutoScale) {return true;}// 点击的中心float x = e.getX();float y = e.getY();if (getScale() < mMidScale) {// mScaleMatrix.postScale(mMidScale / getScale(),// mMidScale / getScale(), x, y);// 放大到指定大小// setImageMatrix(mScaleMatrix);postDelayed(new AutoScaleRunnable(mMidScale, x, y),16);//isAutoScale = true;} else {// 缩小// mScaleMatrix.postScale(mInitScale / getScale(),// mInitScale / getScale(), x, y);// 放大到指定大小// setImageMatrix(mScaleMatrix);postDelayed(new AutoScaleRunnable(mInitScale, x, y), 16);//isAutoScale = true;}return true;}});}/** * 自动放大与缩小 *  * @author Zhang *  */private class AutoScaleRunnable implements Runnable {/** * 缩放的目标值 */private float mTargetScale;// 缩放的中心点private float x;private float y;/** * 缩放的梯度,每次1.07f 放大,0.93的缩小 */private final float BIGGER = 1.07f;private final float SMALL = 0.93f;private float temScale;// 临时public AutoScaleRunnable(float mTargetScale, float x, float y) {this.mTargetScale = mTargetScale;this.x = x;this.y = y;if (getScale() < mTargetScale) {temScale = BIGGER;// 代表还要放大}if (getScale() >mTargetScale) {temScale = SMALL;}}@Overridepublic void run() {// 进行缩放mScaleMatrix.postScale(temScale, temScale, x, y);checkBorderAndCenterWhenScale();// 检测setImageMatrix(mScaleMatrix);float currentScale = getScale();// 当temScale大于1.且当前的scale还小于目标值,可以放大,反之可以缩小if ((temScale > 1.0f && currentScale < mTargetScale)|| (temScale < 1.0f && currentScale > mTargetScale)) {postDelayed(this, 16);// 每16秒执行这个方法,肉眼看起来是顺畅的缩放动作} else {// 达到目标值,设置目标值float scale = mTargetScale / currentScale;mScaleMatrix.postScale(scale, scale, x, y);checkBorderAndCenterWhenScale();setImageMatrix(mScaleMatrix);// 结束isAutoScale = false;}}}public ZoomImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ZoomImageView(Context context) {this(context, null);}/** * 当onAttachedToWindow */@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();getViewTreeObserver().addOnGlobalLayoutListener(this);}/** * 当onDetachedFromWindow */@SuppressWarnings("deprecation")@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();getViewTreeObserver().removeGlobalOnLayoutListener(this);}/** * 全局的布局加载完成后,调用此方法 。 获取ImageView加载完成的图片,使图片居中缩放 */@Overridepublic void onGlobalLayout() {if (!mOnce) {// 得到图片的宽高int width = getWidth();int height = getHeight();// 得到图片,以及宽和高Drawable d = getDrawable();if (d == null)return;// 拉伸后的宽度.而不是真正图片的宽度int dw = d.getIntrinsicWidth();int dh = d.getIntrinsicHeight();Log.d("Debug", "width:" + width + ",height:" + height + ",dw:" + dw+ ",dh:" + dh);float scale = 1.0f;// 默认缩放的值// 将图片的宽高和控件的宽高作对比,如果图片比较小,则将图片放大,反之亦然。// 如果图片的宽度大于控件的宽度,并且图片高度小于控件高度if (dw > width && dh < height) {scale = width * 1.0f / dw;// 图片太宽,宽度缩放Log.d("Debug", "图片宽大,高小");}if (dh > height && dw < width) {scale = height * 1.0f / dh;// 图片太高,高度缩放Log.d("Debug", "图片高大,宽小");}// // 如果宽高都大于控件宽高??// if (dw > width && dh > height) {// scale = Math.min(width * 1.0f / dw, height * 1.0f / dw);// }// // 图片宽高都小于控件的宽高 上下两个一样// if (dw < width && dh < height) {// scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);// }if (dw < width && dh < height || dw > width && dh > height) {scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);Log.d("Debug", "图片宽高都大,或都小");}// 得到初始化缩放的比例mInitScale = scale;// 原大小mMaxScale = mInitScale * 4;// 4倍mMidScale = mInitScale * 2;// 2倍,双击放大达到的值// 将图片移动到控件的中心int dx = getWidth() / 2 - dw / 2;// 向x轴移动dx距离int dy = getHeight() / 2 - dh / 2;// 向y轴移动dx距离/** * matrix: xScale xSkew xTrans 需要9个 ySkew yScale yTrans 0 0 0 */mScaleMatrix.postTranslate(dx, dy);// 平移mScaleMatrix.postScale(mInitScale, mInitScale, width / 2,height / 2);// 缩放,正常显示width/2,height/2中心setImageMatrix(mScaleMatrix);mOnce = true;}}/** * 得到当前缩图片放值 *  * @return */public float getScale() {float[] values = new float[9];mScaleMatrix.getValues(values);return values[Matrix.MSCALE_X];}// 缩放----------------------------------// 缩放的区间,initScale 和maxScale之间@Overridepublic boolean onScale(ScaleGestureDetector detector) {float scale = getScale();// 得到缩放的值// getScaleFactor()这个方法很重要,它的含义是根据你的手势缩放程度预期得到的图片大小和当前图片大小的一个比值,// 当达到最大或最小值时让缩放的量为1就行,按老师那样的计算,在缩放到最大值或最小值后,有可能出现不能再缩放的情况float scaleFactor = detector.getScaleFactor();// 返回从前一个伸缩事件至当前伸缩事件的伸缩比率,if (getDrawable() == null) {return true;}// Log.d("Debug", "scaleFactor--> " + scaleFactor + ".");// 缩放范围的控制// 当前缩放在缩放极限内,缩放一因子大于1,说明想放大,小于1说明想缩小if ((scale < mMaxScale && scaleFactor > 1.0f)|| (scale > mInitScale && scaleFactor < 1.0f)) {// 不能小于最小值if (scale * scaleFactor < mInitScale) {// 小于的话重置scaleFactor = mInitScale / scale;// scale=mInitScale/scaleFactory}if (scale * scaleFactor > mMaxScale) {// 大于也充值scaleFactor = mMaxScale / scale;// scale当前缩放}// 缩放,大的或太小的复原/* * scale 表示的是当前图片基于原图放大的比例 mScaleMatix.postScale(scaleFactor, * scaleFactor, getWidth() / 2,getHeight() / * 2);中的scaleFactor参数表示基于当前已放大的图片再放大scaleFactor倍 * 。所以图片的实际放大的大小是原图的scaleFactor * *scale倍。也就是说,当前方法postScale传入的参数是scaleFactor * ,则图片实际基于原图放大的倍数是scaleFactor*scale 等式scaleFactor=mInitScale/scale * 可以推导出---》 scaleFactor*scale = mInitScale -----》 此时的scaleFactor * 作为postScale的参数,实际图片的缩放大小就是mInitScale */mScaleMatrix.postScale(scaleFactor, scaleFactor,detector.getFocusX(), detector.getFocusY());// detector.getFocusX(),// detector.getFocusY()获得触摸的中心点,这样放大是可以按照触摸的中心放大,缩小就会有问题,出现图片显示偏移。// 不断的检测,checkBorderAndCenterWhenScale();setImageMatrix(mScaleMatrix);}return true;}/** * 要拿到图片放大缩小以后的四角的坐标,以及宽高 *  * @return */private RectF getMatrixRectF() {Matrix matrix = mScaleMatrix;RectF rectF = new RectF();Drawable d = getDrawable();// 得到图片放大或缩小以后的宽和高if (d != null) {rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());matrix.mapRect(rectF);}return rectF;}/** * 在缩放的时候进行边界控制,以及我们的位置的控制 */private void checkBorderAndCenterWhenScale() {RectF rect = getMatrixRectF();// 要移动的偏移距离。float deltaX = 0;float deltaY = 0;int width = getWidth();int height = getHeight();// 缩放时,进行边界检测,防止出现白边。if (rect.width() >= width) {// rect.width()放大或缩小后的宽度// 放大缩小后的图片和屏幕左边的空隙。if (rect.left > 0) {// 加入图片没有完全在屏幕中,而是往右边移动了一小段距离,rect.left:估计是个坐标// Log.d("Debug", "rect.left的偏移距离是:" + rect.left);deltaX = -(rect.left);}// 右边里屏幕有空隙if (rect.right < width) {deltaX = width - rect.right;// 需要向右边移动// Log.d("Debug", " X轴rect.right < width");}}if (rect.height() >= height) {if (rect.top > 0) {deltaY = -rect.top;// Log.d("Debug", " Y轴rect.height() >= height");}if (rect.bottom < height) {deltaY = height - rect.bottom;// Log.d("Debug", " Y轴rect.bottom < height");}}// 如果宽度或高度小于控件的宽度或高度,则让其居中if (rect.width() < width) {deltaX = width / 2f - rect.right + rect.width() / 2f;// x轴移动的距离。移到中心点// Log.d("Debug", " 宽度小于控件的宽度rect.right < width");}if (rect.height() < height) {deltaY = height / 2f - rect.bottom + rect.height() / 2f;// Log.d("Debug", " 高度小于控件的高度rect.height() < height");}mScaleMatrix.postTranslate(deltaX, deltaY);}@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {return true;// 得return true;}@Overridepublic void onScaleEnd(ScaleGestureDetector detector) {}@Overridepublic boolean onTouch(View v, MotionEvent event) {if (mGestureDetector.onTouchEvent(event)) {return true;}mScaleGestureDetector.onTouchEvent(event);// 给mScaleGestureDetector处理// 多点触控的中心点float x = 0;float y = 0;// 拿到多点触控的数量int pointerCount = event.getPointerCount();for (int i = 0; i < pointerCount; i++) {// 拿到所有点的x,y轴的值,除以pointerCount,得到中心x += event.getX(i);y += event.getY(i);}x /= pointerCount;y /= pointerCount;// 如果最后触控点数量,,一开始是不相等的if (mLastPointCount != pointerCount) {isCanDrag = false;mLastX = x;mLastY = y;}mLastPointCount = pointerCount;switch (event.getAction()) {case MotionEvent.ACTION_MOVE:// 记录偏移量float dx = x - mLastX;float dy = y - mLastY;if (!isCanDrag) {isCanDrag = isMoveAction(dx, dy);}// 判断是否可以移动if (isCanDrag) {RectF rectF = getMatrixRectF();// 得到缩放后的图片的宽高if (getDrawable() != null) {// 默认可以全部检测isCheckLeftAndRight = isCheckTopAndBottom = true;// 如果图片小于控件的宽度if (rectF.width() < getWidth()) {isCheckLeftAndRight = false;dx = 0;// 不允许横向移动}// 如果图片的高度小于控件的高度if (rectF.height() < getHeight()) {isCheckTopAndBottom = false;// 不用检测,不移动的时候dy = 0;// 不允许纵向移动}mScaleMatrix.postTranslate(dx, dy);checkBorderWhenTranslate();// 判断移动时,是否到边界setImageMatrix(mScaleMatrix);// 移动}}// 记录mLastX = x;mLastY = y;break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:mLastPointCount = 0;break;}return true;}/** * 当移动时,进行边界检查 */private void checkBorderWhenTranslate() {RectF rectF = getMatrixRectF();float deltaX = 0;float deltaY = 0;int width = getWidth();int height = getHeight();if (rectF.top > 0 && isCheckTopAndBottom) {Log.d("Debug", " top大于0:rectF.top=" + rectF.top);deltaY = -rectF.top;}if (rectF.bottom < height && isCheckTopAndBottom) {deltaY = height - rectF.bottom;Log.d("Debug", " bottom小于高度,rectF.bottom=" + rectF.bottom);}if (rectF.left > 0 && isCheckLeftAndRight) {deltaX = -rectF.left;Log.d("Debug", " left大于0,rectF.left=" + rectF.left);}if (rectF.right < width && isCheckLeftAndRight) {deltaX = width - rectF.right;Log.d("Debug", " 右边小于宽度,rectF.right=" + rectF.right);}mScaleMatrix.postTranslate(deltaX, deltaY);}/** * 判断是否足矣触发移动 *  * @param dx * @param dy * @return */private boolean isMoveAction(float dx, float dy) {return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;// 对角线的长度}}


更多相关文章

  1. Android 自定义dialog 设置宽度
  2. Android – 加载图片本缓存到内存与本地
  3. Android圆形图片--自定义控件
  4. Android APIDemo gif图片显示
  5. android 图片合成
  6. Android通过手势实现的缩放处理
  7. Android中Gallery来显示图片
  8. Android图片按比例缩放

随机推荐

  1. [Android] 基于 Linux 命令行构建 Androi
  2. Android实现全屏显示的方法
  3. Android多进程之Binder解绑监听的问题
  4. android layout属性介绍
  5. Android中资源文件的Shape使用总结
  6. 理解 Android(安卓)Build 系统
  7. Android(安卓)GWES之Android窗口管理
  8. 二、android百度地图定位显示当前位置(an
  9. Android环境变量配置
  10. Android中gravity与layout_gravity的区别