android项目 之 记事本(14) ----- 手势缩放与拖拽图片

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020

上节实现了查看图片及录音的功能,其中查看图片,可以调用系统的图库来查看图片,也可以自定义Activity来查看图片,今天就在上节的基础上,实现手势缩放与拖拽图片。

想必大家都用过系统的图库,浏览图片时,可以通过手势放大或缩小图片,旋转图片,拖拽图片等功能,我们也为自已定义的查看图片的Activity增加手势缩放与拖拽图片的功能,效果如下图:

上面四幅图中,演示了通过手势(多点触控)来缩小,放大,拖拽图片。

这里主要是用到了多点触控,所以我们首先要知道多点和单点的区别。

单手指操作过程: ACTION_DOWN-ACTION_MOVE-ACTIOIN_UP

多手指操作过程:ACTION_DOWN-ACTION_POINTER_DOWN-ACTION_MOVE-ACTION_POINTER_UP-ACTION_UP

一般实现图片的缩放都是用Matrix的postScale方法,那么通过手势(多点)来缩放图片当然也不例外,区别就是通过手指的滑动来判断缩放的比例及中心位置,具体做法如下:

手势缩放图片的步骤:

1. 设置ImageView的scaleType属性为matrix。

因为实现图片的缩放要用到Matrix,所以这个属性是前提,可以在xml里设置ImageView设置,android:scaleType="matrix",或者在代码里设置imageView.setScaleType(ScaleType.matrix)

2. 给ImageView绑定触摸监听器
 //触摸事件
img.setOnTouchListener(new TouchEvent());
3. 在触摸事件中实现多手指缩放及拖拽图片

这是本节的核心,主要是要先判断MotionEvent的类型,是单手指还是多手指,可以通过event.getActionMasked()来获得,并设立三个标志,分别用于判断当前操作是拖拽,缩放还是无操作,如果是拖拽,则需要记录手指的起始位置及终点位置,然后利用Matrix的postTranslate方法来实现图片的移动。如果是缩放,则需要先计算图片缩放的比例及位置,在计算缩放比例时,又需要先知道多手指移动的直径,通过多手指移动前后的比例来得到缩放的比例;而要计算图片缩放的位置,只需要计算出手指移动前后的中点即可。

4. 控制缩放比例

其实,完成前3步就已经能实现通过手势来控制图片的缩放和移动,但是你会发现,这时,图片可以放大的无限大,也可以缩小到无限小,而且,不管图片是在放大状态,还是缩小状态,都会随你的手指的移动而移动到任何地方。这显然是不符合实际使用的,所以这就需要控制图片的缩放的比例,主要代码如下:

        //控制缩放比例
	private void controlScale(){
		float values[] = new float[9];
		matrix.getValues(values);
		if(mode == ZOOM){
			if(values[0] < MINSCALER)
				matrix.setScale(MINSCALER, MINSCALER);
			else if(values[0] > MAXSCALER)
				matrix.setScale(MAXSCALER, MAXSCALER);
		}
	}
5. 设置图片居中显示

通过第4步,可以控制图片的缩放比例,这样,图片就会有一个最大的和最小的绽放比例,当计算出的缩放比例小于最小的缩放比例时,就会设置当前的缩放比例为最小的缩放比例,当大于最大的缩放比例时,就设置当前图片的缩放比例为最大的缩放比例。

但是,还有一个问题,就是,这时不管图片的放大还是缩小状态,都会随着你的手指移动,但在实际过程中,我们往往需要图片的缩放后都是在控件的中心位置,即,设置图片居中显示,代码如下:

    //自动居中  左右及上下都居中
    protected void center()
    {
        center(true,true);
    }  

    private void center(boolean horizontal, boolean vertical)
    {
        Matrix m = new Matrix();
        m.set(matrix);
        RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());
        m.mapRect(rect);
        float height = rect.height();
        float width = rect.width();
        float deltaX = 0, deltaY = 0;
        if (vertical)
        {
            int screenHeight = dm.heightPixels;  //手机屏幕分辨率的高度
            //int screenHeight = 400;
            if (height < screenHeight)
            {
                deltaY = (screenHeight - height)/2 - rect.top;
            }else if (rect.top > 0)
            {
                deltaY = -rect.top;
            }else if (rect.bottom < screenHeight)
            {
                deltaY = screenHeight - rect.bottom;
            }
        }  

        if (horizontal)
        {
            int screenWidth = dm.widthPixels;  //手机屏幕分辨率的宽度
            //int screenWidth = 400;
            if (width < screenWidth)
            {
                deltaX = (screenWidth - width)/2 - rect.left;
            }else if (rect.left > 0)
            {
                deltaX = -rect.left;
            }else if (rect.right < screenWidth)
            {
                deltaX = screenWidth - rect.right;
            }
        }
        matrix.postTranslate(deltaX, deltaY);
    }  

通过居中设置后,这样图片在未占满屏幕时,是不能进行将其移动到其他位置的,只有在图片大于屏幕时,也可以移动图片从而查看图片的不同位置。

基本上,通过这5步,就已经可以实现图片手势(多点)缩放的功能,而且也可以控制图片的缩放比例及居中显示。下面给出整个代码:

public class ShowPicture extends Activity {
	private ImageView img;
	private Bitmap bm;
	private DisplayMetrics dm;
	private Matrix matrix = new Matrix();
	private Matrix savedMatrix = new Matrix();
	private PointF mid = new PointF();
	private PointF start = new PointF();
	private static int DRAG = 2;
	private static int ZOOM = 1;
	private static int NONE = 0;
	private int mode = 0;
	private float oldDist = 1f;
	private static float MINSCALER = 0.3f;
	private static float MAXSCALER = 3.0f;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
		setContentView(R.layout.activity_show_picture);
		getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.title_add);
		//设置标题
		TextView tv_title = (TextView)findViewById(R.id.tv_title);
		tv_title.setText("查看图片");
		Button bt_back = (Button)findViewById(R.id.bt_back);
		bt_back.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				ShowPicture.this.finish();
			}
		});
		Button bt_del = (Button)findViewById(R.id.bt_save);
		bt_del.setBackgroundResource(R.drawable.paint_icon_delete);

		dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm); //获取分辨率  

		img = (ImageView)findViewById(R.id.iv_showPic);

		Intent intent = this.getIntent();
		String imgPath = intent.getStringExtra("imgPath");
		bm = BitmapFactory.decodeFile(imgPath);
		//设置居中显示
		savedMatrix.setTranslate((dm.widthPixels - bm.getWidth())/2 , (dm.heightPixels - bm.getHeight()) / 2);
		img.setImageMatrix(savedMatrix);
		//绑定图片
		img.setImageBitmap(bm);
		//触摸事件
		img.setOnTouchListener(new TouchEvent());
	}

	//添加触摸事件,实现图片的手势缩放
	class TouchEvent implements OnTouchListener{
		@Override
		public boolean onTouch(View view, MotionEvent event) {
			switch(event.getActionMasked()){
				//单击触控,用于拖动
			   	case MotionEvent.ACTION_DOWN :
			   		matrix.set(img.getImageMatrix());
			   		savedMatrix.set(matrix);
			   		start.set(event.getX(), event.getY());
			   		mode = DRAG;
			   		break;
				//多点触控,按下时
				case MotionEvent.ACTION_POINTER_DOWN :
					oldDist = getSpacing(event);
					savedMatrix.set(matrix);
					getMidPoint(mid,event);
					mode = ZOOM;
					break;
				//多点触控,抬起时
				case MotionEvent.ACTION_POINTER_UP :
					mode = NONE;
					break;
				case MotionEvent.ACTION_MOVE :
					if(mode == DRAG){
						matrix.set(savedMatrix);
						matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);
					}
					//缩放
					else if(mode == ZOOM){
						//取得多指移动的直径,如果大于10,则认为是缩放手势
						float newDist = getSpacing(event);
						if(newDist > 10){
							matrix.set(savedMatrix);
							float scale = newDist / oldDist;

							matrix.postScale(scale, scale,mid.x,mid.y);
						}
					}
					break;
			}
			img.setImageMatrix(matrix);
			controlScale();
			//setCenter();
			center();
			return true;
		}
	}

	//求距离
	private float getSpacing(MotionEvent event){
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}

	//求中点
	private void getMidPoint(PointF mid,MotionEvent event){
		float x = event.getX(0) + event.getX(1);
		float y = event.getY(0) + event.getY(1);
		mid.set(x / 2, y / 2);
	}
	//控制缩放比例
	private void controlScale(){
		float values[] = new float[9];
		matrix.getValues(values);
		if(mode == ZOOM){
			if(values[0] < MINSCALER)
				matrix.setScale(MINSCALER, MINSCALER);
			else if(values[0] > MAXSCALER)
				matrix.setScale(MAXSCALER, MAXSCALER);
		}
	}
	//自动居中  左右及上下都居中
    protected void center()
    {
        center(true,true);
    }  

    private void center(boolean horizontal, boolean vertical)
    {
        Matrix m = new Matrix();
        m.set(matrix);
        RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());
        m.mapRect(rect);
        float height = rect.height();
        float width = rect.width();
        float deltaX = 0, deltaY = 0;
        if (vertical)
        {
            int screenHeight = dm.heightPixels;  //手机屏幕分辨率的高度
            //int screenHeight = 400;
            if (height < screenHeight)
            {
                deltaY = (screenHeight - height)/2 - rect.top;
            }else if (rect.top > 0)
            {
                deltaY = -rect.top;
            }else if (rect.bottom < screenHeight)
            {
                deltaY = screenHeight - rect.bottom;
            }
        }  

        if (horizontal)
        {
            int screenWidth = dm.widthPixels;  //手机屏幕分辨率的宽度
            //int screenWidth = 400;
            if (width < screenWidth)
            {
                deltaX = (screenWidth - width)/2 - rect.left;
            }else if (rect.left > 0)
            {
                deltaX = -rect.left;
            }else if (rect.right < screenWidth)
            {
                deltaX = screenWidth - rect.right;
            }
        }
        matrix.postTranslate(deltaX, deltaY);
    }
}

注:

另外还有一个问题就是,图片的初始居中问题,在ImageView中可以设置属性android:scaleType="fitCenter"来实现图片的居中显示,但是这里是要实现手势缩放图片,所以需要将该属性设置为android:scaleType="matrix",但同时,这也带来了一个问题,就是在初始时,图片不能居中。

在网上找过解决办法,第一种方法,就是在XML文件里先设置ImageVIew的scaleType属性为fitCenter,然后在ImageVIew的setOnTouchListener()方法之前设置setScaleType(ScaleType.MATRIX); 但是这种方法我没有成功,图片初始还是在左上角;另一种方法,就是先计算手机屏幕的高和宽,求出使图片居中的点(x,y),然后通过Matrix的平移到该位置,最后通过setImageMatrix()为ImageView绑定该Matrix,以实现图片初始居中显示。而我最后采用的也就是第二种方法,代码如下:

private DisplayMetrics dm;  

... ... 

dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm); //获取分辨率  

... ...

//设置居中显示
savedMatrix.setTranslate((dm.widthPixels - bm.getWidth())/2 , (dm.heightPixels - bm.getHeight()) / 2);
img.setImageMatrix(savedMatrix);

如果大家有更好的办法,能使图片初始时居中,请分享下。

时间: 2024-10-17 11:27:08

android项目 之 记事本(14) ----- 手势缩放与拖拽图片的相关文章

【Android__WebView】多点触摸实现缩放和拖拽图片

http://www.4byte.cn/question/634906/android-webview-can-t-be-scrolled-after-being-set-to-a-touch-listener.html 在MotionEvent中有两个事件值得注意一下,分别是ACTION_DOWN和ACTION_POINTER_DOWN. ACTION_DOWN:这个很好理解,当屏幕检测到有手指按下之后就触发到这个事件. ACTION_POINTER_DOWN:这个是实现多点的关键,当屏幕检测

Android 自定义ImageView支持缩放,拖拽,方便复用

今天刚发了一篇关于ImageView的缩放和拖拽的博客,然后我想了下,将他自定义下,方便我们来复用这个imageView,效果我就不多说了,http://blog.csdn.net/xiaanming/article/details/8827257就是这个效果,我只是把他抽出来自定义了下,代码还是贴上吧,我也将demo上传一下,有疑问大家指出来,大家共同学习,共同进步,呵呵 [java] view plaincopy package com.example.myimageview; import

IOS开发UI篇—手势识别器(拖拽+旋转+缩放)

IOS开发UI篇—手势识别器(拖拽+旋转+缩放) 一.拖拽 示例代码: 1 // 2 // YYViewController.m 3 // 06-拖拽事件 4 // 5 // Created by apple on 14-6-19. 6 // Copyright (c) 2014年 itcase. All rights reserved. 7 // 8 9 #import "YYViewController.h" 10 11 @interface YYViewController ()

猫猫学IOS(三十六)UI之手势事件旋转_缩放_拖拽

猫猫分享,必须精品 原创文章,欢迎转载.转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243?viewmode=contents 源码:http://blog.csdn.net/u013357243/article/details/45560213 效果 完成一个图片的捏合缩放,拖拽,旋转动作. 设计思路 拖拽: 首先是最简单的拖拽 //拖拽 -(void)panTest { UIPanGestureRecognizer *pan = [[UIPanGe

(素材源码)猫猫学IOS(三十六)UI之手势事件旋转_缩放_拖拽

猫猫分享,必须精品 原创文章,欢迎转载.转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243?viewmode=contents 源码:http://download.csdn.net/detail/u013357243/8671943 效果 完成一个图片的捏合缩放,拖拽,旋转动作. 代码:NYViewController.m // // NYViewController.m // 旋转_缩放_拖拽 // // Created by apple on 1

在viewPager中双指缩放图片,双击缩放图片,单指拖拽图片

我们就把这个问题叫做图片查看器吧,它的主要功能有: 1.双击缩放图片. 2. 双指缩放图片. 3.单指拖拽图片. 为此这个图片查看器需要考虑以下的技术点: 一.双击缩放图片: 1.如果图片高度比屏幕的高度小得多,那么就将图片放大到高度与屏幕高度相等,否则就放大一个特定的倍数. 2.如何判断是否到达这个倍数来停止缩放. 3.判断完且停止放大后,图片可能已经超出了这个倍数需要的大小,如何回归到我们的目标大小. 4.判断完且停止缩小后,图片宽度可能已经小于屏幕宽度,在两边留下了空白,如何重置为原来的大

android 拖拽图片&amp;拖动浮动按钮到处跑

来自老外: import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.os.Bundle; import android.view.MotionEvent ; import android.widget.AbsoluteLayout; import android.widget.Button; public class Drag_And_D

Android官方开发文档Training系列课程中文版:手势处理之拖拽或缩放

原文地址:https://developer.android.com/training/gestures/scale.html 这节课主要学习如何使用触摸手势来拖动.放大屏幕上的对象. 拖动对象 如果你的重点在Android 3.0以上的版本,那么你可以使用内置的拖拽事件监听器View.OnDragListener. 触摸手势最常见的操作就是使用它来拖动屏幕上的对象.下面的代码允许用户拖动屏幕上的图像.要注意以下几点: 在拖动操作中,APP会一直保持手指拖动的轨迹,就算是另一只手指触到屏幕也是.

Android自定义控件:类QQ未读消息拖拽效果

QQ的未读消息,算是一个比较好玩的效果,趁着最近时间比较多,参考了网上的一些资料之后,本次实现一个仿照QQ未读消息的拖拽小红点,最终完成效果如下: 首先我们从最基本的原理开始分析,看一张图: 这个图该怎么绘制呢?实际上我们这里是先绘制两个圆,然后将两个圆的切点通过贝塞尔曲线连接起来就达到这个效果了.至于贝塞尔曲线的概念,这里就不多做解释了,百度一下就知道了. 切点怎么算呢,这里我们稍微复习一些初中的数学知识.看了这个图之后,求出四个切点应该是轻而易举了. 现在思路已经很清晰了,按照我们的思路,开