最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android實(shí)現(xiàn)對圖片放大、平移和旋轉(zhuǎn)的功能

 更新時間:2016年08月29日 09:24:07   投稿:daisy  
現(xiàn)在很多App在查看一張圖片的原圖時,都會支持圖片的手勢縮放,手勢平移以及圖片旋轉(zhuǎn)的操作。那么今天小編就來教大家去簡單的實(shí)現(xiàn)圖片的放大、平移、旋轉(zhuǎn)的操作,有需要的可以參考借鑒。

先來看看要實(shí)現(xiàn)的效果圖

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

關(guān)于實(shí)現(xiàn)的流程

     1、自定義View

     2、獲得操作圖片的Bitmap

     3、復(fù)寫ViewonTouchEvent()方法中的ACTION_DOWN,ACTION_POINTER_DOWNACTION_MOVE,ACTION_POINTER_UP以及ACTION_UP事件。

     4、定義相應(yīng)圖片變化的Matrix矩陣,通過手勢操作的變化來設(shè)置相應(yīng)的Matrix。

     5、完成最終的Matrix設(shè)置時,通過invalidate()方法重新繪制頁面。

那么接下來我們根據(jù)以上流程一步一步實(shí)現(xiàn)代碼。

代碼演示

/**
 * 作者: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;
  // 多點(diǎn)觸屏?xí)r的中心點(diǎn)
  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坐標(biāo)
  private float downX;
  // 手指按下屏幕的Y坐標(biāo)
  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: // 多點(diǎn)觸控
        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;
  }

  /**
   * 獲取手指的旋轉(zhuǎn)角度
   *
   * @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);
  }

  /**
   * 獲取手勢中心點(diǎn)
   *
   * @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的點(diǎn)映射成坐標(biāo)點(diǎn)
   *
   * @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 - 在邊界內(nèi) | 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;
  }
}

我已經(jīng)在代碼中針對可能遇到的問題做了詳細(xì)的注釋。

1. Matrix

  // 繪制圖片的矩陣
  private Matrix matrix = new Matrix();
  // 手指按下時圖片的矩陣
  private Matrix downMatrix = new Matrix();
  // 手指移動時圖片的矩陣
  private Matrix moveMatrix = new Matrix();

首先我定義了三個Matrix變量,目的在于通過不同手勢的操控圖片的Matrix最終由繪制圖片的Matrix所接收,因此需要在不同的操作中使用不同的Matrix進(jìn)行圖形變換的數(shù)據(jù)傳遞,從而在渲染頁面的時候?qū)⒆罱K的Matrix再傳遞回繪圖的Matrix。

2. PointF

  // 多點(diǎn)觸屏?xí)r的中心點(diǎn)
  private PointF midPoint = new PointF();

因?yàn)槿绻轻槍D片的旋轉(zhuǎn)和放大操作,需要通過兩個手指進(jìn)行控制,因此我們需要知道在多個手指觸摸屏幕時的中心點(diǎn)坐標(biāo)。

3. 觸控模式

  // 觸控模式
  private int mode;
  private static final int NONE = 0; // 無模式
  private static final int TRANS = 1; // 拖拽模式
  private static final int ZOOM = 2; // 縮放模式

onTouchEvent()事件中,會根據(jù)不同的事件變換觸控的模式,從而進(jìn)行不同圖片變換的操作。

4. onTouchEvent()

首先,我們是自定義的View,因此如果要對該事件進(jìn)行消費(fèi)的話,需要將返回值設(shè)置為true。

(1)ACTION_DOWN - 該事件是單點(diǎn)觸屏的事件,也就是說如果一個手指按下屏幕的時候就會回調(diào)這個事件。那么我們在該事件中就將觸控模式設(shè)置為拖拽模式(TRANS),記錄下按下屏幕的xy坐標(biāo),并在這個事件中將繪圖的Matrix復(fù)制給按下屏幕的Matrix。

case MotionEvent.ACTION_DOWN:
   mode = TRANS;
   downX = event.getX();
   downY = event.getY();
   downMatrix.set(matrix);
   break;

(2)ACTION_POINTER_DOWN - 這個事件發(fā)生在超過一個手指觸摸屏幕的時候。我們在這個事件中即可針對多點(diǎn)觸屏的操作進(jìn)行初始化設(shè)置。在該事件中,我們將觸控模式重新設(shè)置為(ZOOM),初始化兩指之間觸摸屏幕的距離以及兩指之間的旋轉(zhuǎn)角度,初始化兩指之間的中心點(diǎn)坐標(biāo)。最后把繪圖的Matrix復(fù)制給按下屏幕的Matrix。

case MotionEvent.ACTION_POINTER_DOWN: // 多點(diǎn)觸控
    mode = ZOOM;
    oldDistance = getSpaceDistance(event);
    oldRotation = getSpaceRotation(event);
    midPoint = getMidPoint(event);
    downMatrix.set(matrix);
    break;

(3)ACTION_MOVE - 到了移動的事件中,根據(jù)之前的觸控模式進(jìn)行判斷。首先,將按下事件的Matrix復(fù)制給移動事件的Matrix。如果是(ZOOM)模式,我們將會根據(jù)事件獲得手指旋轉(zhuǎn)角度的差值,以及手指之間距離的差值。根據(jù)這兩個差值,以及在ACTION_DOWN事件中獲得的中點(diǎn)坐標(biāo),我們即可設(shè)置MOVE事件的縮放和旋轉(zhuǎn)。(TRANS)模式也是如此。最后通過獲取圖片變換的邊界值來判斷是否進(jìn)行繪圖渲染。

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 - 在這兩個事件中,重新將觸屏的模式設(shè)置會NONE。

5. 邊界判斷

以下即為邊界判斷的邏輯是針對正方形的圖片來說的。首先通過原圖片相對自己四個坐標(biāo)映射成為Matrix對應(yīng)屏幕的點(diǎn)坐標(biāo)。通過得到4個點(diǎn)的坐標(biāo),我們即可根據(jù)手指觸摸圖片時的坐標(biāo)與圖片的4個點(diǎn)坐標(biāo)進(jìn)行關(guān)聯(lián)。

邊界判斷的邏輯是手指觸摸圖片的點(diǎn)到4個頂點(diǎn)的距離之和如果小于(2+根號2倍)的斜邊長度,即視為不超過邊界。

/**
   * 將matrix的點(diǎn)映射成坐標(biāo)點(diǎn)
   *
   * @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 - 在邊界內(nèi) | 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;
  }

總結(jié)

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

相關(guān)文章

最新評論

武陟县| 昌图县| 彭州市| 鄂伦春自治旗| 南陵县| 万荣县| 柳河县| 天柱县| 包头市| 崇义县| 内江市| 宽城| 漠河县| 措美县| 峨眉山市| 神池县| 景洪市| 贵定县| 延安市| 施秉县| 万宁市| 南召县| 古田县| 垣曲县| 志丹县| 镇原县| 玉林市| 扎赉特旗| 凤山市| 茌平县| 凉山| 赣州市| 荃湾区| 江油市| 扶余县| 新野县| 沐川县| 星座| 莱州市| 渝中区| 镇江市|