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;

	}

}

实现效果图

总结

对图片的伸缩释放已经实现了,但是相信读者也能够知道,单单只实现多点触控自由伸缩是不够的,用户还希望可以左右滑动实现图片切换浏览,该部分的实现将在近期发布,尽情期待。

代码下载地址

时间: 2024-10-19 09:40:08

Android实现图片多点触控自由伸缩的相关文章

MultiTouch————多点触控,伸缩图片,变换图片位置

前言:当今的手机都支持多点触控功能(可以进行图片伸缩,变换位置),但是我们程序员要怎样结合硬件去实现这个功能呢? 跟随我一起,来学习这个功能 国际惯例:先上DEMO免费下载地址:http://download.csdn.net/detail/cnwutianhao/9443667 示例图片: 我是用Genymotion录制的,没有真机上多点触控显示的效果那么好,大家在真机上跑程序,会体会到多点触控功能.(注:Genymotion多点触控快捷键是 ctrl+鼠标指针上下拖动) 具体代码实现: 布局

android中禁用多点触控的方法

在自定义一些控件,或者使用别人写的自定义控件的时候,偶尔会遇到多点触控的时候出现各种问题,所以需要对一些地方进行多点触控的禁用,下面是我找到的两个简单的方法: 1. 直接使用自定义主题Theme来修改整个APP的样式,禁止全局多点触控: <style name="MyThemeStyle"> <!-- 禁止多点触控 --> <item name="android:windowEnableSplitTouch">false</

cocos2d3.x在android下屏蔽多点触控

ios上很简单的在AppController.mm里 [eaglView setMultipleTouchEnabled:YES] 设置为NO,就是单点触控了,无需更改cocos底层代码; android上的做法是找到项目所引用的cocos引擎文件: Cocos2dxGLSurfaceView.java,找到onTouchEvent方法,在switch语句里的 MotionEvent.ACTION_POINTER_DOWN MotionEvent.ACTION_DOWN 这两个case 的第一行

android多点触控自由对图片缩放

在系统的相册中,观看相片就可以用多个手指进行缩放. 要实现这个功能,只需要这几步: 1.新建项目,在项目中新建一个ZoomImage.java public class ZoomImageView extends View { //初始化状态常量 public static final int STATUS_INIT=1; //图片放大状态常量 public static final int STATUS_ZOOM_OUT=2; //图片缩小状态常量 public static final in

Android多点触控MultiTouch浅析

申明: 参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2013/0226/914.html 下面实现如何通过应用层支持多点触控操作,对于常规的控件触控操实现onTouchEvent()方法来处理.同时对onTouchEvent方法的参数MotionEvent进行一些了解. 正文: 下面会用两个应用示例来初步学习一下Android中的多点触控. 示例一(DemoMultiTouch-Canvas): 核心技术介绍 本示例是在Sur

Android实现多点触控,自由缩放图片

Android多点触控涉及到的知识点 1.ScaleGestureDetector 2.OnScaleGestureListener 3.Matrix 4.OnTouchListener 四个知识点需要了解一下,需要注意的是Matrix在内存中是一个一维数组,操控图片的Matrxi是一个3X3的矩阵,在内存中也就是一个大小为9的一维数组. 实现多点触控,自由变化图片 1. ImageView的基础上继承 2.因为要在图片加载完成就获取到相关的属性,所以实现OnGlobalLayoutListen

Android多点触控技术实战,自由地对图片进行缩放和移动

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11100327 在上一篇文章中我带着大家一起实现了Android瀑布流照片墙的效果,虽然这种效果很炫很酷,但其实还只能算是一个半成品,因为照片墙中所有的图片都是只能看不能点的.因此本篇文章中,我们就来对这一功能进行完善,加入点击图片就能浏览大图的功能,并且在浏览大图的时候还可以通过多点触控的方式对图片进行缩放. 如果你还没有看过 Android瀑布流照片墙实现,体验不规则排列的美感

Android多点触控(图片的缩放Demo)

本文主要介绍Android的多点触控,使用了一个图片缩放的实例,来更好的说明其原理.需要实现OnTouchListener接口,重写其中的onTouch方法. 实现效果图: 源代码: 布局文件: activity_main: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools&quo

Android多点触控技术,实现对图片的放大缩小平移,惯性滑动等功能

首先推荐一下鸿洋大大的打造个性的图片预览与多点触控视频教程,这套教程教我们一步一步实现了多点触控实现对图片的平移和缩放的功能,这篇文章我将在鸿洋大大的基础之上做了一些扩展功能: 1.图片的惯性滑动 2.图片缩放小于正常比例时,松手会自动回弹成正常比例 3.图片缩放大于最大比例时,松手会自动回弹成最大比例 实现图片的缩放,平移,双击缩放等基本功能的代码如下,每一行代码我都做了详细的注释 public class ZoomImageView extends ImageView implements