欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现对图片放大、平移和旋转的功能

程序员文章站 2024-03-06 19:24:08
先来看看要实现的效果图 在讲解中,需要大家提前了解一些关于图片绘制的原理的相关知识。 关于实现的流程      1、自...

先来看看要实现的效果图

Android实现对图片放大、平移和旋转的功能

在讲解中,需要大家提前了解一些关于图片绘制的原理的相关知识。

关于实现的流程

     1、自定义view

     2、获得操作图片的bitmap

     3、复写viewontouchevent()方法中的action_downaction_pointer_downaction_moveaction_pointer_up以及action_up事件。

     4、定义相应图片变化的matrix矩阵,通过手势操作的变化来设置相应的matrix

     5、完成最终的matrix设置时,通过invalidate()方法重新绘制页面。

那么接下来我们根据以上流程一步一步实现代码。

代码演示

/**
 * 作者:zhouyou
 * 日期:2016/8/23.
 */
public class touchimageview extends view {

  // 绘制图片的边框
  private paint paintedge;
  // 绘制图片的矩阵
  private matrix matrix = new matrix();
  // 手指按下时图片的矩阵
  private matrix downmatrix = new matrix();
  // 手指移动时图片的矩阵
  private matrix movematrix = new matrix();
  // 资源图片的位图
  private bitmap srcimage;
  // 多点触屏时的中心点
  private pointf midpoint = new pointf();
  // 触控模式
  private int mode;
  private static final int none = 0; // 无模式
  private static final int trans = 1; // 拖拽模式
  private static final int zoom = 2; // 缩放模式
  // 是否超过边界
  private boolean withinborder;

  public touchimageview(context context) {
    this(context, null);
  }

  public touchimageview(context context, attributeset attrs) {
    this(context, attrs, 0);
  }

  public touchimageview(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
    init();
  }

  private void init() {
    paintedge = new paint();
    paintedge.setcolor(color.black);
    paintedge.setalpha(170);
    paintedge.setantialias(true);
  }

  @override
  protected void onsizechanged(int w, int h, int oldw, int oldh) {
    super.onsizechanged(w, h, oldw, oldh);
    srcimage = bitmapfactory.decoderesource(getresources(), r.mipmap.ic_avatar_1);
  }

  @override
  protected void ondraw(canvas canvas) {
    super.ondraw(canvas);
    float[] points = getbitmappoints(srcimage, matrix);
    float x1 = points[0];
    float y1 = points[1];
    float x2 = points[2];
    float y2 = points[3];
    float x3 = points[4];
    float y3 = points[5];
    float x4 = points[6];
    float y4 = points[7];
    // 画边框
    canvas.drawline(x1, y1, x2, y2, paintedge);
    canvas.drawline(x2, y2, x4, y4, paintedge);
    canvas.drawline(x4, y4, x3, y3, paintedge);
    canvas.drawline(x3, y3, x1, y1, paintedge);
    // 画图片
    canvas.drawbitmap(srcimage, matrix, null);
  }

  // 手指按下屏幕的x坐标
  private float downx;
  // 手指按下屏幕的y坐标
  private float downy;
  // 手指之间的初始距离
  private float olddistance;
  // 手指之间的初始角度
  private float oldrotation;

  @override
  public boolean ontouchevent(motionevent event) {
    int action = motioneventcompat.getactionmasked(event);
    switch (action) {
      case motionevent.action_down:
        mode = trans;
        downx = event.getx();
        downy = event.gety();
        downmatrix.set(matrix);
        break;
      case motionevent.action_pointer_down: // 多点触控
        mode = zoom;
        olddistance = getspacedistance(event);
        oldrotation = getspacerotation(event);
        downmatrix.set(matrix);
        midpoint = getmidpoint(event);
        break;
      case motionevent.action_move:
        // 缩放
        if (mode == zoom) {
          movematrix.set(downmatrix);
          float deltarotation = getspacerotation(event) - oldrotation;
          float scale = getspacedistance(event) / olddistance;
          movematrix.postscale(scale, scale, midpoint.x, midpoint.y);
          movematrix.postrotate(deltarotation, midpoint.x, midpoint.y);
          withinborder = getmatrixbordercheck(srcimage, event.getx(), event.gety());
          if (withinborder) {
            matrix.set(movematrix);
            invalidate();
          }
        }
        // 平移
        else if (mode == trans) {
          movematrix.set(downmatrix);
          movematrix.posttranslate(event.getx() - downx, event.gety() - downy);
          withinborder = getmatrixbordercheck(srcimage, event.getx(), event.gety());
          if (withinborder) {
            matrix.set(movematrix);
            invalidate();
          }
        }
        break;
      case motionevent.action_pointer_up:
      case motionevent.action_up:
        mode = none;
        break;
      default:
        break;
    }
    return true;
  }

  /**
   * 获取手指的旋转角度
   *
   * @param event
   * @return
   */
  private float getspacerotation(motionevent event) {
    double deltax = event.getx(0) - event.getx(1);
    double deltay = event.gety(0) - event.gety(1);
    double radians = math.atan2(deltay, deltax);
    return (float) math.todegrees(radians);
  }

  /**
   * 获取手指间的距离
   *
   * @param event
   * @return
   */
  private float getspacedistance(motionevent event) {
    float x = event.getx(0) - event.getx(1);
    float y = event.gety(0) - event.gety(1);
    return (float) math.sqrt(x * x + y * y);
  }

  /**
   * 获取手势中心点
   *
   * @param event
   */
  private pointf getmidpoint(motionevent event) {
    pointf point = new pointf();
    float x = event.getx(0) + event.getx(1);
    float y = event.gety(0) + event.gety(1);
    point.set(x / 2, y / 2);
    return point;
  }

  /**
   * 将matrix的点映射成坐标点
   *
   * @return
   */
  protected float[] getbitmappoints(bitmap bitmap, matrix matrix) {
    float[] dst = new float[8];
    float[] src = new float[]{
        0, 0,
        bitmap.getwidth(), 0,
        0, bitmap.getheight(),
        bitmap.getwidth(), bitmap.getheight()
    };
    matrix.mappoints(dst, src);
    return dst;
  }

  /**
   * 检查边界
   *
   * @param x
   * @param y
   * @return true - 在边界内 | false - 超出边界
   */
  private boolean getmatrixbordercheck(bitmap bitmap, float x, float y) {
    if (bitmap == null) return false;
    float[] points = getbitmappoints(bitmap, movematrix);
    float x1 = points[0];
    float y1 = points[1];
    float x2 = points[2];
    float y2 = points[3];
    float x3 = points[4];
    float y3 = points[5];
    float x4 = points[6];
    float y4 = points[7];
    float edge = (float) math.sqrt(math.pow(x1 - x2, 2) + math.pow(y1 - y2, 2));
    if ((2 + math.sqrt(2)) * edge >= math.sqrt(math.pow(x - x1, 2) + math.pow(y - y1, 2))
        + math.sqrt(math.pow(x - x2, 2) + math.pow(y - y2, 2))
        + math.sqrt(math.pow(x - x3, 2) + math.pow(y - y3, 2))
        + math.sqrt(math.pow(x - x4, 2) + math.pow(y - y4, 2))) {
      return true;
    }
    return false;
  }
}

我已经在代码中针对可能遇到的问题做了详细的注释。

1. matrix

  // 绘制图片的矩阵
  private matrix matrix = new matrix();
  // 手指按下时图片的矩阵
  private matrix downmatrix = new matrix();
  // 手指移动时图片的矩阵
  private matrix movematrix = new matrix();

首先我定义了三个matrix变量,目的在于通过不同手势的操控图片的matrix最终由绘制图片的matrix所接收,因此需要在不同的操作中使用不同的matrix进行图形变换的数据传递,从而在渲染页面的时候将最终的matrix再传递回绘图的matrix

2. pointf

  // 多点触屏时的中心点
  private pointf midpoint = new pointf();

因为如果是针对图片的旋转和放大操作,需要通过两个手指进行控制,因此我们需要知道在多个手指触摸屏幕时的中心点坐标。

3. 触控模式

  // 触控模式
  private int mode;
  private static final int none = 0; // 无模式
  private static final int trans = 1; // 拖拽模式
  private static final int zoom = 2; // 缩放模式

ontouchevent()事件中,会根据不同的事件变换触控的模式,从而进行不同图片变换的操作。

4. ontouchevent()

首先,我们是自定义的view,因此如果要对该事件进行消费的话,需要将返回值设置为true

(1)action_down - 该事件是单点触屏的事件,也就是说如果一个手指按下屏幕的时候就会回调这个事件。那么我们在该事件中就将触控模式设置为拖拽模式(trans),记录下按下屏幕的xy坐标,并在这个事件中将绘图的matrix复制给按下屏幕的matrix。

case motionevent.action_down:
   mode = trans;
   downx = event.getx();
   downy = event.gety();
   downmatrix.set(matrix);
   break;

(2)action_pointer_down - 这个事件发生在超过一个手指触摸屏幕的时候。我们在这个事件中即可针对多点触屏的操作进行初始化设置。在该事件中,我们将触控模式重新设置为(zoom),初始化两指之间触摸屏幕的距离以及两指之间的旋转角度,初始化两指之间的中心点坐标。最后把绘图的matrix复制给按下屏幕的matrix。

case motionevent.action_pointer_down: // 多点触控
    mode = zoom;
    olddistance = getspacedistance(event);
    oldrotation = getspacerotation(event);
    midpoint = getmidpoint(event);
    downmatrix.set(matrix);
    break;

(3)action_move - 到了移动的事件中,根据之前的触控模式进行判断。首先,将按下事件的matrix复制给移动事件的matrix。如果是(zoom)模式,我们将会根据事件获得手指旋转角度的差值,以及手指之间距离的差值。根据这两个差值,以及在action_down事件中获得的中点坐标,我们即可设置move事件的缩放和旋转。(trans)模式也是如此。最后通过获取图片变换的边界值来判断是否进行绘图渲染。

case motionevent.action_move:
        // 缩放
        if (mode == zoom) {
          movematrix.set(downmatrix);
          float deltarotation = getspacerotation(event) - oldrotation;
          float scale = getspacedistance(event) / olddistance;
          movematrix.postscale(scale, scale, midpoint.x, midpoint.y);
          movematrix.postrotate(deltarotation, midpoint.x, midpoint.y);
          withinborder = getmatrixbordercheck(srcimage, event.getx(), event.gety());
          if (withinborder) {
            matrix.set(movematrix);
            invalidate();
          }
        }
        // 平移
        else if (mode == trans) {
          movematrix.set(downmatrix);
          movematrix.posttranslate(event.getx() - downx, event.gety() - downy);
          withinborder = getmatrixbordercheck(srcimage, event.getx(), event.gety());
          if (withinborder) {
            matrix.set(movematrix);
            invalidate();
          }
        }
        break;

(4)action_pointer_up和action_up - 在这两个事件中,重新将触屏的模式设置会none。

5. 边界判断

以下即为边界判断的逻辑是针对正方形的图片来说的。首先通过原图片相对自己四个坐标映射成为matrix对应屏幕的点坐标。通过得到4个点的坐标,我们即可根据手指触摸图片时的坐标与图片的4个点坐标进行关联。

边界判断的逻辑是手指触摸图片的点到4个顶点的距离之和如果小于(2+根号2倍)的斜边长度,即视为不超过边界。

/**
   * 将matrix的点映射成坐标点
   *
   * @return
   */
  protected float[] getbitmappoints(bitmap bitmap, matrix matrix) {
    float[] dst = new float[8];
    float[] src = new float[]{
        0, 0,
        bitmap.getwidth(), 0,
        0, bitmap.getheight(),
        bitmap.getwidth(), bitmap.getheight()
    };
    matrix.mappoints(dst, src);
    return dst;
  }
/**
   * 检查边界
   *
   * @param x
   * @param y
   * @return true - 在边界内 | false - 超出边界
   */
  private boolean getmatrixbordercheck(bitmap bitmap, float x, float y) {
    if (bitmap == null) return false;
    float[] points = getbitmappoints(bitmap, movematrix);
    float x1 = points[0];
    float y1 = points[1];
    float x2 = points[2];
    float y2 = points[3];
    float x3 = points[4];
    float y3 = points[5];
    float x4 = points[6];
    float y4 = points[7];
    float edge = (float) math.sqrt(math.pow(x1 - x2, 2) + math.pow(y1 - y2, 2));
    if ((2 + math.sqrt(2)) * edge >= math.sqrt(math.pow(x - x1, 2) + math.pow(y - y1, 2))
        + math.sqrt(math.pow(x - x2, 2) + math.pow(y - y2, 2))
        + math.sqrt(math.pow(x - x3, 2) + math.pow(y - y3, 2))
        + math.sqrt(math.pow(x - x4, 2) + math.pow(y - y4, 2))) {
      return true;
    }
    return false;
  }

总结

好了,本文的内容到这就结束了,完成了以上的步骤,即可完成针对图片在屏幕上的放大、平移和旋转的操作。是不是还是很简单的。有兴趣的可以自己动手操作起来,希望这篇文章对大家的学习和工作能有所帮助,如果有疑问可以留言交流,谢谢大家对的支持。