Android中图像变换Matrix的原理、代码验证和应用(三)

时间:2022-06-22 11:37:20

第三部分 应用

在这一部分,我们会将前面两部分所了解到的内容和Android手势结合起来,利用各种不同的手势对图像进行平移、缩放和旋转,前面两项都是在实践中经常需要用到的功能,后一项据说苹果也是最近才加上的,而实际上在Android中,咱们通过自己的双手,也可以很轻松地实现之。

首先创建一个Android项目PatImageView,同时创建一个Activity:PatImageViewActivity。完成这一步后, 记得在AndroidManifest.xml中增加如下许可:

<uses-permissionandroid:name="android.permission.VIBRATE"/>

因为我们将要通过短按还是长按,来确定将图片到底是缩放还是旋转。

现在来创建一个ImageView的派生类:PatImageView,其代码(PatImageView.java)如下(2011-11-22 revised):

  1. package com.pat.imageview;
  2. import android.app.Service;
  3. import android.content.Context;
  4. import android.graphics.Matrix;
  5. import android.graphics.PointF;
  6. import android.os.Vibrator;
  7. import android.util.FloatMath;
  8. import android.view.GestureDetector;
  9. import android.view.MotionEvent;
  10. import android.view.View;
  11. import android.widget.ImageView;
  12. public class PatImageView extends ImageView
  13. {
  14. private Matrix matrix;
  15. private Matrix savedMatrix;
  16. private boolean long_touch = false;
  17. private static int NONE = 0;
  18. private static int DRAG = 1;    // 拖动
  19. private static int ZOOM = 2;    // 缩放
  20. private static int ROTA = 3;    // 旋转
  21. private int mode = NONE;
  22. private PointF startPoint;
  23. private PointF middlePoint;
  24. private float oldDistance;
  25. private float oldAngle;
  26. private Vibrator vibrator;
  27. private GestureDetector gdetector;
  28. public PatImageView(final Context context)
  29. {
  30. super(context);
  31. matrix = new Matrix();
  32. savedMatrix = new Matrix();
  33. matrix.setTranslate(0f, 0f);
  34. setScaleType(ScaleType.MATRIX);
  35. setImageMatrix(matrix);
  36. startPoint = new PointF();
  37. middlePoint = new PointF();
  38. oldDistance = 1f;
  39. gdetector = new GestureDetector(context, new GestureDetector.OnGestureListener()
  40. {
  41. @Override
  42. public boolean onSingleTapUp(MotionEvent e)
  43. {
  44. return true;
  45. }
  46. @Override
  47. public void onShowPress(MotionEvent e)
  48. {
  49. }
  50. @Override
  51. public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
  52. {
  53. return true;
  54. }
  55. @Override
  56. public void onLongPress(MotionEvent e)
  57. {
  58. long_touch = true;
  59. vibrator = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
  60. // 振动50ms,提示后续的操作将是旋转图片,而非缩放图片
  61. vibrator.vibrate(50);
  62. }
  63. @Override
  64. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
  65. {
  66. return true;
  67. }
  68. @Override
  69. public boolean onDown(MotionEvent e)
  70. {
  71. return true;
  72. }
  73. });
  74. setOnTouchListener(new OnTouchListener()
  75. {
  76. public boolean onTouch(View view, MotionEvent event)
  77. {
  78. switch(event.getAction() & MotionEvent.ACTION_MASK)
  79. {
  80. case MotionEvent.ACTION_DOWN:           // 第一个手指touch
  81. savedMatrix.set(matrix);
  82. startPoint.set(event.getX(), event.getY());
  83. mode = DRAG;
  84. long_touch = false;
  85. break;
  86. case MotionEvent.ACTION_POINTER_DOWN:   // 第二个手指touch
  87. oldDistance = getDistance(event);   // 计算第二个手指touch时,两指之间的距离
  88. oldAngle = getDegree(event);        // 计算第二个手指touch时,两指所形成的直线和x轴的角度
  89. if(oldDistance > 10f)
  90. {
  91. savedMatrix.set(matrix);
  92. middlePoint = midPoint(event);
  93. if(!long_touch)
  94. {
  95. mode = ZOOM;
  96. }
  97. else
  98. {
  99. mode = ROTA;
  100. }
  101. }
  102. break;
  103. case MotionEvent.ACTION_UP:
  104. mode = NONE;
  105. break;
  106. case MotionEvent.ACTION_POINTER_UP:
  107. mode = NONE;
  108. break;
  109. case MotionEvent.ACTION_MOVE:
  110. if(vibrator != null)    vibrator.cancel();
  111. if(mode == DRAG)
  112. {
  113. matrix.set(savedMatrix);
  114. matrix.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y);
  115. }
  116. if(mode == ZOOM)
  117. {
  118. float newDistance = getDistance(event);
  119. if(newDistance > 10f)
  120. {
  121. matrix.set(savedMatrix);
  122. float scale = newDistance / oldDistance;
  123. matrix.postScale(scale, scale, middlePoint.x, middlePoint.y);
  124. }
  125. }
  126. if(mode == ROTA)
  127. {
  128. float newAngle = getDegree(event);
  129. matrix.set(savedMatrix);
  130. float degrees = newAngle - oldAngle;
  131. matrix.postRotate(degrees, middlePoint.x, middlePoint.y);
  132. }
  133. break;
  134. }
  135. setImageMatrix(matrix);
  136. invalidate();
  137. gdetector.onTouchEvent(event);
  138. return true;
  139. }
  140. });
  141. }
  142. // 计算两个手指之间的距离
  143. private float getDistance(MotionEvent event)
  144. {
  145. float x = event.getX(0) - event.getX(1);
  146. float y = event.getY(0) - event.getY(1);
  147. return FloatMath.sqrt(x * x + y * y);
  148. }
  149. // 计算两个手指所形成的直线和x轴的角度
  150. private float getDegree(MotionEvent event)
  151. {
  152. return (float)(Math.atan((event.getY(1) - event.getY(0)) / (event.getX(1) - event.getX(0))) * 180f);
  153. }
  154. // 计算两个手指之间,中间点的坐标
  155. private PointF midPoint( MotionEvent event)
  156. {
  157. PointF point = new PointF();
  158. float x = event.getX(0) + event.getX(1);
  159. float y = event.getY(0) + event.getY(1);
  160. point.set(x / 2, y / 2);
  161. return point;
  162. }
  163. }

下面完善PatImageViewActivity.java的代码,使之如下:

  1. package com.pat.imageview;
  2. import android.app.Activity;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. import android.view.WindowManager;
  8. public class PatImageViewActivity extends Activity
  9. {
  10. @Override
  11. public void onCreate(Bundle savedInstanceState)
  12. {
  13. super.onCreate(savedInstanceState);
  14. requestWindowFeature(Window.FEATURE_NO_TITLE);
  15. this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  16. WindowManager.LayoutParams.FLAG_FULLSCREEN);
  17. PatImageView piv = new PatImageView(this);
  18. Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);
  19. piv.setImageBitmap(bmp);
  20. setContentView(piv);
  21. }
  22. }

由于有些手势在模拟器上无法模拟,所以就不上运行结果的图片了。本人在真机上运行后(照片就不拍了,有点累啦),可以轻松做到:

1.     很方便地拖动图片(比如,单指按住屏幕进行拖动)

2.     很方便地缩放图片(比如,双指按住屏幕进行分开或者并拢操作,可分别实现放大或者缩小图片的功能)

3.     长按出现振动后,可以很方便地旋转图片(一个手指固定,另外一个手指围绕那个固定的手指运动)。