国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android實現對圖片放大、平移和旋轉的功能

2019-12-12 05:31:02
字體:
來源:轉載
供稿:網友

先來看看要實現的效果圖

在講解中,需要大家提前了解一些關于圖片繪制的原理的相關知識。

關于實現的流程

     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;  }

總結

好了,本文的內容到這就結束了,完成了以上的步驟,即可完成針對圖片在屏幕上的放大、平移和旋轉的操作。是不是還是很簡單的。有興趣的可以自己動手操作起來,希望這篇文章對大家的學習和工作能有所幫助,如果有疑問可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 连江县| 长岭县| 四川省| 广南县| 香港| 杨浦区| 长丰县| 华容县| 都安| 梅河口市| 永仁县| 永泰县| 河北省| 泰宁县| 得荣县| 陕西省| 德钦县| 灵石县| 梨树县| 勃利县| 太和县| 和林格尔县| 麦盖提县| 仲巴县| 白朗县| 新沂市| 长岛县| 丰宁| 南木林县| 万全县| 江永县| 沽源县| 陵水| 张家口市| 青阳县| 洪湖市| 永丰县| 贺兰县| 大安市| 辽中县| 北海市|