package com.crpcg.domain_hchain.view;import android.annotation.SuppressLint;import android.content.Context;import android.graphics.Matrix;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.os.Build;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;import android.widget.ImageView;/** * */public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener,      ScaleGestureDetector.OnScaleGestureListener,View.OnTouchListener{   @SuppressWarnings("unused")   private static final String TAG = "ZoomImageView";   /**    * 最大放大倍数    */   public static final float mMaxScale = 4.0f;   /**    * 默认缩放    */   private float mInitScale = 1.0f;   /**    * 双击放大比例    */   private float mMidScale=2.0f;   /**    * 检测缩放手势 多点触控手势识别 独立的类不是GestureDetector的子类    */   ScaleGestureDetector mScaleGestureDetector = null;//检测缩放的手势   /**    *检测类似长按啊 轻按啊 拖动 快速滑动 双击啊等等 OnTouch方法虽然也可以    * 但是对于一些复杂的手势需求自己去通过轨迹时间等等判断很复杂,因此我们采用系统    * 提供的手势类进行处理    */   private GestureDetector mGestureDetector;   /**    * 如果正在缩放中就不向下执行,防止多次双击    */   private boolean mIsAutoScaling;   /**    * Matrix的对图像的处理    * Translate 平移变换    * Rotate 旋转变换    * Scale 缩放变换    * Skew 错切变换    */   Matrix mScaleMatrix = new Matrix();   /**    * 处理矩阵的9个值    */   float[] mMartixValue = new float[9];   public ZoomImageView(Context context) {      this(context, null);   }   public ZoomImageView(Context context, AttributeSet attrs) {      this(context, attrs, 0);   }   public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) {      super(context, attrs, defStyleAttr);      setScaleType(ScaleType.MATRIX);      mScaleGestureDetector = new ScaleGestureDetector(context, this);      this.setOnTouchListener(this); //缩放的捕获要建立在setOnTouchListener      //符合滑动的距离 它获得的是触发移动事件的最短距离,如果小于这个距离就不触发移动控件,      //viewpager就是用这个距离来判断用户是否翻页      mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();      //监听双击事件 SimpleOnGestureListenerOnGestureListener接口实现类,      //使用这个复写需要的方法就可以不用复写所有的方法      mGestureDetector = new GestureDetector(context,            new   GestureDetector.SimpleOnGestureListener() {               @Override               public boolean onDoubleTap(MotionEvent e) {                  //如果正在缩放中就不向下执行,防止多次双击                  if (mIsAutoScaling) {                     return true;                  }                  //缩放的中心点                  float x = e.getX();                  float y = e.getY();                  //如果当前缩放值小于这个临界值 则进行放大                  if (getScale() < mMidScale) {                     mIsAutoScaling = true;                     //view中的方法 已x,y为坐标点放大到mMidScale 延时10ms                     postDelayed(new AutoScaleRunble(mMidScale, x, y), 16);                  } else {                     //如果当前缩放值大于这个临界值 则进行缩小操作 缩小到mInitScale                     mIsAutoScaling = true;                     postDelayed(new AutoScaleRunble(mInitScale, x, y), 16);                  }                  return true;               }            });   }   @Override   protected void onAttachedToWindow() {      super.onAttachedToWindow();      getViewTreeObserver().addOnGlobalLayoutListener(this);   }   //suppress deprecate warning because i have dealt with it   @Override   @SuppressWarnings("deprecation")   protected void onDetachedFromWindow() {      super.onDetachedFromWindow();      if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {         getViewTreeObserver().removeOnGlobalLayoutListener(this);      }      getViewTreeObserver().removeGlobalOnLayoutListener(this);   }   //--------------------------implement OnTouchListener----------------------------/   /**    *处理现图片放大后移动查看    */   private int mLastPointCount;//触摸点发生移动时的触摸点个数   private boolean isCanDrag;//判断是否可以拖拽   private float mLatX;//记录移动之前按下去的那个坐标点   private float mLastY;   private int mTouchSlop;//系统默认触发移动事件的最短距离   private boolean isCheckTopAndBottom;//是否可以上下拖动   private boolean isCheckLeftAndRight;//是否可以左右拖动   @Override   public boolean onTouch(View v, MotionEvent event) {      //双击事件进行关联      if (mGestureDetector.onTouchEvent(event)) {         //如果是双击的话就直接不向下执行了         return true;      }      //将事件传递给ScaleGestureDetector      mScaleGestureDetector.onTouchEvent(event);      float x = 0;      float y = 0;      //可能出现多手指触摸的情况 ACTION_DOWN事件只能执行一次所以多点触控不能在down事件里面处理      int pointerCount = event.getPointerCount();      for (int i = 0; i < pointerCount; i++) {         x += event.getX(i);         y += event.getY(i);      }      //取平均值,得到的就是多点触控后产生的那个点的坐标      x /= pointerCount;      y /= pointerCount;      //每当触摸点发生移动时(从静止到移动),重置mLasX , mLastY mLastPointCount防止再次进入      if (mLastPointCount != pointerCount) {         //这里加一个参数并且设置成false的目的是,要判断位移的距离是否符合触发移动事件的最短距离         isCanDrag = false;         //记录移动之前按下去的那个坐标点,记录的值类似于断点续移,下次移动的时候从这个点开始         mLatX = x;         mLastY = y;      }      //重新赋值 说明如果是一些列连续滑动的操作就不会再次进入上面的判断 否则会重新确定坐标移动原点      mLastPointCount = pointerCount;      RectF rectF = getMatrixRectF();      switch (event.getAction()) {         case MotionEvent.ACTION_DOWN:            //按下的时候如果发现图片缩放宽或者高大于屏幕宽高则请求viewpager不拦截事件交给ZoomImageView处理            //ZoomImageView可以进行缩放操作            if (rectF.width() > getWidth() || rectF.height() > getHeight())            {               getParent().requestDisallowInterceptTouchEvent(true);            }            break;         case MotionEvent.ACTION_MOVE:            //按下的时候如果发现图片缩放宽或者高大于屏幕宽高则请求viewpager不拦截事件交给ZoomImageView处理            //ZoomImageView可以进行缩放操作            if (rectF.width() > getWidth() || rectF.height() > getHeight())            {               getParent().requestDisallowInterceptTouchEvent(true);            }            //x,y移动的距离            float dx = x - mLatX;            float dy = y - mLastY;            //如果是不能拖拽,可能是因为手指变化,这时就去重新检测看看是不是符合滑动            if (!isCanDrag) {               //反正是根据勾股定理,调用系统API               isCanDrag = isMoveAction(dx, dy);               Log.e(TAG, "移动3---->" + pointerCount);            }            if (isCanDrag) {               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);                  //解决拖拽的时候左右 上下都会出现留白的情况                  checkBorderAndCenterWhenTranslate();                  setImageMatrix(mScaleMatrix);               }            }            mLatX = x;//记录的值类似于断点续移,下次移动的时候从这个点开始            mLastY = y;            break;         case MotionEvent.ACTION_UP:         case MotionEvent.ACTION_CANCEL:            mLastPointCount = 0;//抬起或者取消事件时候把这个置空            break;      }      return true;   }   //----------------------手势implement OnScaleGestureListener------------------------//   /**    *处理图片缩放    */   @Override   public boolean onScale(ScaleGestureDetector detector) {      float scale = getScale();//当前相对于初始尺寸的缩放(之前matrix中获得)      Log.e(TAG, "matrix scale---->" + scale);      float scaleFactor = detector.getScaleFactor();//这个时刻缩放的/当前缩放尺度 (现在手势获取)      Log.e(TAG, "scaleFactor---->" + scaleFactor);      if (getDrawable() == null)         return true;      if ((scale < mMaxScale && scaleFactor > 1.0f) //放大            || (scale > mInitScale && scaleFactor < 1.0f)) {   //缩小         //如果要缩放的值比初始化还要小的话,就按照最小可以缩放的值进行缩放         if (scaleFactor * scale < mInitScale){            scaleFactor = mInitScale / scale;            Log.e(TAG, "进来了1" + scaleFactor);         }         ///如果要缩放的值比最大缩放值还要大,就按照最大可以缩放的值进行缩放         if (scaleFactor * scale > mMaxScale){            scaleFactor = mMaxScale / scale;            Log.e(TAG, "进来了2---->" + scaleFactor);         }         Log.e(TAG, "scaleFactor2---->" + scaleFactor);         //设置缩放比例         mScaleMatrix.postScale(scaleFactor, scaleFactor,               detector.getFocusX(), detector.getFocusY());//缩放中心是两手指之间         checkBorderAndCenterWhenScale();//解决这种缩放导致缩放到最小时图片位置可能发生了变化//            mScaleMatrix.postScale(scaleFactor, scaleFactor,//                    getWidth() / 2, getHeight() / 2);//缩放中心是屏幕中心点         setImageMatrix(mScaleMatrix);//通过手势给图片设置缩放      }      //返回值代表本次缩放事件是否已被处理。如果已被处理,那么detector就会重置缩放事件;      // 如果未被处理,detector会继续进行计算,修改getScaleFactor()的返回值,直到被处理为止。      // 因此,它常用在判断只有缩放值达到一定数值时才进行缩放      return true;   }   @Override   public boolean onScaleBegin(ScaleGestureDetector detector) {      //缩放开始一定要返回truedetector是否处理后继的缩放事件。返回false时,不会执行onScale()      return true;   }   @Override   public void onScaleEnd(ScaleGestureDetector detector) {      //缩放结束时   }   boolean once = true;   /**    *图片初始化其大小 必须在onAttachedToWindow方法后才能获取宽高    */   @Override   public void onGlobalLayout() {      if (!once)         return;      Drawable d = getDrawable();      if (d == null)         return;      //获取imageview宽高      int width = getWidth();      int height = getHeight();      //获取图片宽高      int imgWidth = d.getIntrinsicWidth();      int imgHeight = d.getIntrinsicHeight();      float scale = 1.0f;      //如果图片的宽或高大于屏幕,缩放至屏幕的宽或者高      if (imgWidth > width && imgHeight <= height)         scale = (float) width / imgWidth;      if (imgHeight > height && imgWidth <= width)         scale = (float) height / imgHeight;      //如果图片宽高都大于屏幕,按比例缩小      if (imgWidth > width && imgHeight > height)         scale = Math.min((float) imgWidth / width, (float) imgHeight / height);      mInitScale = scale;      //将图片移动至屏幕中心      mScaleMatrix.postTranslate((width - imgWidth) / 2, (height - imgHeight) / 2);      mScaleMatrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);      setImageMatrix(mScaleMatrix);      once = false;   }   /**    * 获取当前缩放比例    */   public float getScale() {      //Matrix为一个3*3的矩阵,一共9个值,复制到这个数组当中      mScaleMatrix.getValues(mMartixValue);      return mMartixValue[Matrix.MSCALE_X];//取出图片宽度的缩放比例   }   /**    * 在缩放时,解决上下左右留白的情况    */   private void checkBorderAndCenterWhenScale() {      RectF rectF = getMatrixRectF();      float deltaX = 0;      float deltaY = 0;      int width = getWidth();      int height = getHeight();      // 如果宽或高大于屏幕,则控制范围      if (rectF.width() >= width) {         if (rectF.left > 0) {            deltaX = -rectF.left;//获取坐标留白的距离            Log.e(TAG, "宽有问题1---->" +rectF.width()+"--"+rectF.left+"--"+width);         }         if (rectF.right < width) {            //屏幕宽-屏幕已经占据的大小 得到右边留白的宽度            deltaX = width - rectF.right;            Log.e(TAG, "宽有问题2---->" +rectF.width()+"--"+rectF.left+"--"+width);         }      }      if (rectF.height() >= height) {         if (rectF.top > 0) {            deltaY = -rectF.top;//同上,获取上面留白的距离         }         if (rectF.bottom < height) {   //同上 获取下面留白的距离            deltaY = height - rectF.bottom;         }      }      // 如果宽或高小于屏幕,则让其居中      if (rectF.width() < width) {         //图片的中心点距离屏幕的中心点距离计算(画个图很明了)         deltaX = width * 0.5f - rectF.right + 0.5f * rectF.width();         Log.e(TAG, "宽有问题3---->" +rectF.width()+"--"+rectF.right+"结果"+deltaX);      }      if (rectF.height() < height) {         deltaY = height * 0.5f - rectF.bottom + 0.5f * rectF.height();         Log.e(TAG, "高有问题4---->" +rectF.height()+"--"+rectF.bottom+"结果"+deltaY);      }      mScaleMatrix.postTranslate(deltaX, deltaY);   }   /**    * 获得图片放大缩小以后的宽和高,以及l,r,t,b    */   private RectF getMatrixRectF() {      Matrix rMatrix = mScaleMatrix;//获得当前图片的矩阵      RectF rectF = new RectF();//创建一个空矩形      Drawable d = getDrawable();      if (d != null) {         //使这个矩形的宽和高同当前图片一致         //设置坐标位置(lr是左边矩形的坐标点 tb是右边矩形的坐标点 lr设置为0就是设置为原宽高)         rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());         //将矩阵映射到矩形上面,之后我们可以通过获取到矩阵的上下左右坐标以及宽高         //来得到缩放后图片的上下左右坐标和宽高         rMatrix.mapRect(rectF);//把坐标位置放入矩阵      }      return rectF;   }   /**    *判断是否可以拖动    */   private boolean isMoveAction(float dx, float dy) {      return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;   }   /**    * 放大移动的过程中解决上下左右留白的情况    */   private void checkBorderAndCenterWhenTranslate() {      RectF rectF = getMatrixRectF();      float deltax = 0;      float deltay = 0;      int width = getWidth();      int height = getHeight();      //可以上下拖动且距离屏幕上方留白 根据Android系统坐标系往上移动的值要取负值      if (rectF.top > 0 && isCheckTopAndBottom) {         deltay = -rectF.top;         Log.e(TAG, "上面留白距离---->" +rectF.top);      }      //可以上下拖动且距离屏幕底部留白 根据Android系统坐标系往下移动的值要取正值      if (rectF.bottom < height && isCheckTopAndBottom) {         deltay = height - rectF.bottom;         Log.e(TAG, "下面留白距离---->" +rectF.bottom);      }      //可以左右拖动且左边留白 根据Android系统坐标系往左移动的值要取负值      if (rectF.left > 0 && isCheckLeftAndRight) {         deltax = -rectF.left;         Log.e(TAG, "左边留白距离---->" +rectF.left);      }      //可以左右拖动且右边留白 根据Android系统坐标系往右移动的值要取正值      if (rectF.right < width && isCheckLeftAndRight) {         deltax = width - rectF.right;         Log.e(TAG, "右边留白距离---->" +rectF.right);      }      mScaleMatrix.postTranslate(deltax, deltay);//处理偏移量   }   /**    * View.postDelay()方法延时执行双击放大缩小 在主线程中运行 没隔16ms给用户产生过渡的效果的    */   private class AutoScaleRunble implements Runnable {      private float mTrgetScale;//缩放目标值      private float x;//缩放中心点      private float y;      private float tempScale;//可能是BIGGER可能是SMALLER      private float BIGGER = 1.07f;      private float SMALLER = 0.93f;      //构造传入缩放目标值,缩放的中心点      public AutoScaleRunble(float mTrgetScale, float x, float y) {         this.mTrgetScale = mTrgetScale;         this.x = x;         this.y = y;         if (getScale() < mTrgetScale) {   //双击放大            //这个缩放比1f大就行 随便取个1.07            tempScale = BIGGER;         }         if (getScale() > mTrgetScale) {   //双击缩小            //这个缩放比1f小就行 随便取个0.93            tempScale = SMALLER;         }      }      @Override      public void run() {         //执行缩放         mScaleMatrix.postScale(tempScale, tempScale, x, y);         //在缩放时,解决上下左右留白的情况         checkBorderAndCenterWhenScale();         setImageMatrix(mScaleMatrix);         //获取当前的缩放值         float currentScale = getScale();         //如果当前正在放大操作并且当前的放大尺度小于缩放的目标值,或者正在缩小并且缩小的尺度大于目标值         //则再次延时16ms递归调用直到缩放到目标值         if ((tempScale > 1.0f && currentScale < mTrgetScale) || (tempScale <               1.0f && currentScale > mTrgetScale)) {            postDelayed(this, 16);         } else {            //代码走到这儿来说明不能再进行缩放了,可能放大的尺寸超过了mTrgetScale            //也可能缩小的尺寸小于mTrgetScale            //所以这里我们mTrgetScale / currentScale 用目标缩放尺寸除以当前的缩放尺寸            //得到缩放比,重新执行缩放到            //mMidScale或者mInitScale            float scale = mTrgetScale / currentScale;            mScaleMatrix.postScale(scale, scale, x, y);            checkBorderAndCenterWhenScale();            setImageMatrix(mScaleMatrix);            //执行完成后重置            mIsAutoScaling = false;         }      }   }}

更多相关文章

  1. Android图片的处理类
  2. Android(安卓)支持多屏幕机制
  3. 【android 其他】转载:Android(安卓)简史
  4. 【Android源码分享】 android 控件的移动
  5. 使用navigator.userAgent.toLowerCase()判断移动端类型
  6. android:layout_marginStart和android:paddingStart的区别
  7. Android实现TextView动画缩放
  8. 使用navigator.userAgent.toLowerCase()判断移动端类型
  9. Android(安卓)Bitmap移动游戏背景

随机推荐

  1. [置顶] Android(安卓)从硬件到应用:一步一
  2. Android(安卓)开发中常用英语词句
  3. 深入浅出Android(安卓)App耗电量统计
  4. 【Android(安卓)4.0】Android(安卓)Icon
  5. Android(安卓)Ubuntu平台下ADB驱动的安装
  6. 如何运行/调试你的PHP代码
  7. php中.=什么意思
  8. THINKPHP6 多应用隐藏入口文件
  9. php实现记事本案例
  10. Hexo博客框架—轻量、一令部署