time 
设为首页】【收藏本站
当前位置: 主页 > 电脑网络 > 操作系统 > 嵌入式 > Android > Android实现自由地对图片进行缩放和移动的多点触控

Android实现自由地对图片进行缩放和移动的多点触控

时间:2014-01-26 13:42 点击:3432次 字体:[ ]




 

上一篇文章中我带着大家一起实现了Android瀑布流照片墙的效果,虽然这种效果很炫很酷,但其实还只能算是一个半成品,因为照片墙中所有的图片都是只能看不能点的。因此本篇文章中,我们就来对这一功能进行完善,加入点击图片就能浏览大图的功能,并且在浏览大图的时候还可以通过多点触控的方式对图片进行缩放。

如果你还没有看过 http://www.fengfly.com/plus/view-214550-1.html 这篇文章,请尽量先去阅读完再来看本篇文章,因为这次的代码完全是在上次的基础上进行开发的。

那我们现在就开始动手吧,首先打开上次的PhotoWallFallsDemo项目,在里面加入一个ZoomImageView类,这个类就是用于进行大图展示和多点触控缩放的,代码如下所示:

  1. public class ZoomImageView extends View { 
  2.  
  3.     /** 
  4.      * 初始化状态常量 
  5.      */ 
  6.     public static final int STATUS_INIT = 1
  7.  
  8.     /** 
  9.      * 图片放大状态常量 
  10.      */ 
  11.     public static final int STATUS_ZOOM_OUT = 2
  12.  
  13.     /** 
  14.      * 图片缩小状态常量 
  15.      */ 
  16.     public static final int STATUS_ZOOM_IN = 3
  17.  
  18.     /** 
  19.      * 图片拖动状态常量 
  20.      */ 
  21.     public static final int STATUS_MOVE = 4
  22.  
  23.     /** 
  24.      * 用于对图片进行移动和缩放变换的矩阵 
  25.      */ 
  26.     private Matrix matrix = new Matrix(); 
  27.  
  28.     /** 
  29.      * 待展示的Bitmap对象 
  30.      */ 
  31.     private Bitmap sourceBitmap; 
  32.  
  33.     /** 
  34.      * 记录当前操作的状态,可选值为STATUS_INIT、STATUS_ZOOM_OUT、STATUS_ZOOM_IN和STATUS_MOVE 
  35.      */ 
  36.     private int currentStatus; 
  37.  
  38.     /** 
  39.      * ZoomImageView控件的宽度 
  40.      */ 
  41.     private int width; 
  42.  
  43.     /** 
  44.      * ZoomImageView控件的高度 
  45.      */ 
  46.     private int height; 
  47.  
  48.     /** 
  49.      * 记录两指同时放在屏幕上时,中心点的横坐标值 
  50.      */ 
  51.     private float centerPointX; 
  52.  
  53.     /** 
  54.      * 记录两指同时放在屏幕上时,中心点的纵坐标值 
  55.      */ 
  56.     private float centerPointY; 
  57.  
  58.     /** 
  59.      * 记录当前图片的宽度,图片被缩放时,这个值会一起变动 
  60.      */ 
  61.     private float currentBitmapWidth; 
  62.  
  63.     /** 
  64.      * 记录当前图片的高度,图片被缩放时,这个值会一起变动 
  65.      */ 
  66.     private float currentBitmapHeight; 
  67.  
  68.     /** 
  69.      * 记录上次手指移动时的横坐标 
  70.      */ 
  71.     private float lastXMove = -1
  72.  
  73.     /** 
  74.      * 记录上次手指移动时的纵坐标 
  75.      */ 
  76.     private float lastYMove = -1
  77.  
  78.     /** 
  79.      * 记录手指在横坐标方向上的移动距离 
  80.      */ 
  81.     private float movedDistanceX; 
  82.  
  83.     /** 
  84.      * 记录手指在纵坐标方向上的移动距离 
  85.      */ 
  86.     private float movedDistanceY; 
  87.  
  88.     /** 
  89.      * 记录图片在矩阵上的横向偏移值 
  90.      */ 
  91.     private float totalTranslateX; 
  92.  
  93.     /** 
  94.      * 记录图片在矩阵上的纵向偏移值 
  95.      */ 
  96.     private float totalTranslateY; 
  97.  
  98.     /** 
  99.      * 记录图片在矩阵上的总缩放比例 
  100.      */ 
  101.     private float totalRatio; 
  102.  
  103.     /** 
  104.      * 记录手指移动的距离所造成的缩放比例 
  105.      */ 
  106.     private float scaledRatio; 
  107.  
  108.     /** 
  109.      * 记录图片初始化时的缩放比例 
  110.      */ 
  111.     private float initRatio; 
  112.  
  113.     /** 
  114.      * 记录上次两指之间的距离 
  115.      */ 
  116.     private double lastFingerDis; 
  117.  
  118.     /** 
  119.      * ZoomImageView构造函数,将当前操作状态设为STATUS_INIT。 
  120.      *  
  121.      * @param context 
  122.      * @param attrs 
  123.      */ 
  124.     public ZoomImageView(Context context, AttributeSet attrs) { 
  125.         super(context, attrs); 
  126.         currentStatus = STATUS_INIT; 
  127.     } 
  128.  
  129.     /** 
  130.      * 将待展示的图片设置进来。 
  131.      *  
  132.      * @param bitmap 
  133.      *            待展示的Bitmap对象 
  134.      */ 
  135.     public void setImageBitmap(Bitmap bitmap) { 
  136.         sourceBitmap = bitmap; 
  137.         invalidate(); 
  138.     } 
  139.  
  140.     @Override 
  141.     protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
  142.         super.onLayout(changed, left, top, right, bottom); 
  143.         if (changed) { 
  144.             // 分别获取到ZoomImageView的宽度和高度 
  145.             width = getWidth(); 
  146.             height = getHeight(); 
  147.         } 
  148.     } 
  149.  
  150.     @Override 
  151.     public boolean onTouchEvent(MotionEvent event) { 
  152.         switch (event.getActionMasked()) { 
  153.         case MotionEvent.ACTION_POINTER_DOWN: 
  154.             if (event.getPointerCount() == 2) { 
  155.                 // 当有两个手指按在屏幕上时,计算两指之间的距离 
  156.                 lastFingerDis = distanceBetweenFingers(event); 
  157.             } 
  158.             break
  159.         case MotionEvent.ACTION_MOVE: 
  160.             if (event.getPointerCount() == 1) { 
  161.                 // 只有单指按在屏幕上移动时,为拖动状态 
  162.                 float xMove = event.getX(); 
  163.                 float yMove = event.getY(); 
  164.                 if (lastXMove == -1 && lastYMove == -1) { 
  165.                     lastXMove = xMove; 
  166.                     lastYMove = yMove; 
  167.                 } 
  168.                 currentStatus = STATUS_MOVE; 
  169.                 movedDistanceX = xMove - lastXMove; 
  170.                 movedDistanceY = yMove - lastYMove; 
  171.                 // 进行边界检查,不允许将图片拖出边界 
  172.                 if (totalTranslateX + movedDistanceX > 0) { 
  173.                     movedDistanceX = 0
  174.                 } else if (width - (totalTranslateX + movedDistanceX) > currentBitmapWidth) { 
  175.                     movedDistanceX = 0
  176.                 } 
  177.                 if (totalTranslateY + movedDistanceY > 0) { 
  178.                     movedDistanceY = 0
  179.                 } else if (height - (totalTranslateY + movedDistanceY) > currentBitmapHeight) { 
  180.                     movedDistanceY = 0
  181.                 } 
  182.                 // 调用onDraw()方法绘制图片 
  183.                 invalidate(); 
  184.                 lastXMove = xMove; 
  185.                 lastYMove = yMove; 
  186.             } else if (event.getPointerCount() == 2) { 
  187.                 // 有两个手指按在屏幕上移动时,为缩放状态 
  188.                 centerPointBetweenFingers(event); 
  189.                 double fingerDis = distanceBetweenFingers(event); 
  190.                 if (fingerDis > lastFingerDis) { 
  191.                     currentStatus = STATUS_ZOOM_OUT; 
  192.                 } else { 
  193.                     currentStatus = STATUS_ZOOM_IN; 
  194.                 } 
  195.                 // 进行缩放倍数检查,最大只允许将图片放大4倍,最小可以缩小到初始化比例 
  196.                 if ((currentStatus == STATUS_ZOOM_OUT && totalRatio < 4 * initRatio) 
  197.                         || (currentStatus == STATUS_ZOOM_IN && totalRatio > initRatio)) { 
  198.                     scaledRatio = (float) (fingerDis / lastFingerDis); 
  199.                     totalRatio = totalRatio * scaledRatio; 
  200.                     if (totalRatio > 4 * initRatio) { 
  201.                         totalRatio = 4 * initRatio; 
  202.                     } else if (totalRatio < initRatio) { 
  203.                         totalRatio = initRatio; 
  204.                     } 
  205.                     // 调用onDraw()方法绘制图片 
  206.                     invalidate(); 
  207.                     lastFingerDis = fingerDis; 
  208.                 } 
  209.             } 
  210.             break
  211.         case MotionEvent.ACTION_POINTER_UP: 
  212.             if (event.getPointerCount() == 2) { 
  213.                 // 手指离开屏幕时将临时值还原 
  214.                 lastXMove = -1
  215.                 lastYMove = -1
  216.             } 
  217.             break
  218.         case MotionEvent.ACTION_UP: 
  219.             // 手指离开屏幕时将临时值还原 
  220.             lastXMove = -1
  221.             lastYMove = -1
  222.             break
  223.         default
  224.             break
  225.         } 
  226.         return true
  227.     } 
  228.  
  229.     /** 
  230.      * 根据currentStatus的值来决定对图片进行什么样的绘制操作。 
  231.      */ 
  232.     @Override 
  233.     protected void onDraw(Canvas canvas) { 
  234.         super.onDraw(canvas); 
  235.         switch (currentStatus) { 
  236.         case STATUS_ZOOM_OUT: 
  237.         case STATUS_ZOOM_IN: 
  238.             zoom(canvas); 
  239.             break
  240.         case STATUS_MOVE: 
  241.             move(canvas); 
  242.             break
  243.         case STATUS_INIT: 
  244.             initBitmap(canvas); 
  245.         default
  246.             canvas.drawBitmap(sourceBitmap, matrix, null); 
  247.             break
  248.         } 
  249.     } 
  250.  
  251.     /** 
  252.      * 对图片进行缩放处理。 
  253.      *  
  254.      * @param canvas 
  255.      */ 
  256.     private void zoom(Canvas canvas) { 
  257.         matrix.reset(); 
  258.         // 将图片按总缩放比例进行缩放 
  259.         matrix.postScale(totalRatio, totalRatio); 
  260.         float scaledWidth = sourceBitmap.getWidth() * totalRatio; 
  261.         float scaledHeight = sourceBitmap.getHeight() * totalRatio; 
  262.         float translateX = 0f; 
  263.         float translateY = 0f; 
  264.         // 如果当前图片宽度小于屏幕宽度,则按屏幕中心的横坐标进行水平缩放。否则按两指的中心点的横坐标进行水平缩放 
  265.         if (currentBitmapWidth < width) { 
  266.             translateX = (width - scaledWidth) / 2f; 
  267.         } else { 
  268.             translateX = totalTranslateX * scaledRatio + centerPointX * (1 - scaledRatio); 
  269.             // 进行边界检查,保证图片缩放后在水平方向上不会偏移出屏幕 
  270.             if (translateX > 0) { 
  271.                 translateX = 0
  272.             } else if (width - translateX > scaledWidth) { 
  273.                 translateX = width - scaledWidth; 
  274.             } 
  275.         } 
  276.         // 如果当前图片高度小于屏幕高度,则按屏幕中心的纵坐标进行垂直缩放。否则按两指的中心点的纵坐标进行垂直缩放 
  277.         if (currentBitmapHeight < height) { 
  278.             translateY = (height - scaledHeight) / 2f; 
  279.         } else { 
  280.             translateY = totalTranslateY * scaledRatio + centerPointY * (1 - scaledRatio); 
  281.             // 进行边界检查,保证图片缩放后在垂直方向上不会偏移出屏幕 
  282.             if (translateY > 0) { 
  283.                 translateY = 0
  284.             } else if (height - translateY > scaledHeight) { 
  285.                 translateY = height - scaledHeight; 
  286.             } 
  287.         } 
  288.         // 缩放后对图片进行偏移,以保证缩放后中心点位置不变 
  289.         matrix.postTranslate(translateX, translateY); 
  290.         totalTranslateX = translateX; 
  291.         totalTranslateY = translateY; 
  292.         currentBitmapWidth = scaledWidth; 
  293.         currentBitmapHeight = scaledHeight; 
  294.         canvas.drawBitmap(sourceBitmap, matrix, null); 
  295.     } 
  296.  
  297.     /** 
  298.      * 对图片进行平移处理 
  299.      *  
  300.      * @param canvas 
  301.      */ 
  302.     private void move(Canvas canvas) { 
  303.         matrix.reset(); 
  304.         // 根据手指移动的距离计算出总偏移值 
  305.         float translateX = totalTranslateX + movedDistanceX; 
  306.         float translateY = totalTranslateY + movedDistanceY; 
  307.         // 先按照已有的缩放比例对图片进行缩放 
  308.         matrix.postScale(totalRatio, totalRatio); 
  309.         // 再根据移动距离进行偏移 
  310.         matrix.postTranslate(translateX, translateY); 
  311.         totalTranslateX = translateX; 
  312.         totalTranslateY = translateY; 
  313.         canvas.drawBitmap(sourceBitmap, matrix, null); 
  314.     } 
  315.  
  316.     /** 
  317.      * 对图片进行初始化操作,包括让图片居中,以及当图片大于屏幕宽高时对图片进行压缩。 
  318.      *  
  319.      * @param canvas 
  320.      */ 
  321.     private void initBitmap(Canvas canvas) { 
  322.         if (sourceBitmap != null) { 
  323.             matrix.reset(); 
  324.             int bitmapWidth = sourceBitmap.getWidth(); 
  325.             int bitmapHeight = sourceBitmap.getHeight(); 
  326.             if (bitmapWidth > width || bitmapHeight > height) { 
  327.                 if (bitmapWidth - width > bitmapHeight - height) { 
  328.                     // 当图片宽度大于屏幕宽度时,将图片等比例压缩,使它可以完全显示出来 
  329.                     float ratio = width / (bitmapWidth * 1.0f); 
  330.                     matrix.postScale(ratio, ratio); 
  331.                     float translateY = (height - (bitmapHeight * ratio)) / 2f; 
  332.                     // 在纵坐标方向上进行偏移,以保证图片居中显示 
  333.                     matrix.postTranslate(0, translateY); 
  334.                     totalTranslateY = translateY; 
  335.                     totalRatio = initRatio = ratio; 
  336.                 } else { 
  337.                     // 当图片高度大于屏幕高度时,将图片等比例压缩,使它可以完全显示出来 
  338.                     float ratio = height / (bitmapHeight * 1.0f); 
  339.                     matrix.postScale(ratio, ratio); 
  340.                     float translateX = (width - (bitmapWidth * ratio)) / 2f; 
  341.                     // 在横坐标方向上进行偏移,以保证图片居中显示 
  342.                     matrix.postTranslate(translateX, 0); 
  343.                     totalTranslateX = translateX; 
  344.                     totalRatio = initRatio = ratio; 
  345.                 } 
  346.                 currentBitmapWidth = bitmapWidth * initRatio; 
  347.                 currentBitmapHeight = bitmapHeight * initRatio; 
  348.             } else { 
  349.                 // 当图片的宽高都小于屏幕宽高时,直接让图片居中显示 
  350.                 float translateX = (width - sourceBitmap.getWidth()) / 2f; 
  351.                 float translateY = (height - sourceBitmap.getHeight()) / 2f; 
  352.                 matrix.postTranslate(translateX, translateY); 
  353.                 totalTranslateX = translateX; 
  354.                 totalTranslateY = translateY; 
  355.                 totalRatio = initRatio = 1f; 
  356.                 currentBitmapWidth = bitmapWidth; 
  357.                 currentBitmapHeight = bitmapHeight; 
  358.             } 
  359.             canvas.drawBitmap(sourceBitmap, matrix, null); 
  360.         } 
  361.     } 
  362.  
  363.     /** 
  364.      * 计算两个手指之间的距离。 
  365.      *  
  366.      * @param event 
  367.      * @return 两个手指之间的距离 
  368.      */ 
  369.     private double distanceBetweenFingers(MotionEvent event) { 
  370.         float disX = Math.abs(event.getX(0) - event.getX(1)); 
  371.         float disY = Math.abs(event.getY(0) - event.getY(1)); 
  372.         return Math.sqrt(disX * disX + disY * disY); 
  373.     } 
  374.  
  375.     /** 
  376.      * 计算两个手指之间中心点的坐标。 
  377.      *  
  378.      * @param event 
  379.      */ 
  380.     private void centerPointBetweenFingers(MotionEvent event) { 
  381.         float xPoint0 = event.getX(0); 
  382.         float yPoint0 = event.getY(0); 
  383.         float xPoint1 = event.getX(1); 
  384.         float yPoint1 = event.getY(1); 
  385.         centerPointX = (xPoint0 + xPoint1) / 2
  386.         centerPointY = (yPoint0 + yPoint1) / 2
  387.     } 
  388.  

由于这个类是整个多点触控缩放功能最核心的一个类,我在这里给大家详细的讲解一下。首先在ZoomImageView里我们定义了四种状态,STATUS_INIT、STATUS_ZOOM_OUT、STATUS_ZOOM_IN和STATUS_MOVE,这四个状态分别代表初始化、放大、缩小和移动这几个动作,然后在构造函数里我们将当前状态置为初始化状态。接着我们可以调用setImageBitmap()方法把要显示的图片对象传进去,这个方法会invalidate一下当前的View,因此onDraw()方法就会得到执行。然后在onDraw()方法里判断出当前的状态是初始化状态,所以就会调用initBitmap()方法进行初始化操作。



本文地址 : http://www.fengfly.com/plus/view-214551-1.html
标签: Android 移动 缩放 多点触控
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
验证码: