Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android實現圖片多點觸控自由伸縮

Android實現圖片多點觸控自由伸縮

編輯:關於Android編程

簡介

作為Android開發者,我們經常需要自定義控件,比如下面我們說的實現圖片的多點觸控和伸縮釋放,這也是由於用戶已經有這樣的常識了,那就是看見有圖片的地方就可以點擊查看大圖,並且可以通過手指對圖片進行伸縮和移動,如果應用沒有實現這一點,那麼對用戶來說將會是很糟糕的體驗,用戶很“憤怒”。所以作為Android開發者,我們的任務就是讓用戶“爽”。哈哈哈。。。。下面我們將通過自定義ImageView實現以上功能。

涉及技術

一、Matrix(矩陣),Android是通過Matrix去控制圖片的伸縮和平移的。

二、ScaleGestureDetector(伸縮手勢探測器),實現對用戶操作圖片伸縮的監聽。

實現原理

一、創建ZoomImageView類,通過重寫ImageView的OnAttachedToWindow()注冊全局布局監聽器getViewTreeObserver().addOnGlobalLayoutListener(this),實現對圖片的初始化控制

二、在全局布局監聽器裡面通過Matrix控制初始化圖片居中顯示和縮放。

三、注冊onTouch()事件,通過代碼mScaleGestureDetector.onTouchEvent(motionEvent) ;實現ScaleGestureDetector監聽手勢。

四、在ScaleGestureDetector的回調方法onScale(ScaleGestureDetector detector)裡面實現對根據用戶的操作,實現對圖片的伸縮和移動。

 

代碼

package com.liujun.liujunzoomimagedemo.view;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.view.ViewTreeObserver;

public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener, OnScaleGestureListener, OnTouchListener {

	private ScaleGestureDetector mScaleGestureDetector = null;

	private Matrix matrix;

	private float[] matrixValues = new float[9];// 矩陣的九個值

	private boolean isOnceLayout = true;

	// 設置縮放比
	private float initScale = 1.0f;
	private float midScale = 2.0f;
	private float maxScale = 4.0f;

	public ZoomImageView(Context context, AttributeSet attrs, int defStyle) {

		super(context, attrs, defStyle);

		super.setScaleType(ScaleType.MATRIX);// 設置圖片通過矩陣控制

		// 實例化伸縮手勢探測器
		mScaleGestureDetector = new ScaleGestureDetector(context, this);

		matrix = new Matrix();

		this.setOnTouchListener(this);

	}

	public ZoomImageView(Context context, AttributeSet attrs) {

		this(context, attrs, 0);
	}

	public ZoomImageView(Context context) {

		this(context, null);

	}

	@Override
	protected void onAttachedToWindow() {
		super.onAttachedToWindow();

		// 注冊全局布局監聽器
		getViewTreeObserver().addOnGlobalLayoutListener(this);

	}

	@SuppressWarnings("deprecation")
	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();

		// 取消全局布局監聽器
		getViewTreeObserver().removeGlobalOnLayoutListener(this);
	}

	/**
	 * 獲取布局的參數(這個方法會調用兩次)
	 */
	@Override
	public void onGlobalLayout() {

		if (isOnceLayout) {// 將圖片居中顯示,並且伸縮圖片

			Drawable drawable = this.getDrawable();

			if (drawable == null) {
				return;
			}

			// 獲取父控制的寬高
			int parentWidgetWidth = this.getWidth();
			int parentWidgetHeight = this.getHeight();

			// 獲取圖片的寬高
			int drawableHeight = drawable.getIntrinsicHeight();
			int drawableWidth = drawable.getIntrinsicWidth();

			// 定義縮放比
			float scale = 1.0f;

			// 當圖片寬度大於父控件的寬度,當高度小於父控件高度時(縮小)
			if (drawableWidth > parentWidgetWidth && drawableHeight <= parentWidgetHeight) {

				scale = parentWidgetWidth * 1.0f / drawableWidth;

			}

			// 當圖片高度高於父控件,但寬度小於父控件時(縮小)
			if (drawableHeight > parentWidgetHeight && drawableWidth <= parentWidgetWidth) {

				scale = parentWidgetHeight * 1.0f / drawableHeight;

			}

			// 當圖片寬度和高度都大於父控件時(縮小)
			if (drawableHeight > parentWidgetHeight && drawableWidth > parentWidgetWidth) {

				scale = Math.min(parentWidgetHeight * 1.0f / drawableHeight, parentWidgetWidth * 1.0f / drawableWidth);

			}

			// 當圖片寬度和高度都小於父控件(擴大)
			if (drawableHeight < parentWidgetHeight && drawableWidth < parentWidgetWidth) {

				scale = Math.min(parentWidgetHeight * 1.0f / drawableHeight, parentWidgetWidth * 1.0f / drawableWidth);

			}

			// 設置初始化的縮放比
			initScale = scale;

			// 將圖片縮放並且將圖片移動到父控件中間
			float dx = (parentWidgetWidth - drawableWidth) / 2;
			float dy = (parentWidgetHeight - drawableHeight) / 2;
			matrix.postTranslate(dx, dy);

			// 將圖片縮放
			matrix.postScale(scale, scale, parentWidgetWidth / 2, parentWidgetHeight / 2);

			// 將矩陣運用到圖片中
			this.setImageMatrix(matrix);

			isOnceLayout = false;

		}

	}

	/**
	 * 獲取圖片當前的縮放比
	 * 
	 * @return
	 */
	private float getCurrentImageScale() {

		matrix.getValues(matrixValues);

		return matrixValues[Matrix.MSCALE_X];

	}

	@Override
	public boolean onScale(ScaleGestureDetector detector) {

		// 獲取圖片當前的縮放比
		float currentScalse = getCurrentImageScale();

		// 拿到圖片將要的縮放比例
		float scaleFactor = detector.getScaleFactor();

		if (this.getDrawable() == null) {
			return true;
		}

		// 用戶將要放大圖片或者用戶將要縮小圖片
		if ((scaleFactor > 1.0f && currentScalse < maxScale) || (scaleFactor < 1.0f && currentScalse > initScale)) {

			// 縮小時
			if (scaleFactor * currentScalse < initScale) {
				scaleFactor = initScale / currentScalse;
			}

			// 放大時
			if (scaleFactor * currentScalse > maxScale) {
				scaleFactor = maxScale / currentScalse;
			}

			matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());

			// 檢查邊界和中心點
			checkBorderAndCenterWhenScale();

			setImageMatrix(matrix);

		}

		return true;

	}

	@Override
	public boolean onScaleBegin(ScaleGestureDetector detector) {

		return true;
	}

	@Override
	public void onScaleEnd(ScaleGestureDetector detector) {

	}

	/**
	 * 當在縮放的時候,對圖片的邊界和中心進行控制
	 */
	private void checkBorderAndCenterWhenScale() {

		// 獲取當前縮放過程中的圖片的矩形
		RectF rectF = getMatrixRectF();

		float deltaX = 0;
		float deltaY = 0;

		// 獲取父控件的寬高
		int parentWidth = getWidth();
		int parentHeight = getHeight();

		// 如果寬度大於屏幕寬度
		if (rectF.width() >= parentWidth) {

			if (rectF.left > 0) {// 左邊出現了空白

				deltaX = -rectF.left;// 往左移動

			}

			if (rectF.right < parentWidth) {// 右邊出現了空白

				deltaX = parentWidth - rectF.right;// 往右移動

			}

		}

		// 如果高度大於屏幕高度
		if (rectF.height() >= parentHeight) {

			if (rectF.top > 0) {// 上邊出現了空白

				deltaY = -rectF.top;// 往下移動

			}

			if (rectF.bottom < parentHeight) {// 下面出現了空白

				deltaY = parentHeight - rectF.bottom;// 往下移動

			}

		}

		// 如果寬度小於父控件的寬度
		if (rectF.width() < parentWidth) {// 要基中顯示

			deltaX = parentWidth * 0.5f - rectF.right + 0.5f * rectF.width();

		}

		// 如果高度消息小於父控件的高度
		if (rectF.height() < parentHeight) {// 需要基中顯示

			deltaY = parentHeight * 0.5f - rectF.bottom + 0.5f * rectF.height();

		}

		// 將圖片移動到父控件中心
		matrix.postTranslate(deltaX, deltaY);

	}

	/**
	 * 獲取圖片通過矩陣控制縮放之後的矩形
	 * 
	 * @return
	 */
	private RectF getMatrixRectF() {

		Matrix matrix2 = matrix;

		RectF rectF = new RectF();

		Drawable drawable = this.getDrawable();

		if (drawable != null) {

			rectF.set(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

			matrix2.mapRect(rectF);

		}

		return rectF;

	}

	@Override
	public boolean onTouch(View view, MotionEvent motionEvent) {

		// 用戶縮放手機探測器處理觸摸事件
		mScaleGestureDetector.onTouchEvent(motionEvent);

		return true;

	}

}
實現效果圖
\

總結

對圖片的伸縮釋放已經實現了,但是相信讀者也能夠知道,單單只實現多點觸控自由伸縮是不夠的,用戶還希望可以左右滑動實現圖片切換浏覽,該部分的實現將在近期發布,盡情期待。

 

代碼下載地址






  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved