• Android 手势检测实战 打造支持缩放平移的图片预览效果(上)


    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39474553,本文出自:【张鸿洋的博客】

    1、背景

    现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位~~~

    我相信看图的整个步骤,大家或者说用户应该不希望被打断把~~~“我擦,竟然不能放大,什么玩意,卸了~~“ , "我擦,竟然不能移动,留有何用,卸了~~"。

    哈~所以对于图片的预览,一来,我们要让用户爽;二来,我们作为开发者,也得知道如何实现~~~

    2、概述

    想要做到图片支持多点触控,自由的进行缩放、平移,需要了解几个知识点:Matrix , GestureDetector , ScaleGestureDetector 以及事件分发机制,ps:不会咋办,不会你懂的。

    1、Matrix

    矩阵,看深入了都是3维矩阵的乘啊什么的,怪麻烦的~~

    其实这么了解下就行了:

    Matrix

    数据结构:3维矩阵;

    内部存储:new Float[9] ; 内部就是个一维数组,内部9个元素;可以进行setValues(float[] values)进行初始化

    每个元素代表的意思

    {
    		MSCALE_X, MSKEW_X, MTRANS_X,  
                    MSKEW_Y, MSCALE_Y, MTRANS_Y,  
                    MPERSP_0, MPERSP_1, MPERSP_2  
    };  

    字面上,应该能看出来哪个代表x方向缩放,哪个代表垂直方向的偏移量吧~~有不认识的3个,没事,请无视。

    操作

    比如你想要设置matrix的偏移量为200,100

    你可以这么写:

    Matrix transMatrix = new Matrix();
    		float[] values = new float[] { 1.0, 0, 200, 0, 1.0, 100, 0, 0, 1.0 };
    		transMatrix.setValues(values);
    如果需要在旋转30度,放大两倍~~

    这么写其实怪麻烦的~~

    Matrix提供了一些常用的API:例如我们可以这么写:

    Matrix transMatrix = new Matrix();
    		transMatrix.postTranslate(200, 100);

    如何获取值

    当然了,我们对一个Matrix进行了各种操作,一会postScale,一会postTranslate;那么现在如何获得当前的缩放比例:
    前面说setValues可以初始化,那么getValues就能拿到当前矩阵的值,拿到的是个一维数组,9个元素;再通过下标取对应值就可以。

    比如我想知道现在x方向缩放比例:

    public final float getScale()
    	{
    		scaleMatrix.getValues(matrixValues);
    		return matrixValues[Matrix.MSCALE_X];
    	}

    好了,知道这些就够了~~

    2、GestureDetector 

    嗯,自己看API,能够捕捉到长按、双击什么的;用法会在例子中

    3、ScaleGestureDetector 

    嗯,有点像继承来的,其实不是的,独立的一个类~用于检测缩放的手势~~~用法会在例子中

    3、实战

    为了大家更好的理解,我会独立出每个功能,最后再整合到一起~~也方面大家对每个API的使用的学习。

    1、自由的缩放

    需求:当图片加载时,将图片在屏幕中居中;图片宽或高大于屏幕的,缩小至屏幕大小;自由对图片进行方法或缩小;

    代码不是很长,直接贴代码了:

    package com.zhy.view;
    
    import android.content.Context;
    import android.graphics.Matrix;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import android.view.ScaleGestureDetector.OnScaleGestureListener;
    import android.view.View;
    import android.view.View.OnTouchListener;
    import android.view.ViewTreeObserver;
    import android.widget.ImageView;
    
    public class ZoomImageView extends ImageView implements OnScaleGestureListener,
    		OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener
    
    {
    	private static final String TAG = ZoomImageView.class.getSimpleName();
    	
    	public static final float SCALE_MAX = 4.0f;
    	/**
    	 * 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小于0
    	 */
    	private float initScale = 1.0f;
    
    	/**
    	 * 用于存放矩阵的9个值
    	 */
    	private final float[] matrixValues = new float[9];
    
    	private boolean once = true;
    
    	/**
    	 * 缩放的手势检测
    	 */
    	private ScaleGestureDetector mScaleGestureDetector = null;
    
    	private final Matrix mScaleMatrix = new Matrix();
    
    	public ZoomImageView(Context context)
    	{
    		this(context, null);
    	}
    
    	public ZoomImageView(Context context, AttributeSet attrs)
    	{
    		super(context, attrs);
    		super.setScaleType(ScaleType.MATRIX);
    		mScaleGestureDetector = new ScaleGestureDetector(context, this);
    		this.setOnTouchListener(this);
    	}
    
    	@Override
    	public boolean onScale(ScaleGestureDetector detector)
    	{
    		float scale = getScale();
    		float scaleFactor = detector.getScaleFactor();
    
    		if (getDrawable() == null)
    			return true;
    
    		/**
    		 * 缩放的范围控制
    		 */
    		if ((scale < SCALE_MAX && scaleFactor > 1.0f)
    				|| (scale > initScale && scaleFactor < 1.0f))
    		{
    			/**
    			 * 最大值最小值判断
    			 */
    			if (scaleFactor * scale < initScale)
    			{
    				scaleFactor = initScale / scale;
    			}
    			if (scaleFactor * scale > SCALE_MAX)
    			{
    				scaleFactor = SCALE_MAX / scale;
    			}
    			/**
    			 * 设置缩放比例
    			 */
    			mScaleMatrix.postScale(scaleFactor, scaleFactor, getWidth() / 2,
    					getHeight() / 2);
    			setImageMatrix(mScaleMatrix);
    		}
    		return true;
    
    	}
    
    	@Override
    	public boolean onScaleBegin(ScaleGestureDetector detector)
    	{
    		return true;
    	}
    
    	@Override
    	public void onScaleEnd(ScaleGestureDetector detector)
    	{
    	}
    
    	@Override
    	public boolean onTouch(View v, MotionEvent event)
    	{
    		return mScaleGestureDetector.onTouchEvent(event);
    
    	}
    
    	
    	/**
    	 * 获得当前的缩放比例
    	 * 
    	 * @return
    	 */
    	public final float getScale()
    	{
    		mScaleMatrix.getValues(matrixValues);
    		return matrixValues[Matrix.MSCALE_X];
    	}
    
    	@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 (once)
    		{
    			Drawable d = getDrawable();
    			if (d == null)
    				return;
    			Log.e(TAG, d.getIntrinsicWidth() + " , " + d.getIntrinsicHeight());
    			int width = getWidth();
    			int height = getHeight();
    			// 拿到图片的宽和高
    			int dw = d.getIntrinsicWidth();
    			int dh = d.getIntrinsicHeight();
    			float scale = 1.0f;
    			// 如果图片的宽或者高大于屏幕,则缩放至屏幕的宽或者高
    			if (dw > width && dh <= height)
    			{
    				scale = width * 1.0f / dw;
    			}
    			if (dh > height && dw <= width)
    			{
    				scale = height * 1.0f / dh;
    			}
    			// 如果宽和高都大于屏幕,则让其按按比例适应屏幕大小
    			if (dw > width && dh > height)
    			{
    				scale = Math.min(dw * 1.0f / width, dh * 1.0f / height);
    			}
    			initScale = scale;
    			// 图片移动至屏幕中心
    						mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);
    			mScaleMatrix
    					.postScale(scale, scale, getWidth() / 2, getHeight() / 2);
    			setImageMatrix(mScaleMatrix);
    			once = false;
    		}
    
    	}
    
    }

    1、我们在onGlobalLayout的回调中,根据图片的宽和高以及屏幕的宽和高,对图片进行缩放以及移动至屏幕的中心。如果图片很小,那就正常显示,不放大了~

    2、我们让OnTouchListener的MotionEvent交给ScaleGestureDetector进行处理

    @Override
    	public boolean onTouch(View v, MotionEvent event)
    	{
    		return mScaleGestureDetector.onTouchEvent(event);
    
    	}

    3、在onScale的回调中对图片进行缩放的控制,首先进行缩放范围的判断,然后设置mScaleMatrix的scale值

    现在的效果:

    1、小于屏幕的宽和高


    2、大于屏幕的宽和高


    真机录的效果不太好~~凑合看~

    现在已经能够~~~随意的放大缩小了~~~

    源码点击下载

    可是,可是,存在问题:

    1、缩放的中心点,我们设置是固定的,屏幕中间

    2、放大后,无法移动~

    下面,我们先解决缩放的中心点问题,不能一直按屏幕中心么,像我这样的,我比较关注妹子的眼睛,我要放大那一块~~~

    2、设置缩放中心

    1、单纯的设置缩放中心

    仅仅是设置中心很简单,直接修改下中心点 :

    /**
    			 * 设置缩放比例
    			 */
    			mScaleMatrix.postScale(scaleFactor, scaleFactor,
    					detector.getFocusX(), detector.getFocusX());
    			setImageMatrix(mScaleMatrix);

    但是,随意的中心点放大、缩小,会导致图片的位置的变化,最终导致,图片宽高大于屏幕时,图片与屏幕间出现白边;图片小于屏幕,但是不居中。

    2、控制缩放时图片显示的范围

    所以我们在缩放的时候需要手动控制下范围:

    /**
    	 * 在缩放时,进行图片显示范围的控制
    	 */
    	private void checkBorderAndCenterWhenScale()
    	{
    
    		RectF rect = getMatrixRectF();
    		float deltaX = 0;
    		float deltaY = 0;
    
    		int width = getWidth();
    		int height = getHeight();
    
    		// 如果宽或高大于屏幕,则控制范围
    		if (rect.width() >= width)
    		{
    			if (rect.left > 0)
    			{
    				deltaX = -rect.left;
    			}
    			if (rect.right < width)
    			{
    				deltaX = width - rect.right;
    			}
    		}
    		if (rect.height() >= height)
    		{
    			if (rect.top > 0)
    			{
    				deltaY = -rect.top;
    			}
    			if (rect.bottom < height)
    			{
    				deltaY = height - rect.bottom;
    			}
    		}
    		// 如果宽或高小于屏幕,则让其居中
    		if (rect.width() < width)
    		{
    			deltaX = width * 0.5f - rect.right + 0.5f * rect.width();
    		}
    		if (rect.height() < height)
    		{
    			deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();
    		}
    		Log.e(TAG, "deltaX = " + deltaX + " , deltaY = " + deltaY);
    
    		mScaleMatrix.postTranslate(deltaX, deltaY);
    
    	}
    
    	/**
    	 * 根据当前图片的Matrix获得图片的范围
    	 * 
    	 * @return
    	 */
    	private RectF getMatrixRectF()
    	{
    		Matrix matrix = mScaleMatrix;
    		RectF rect = new RectF();
    		Drawable d = getDrawable();
    		if (null != d)
    		{
    			rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
    			matrix.mapRect(rect);
    		}
    		return rect;
    	}

    在onScale里面记得调用:

    /**
    			 * 设置缩放比例
    			 */
    			mScaleMatrix.postScale(scaleFactor, scaleFactor,
    					detector.getFocusX(), detector.getFocusY());
    			checkBorderAndCenterWhenScale();
    			setImageMatrix(mScaleMatrix);

    这样就好了,可以自由的放大任何地方,并且不会出现边界出现白边,也能很好的让图片显示在屏幕中间(当图片宽或高小于屏幕);

    3、贴下布局文件

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <com.zhy.view.ZoomImageView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:scaleType="matrix"
            android:src="@drawable/xx" />
    
    </RelativeLayout>


    眼睛是心灵的窗户,咱们来放大看看,效果图:



    好了,到此我们的图片随意的方法缩小~~~已经完成了~~~如果只需要缩放功能的,就可以拿去用了~

    由于篇幅原因,下一篇将继续完善此View:

    1、增加多点触控时移动

    2、增加双击变大,双击变小

    3、与ViewPager一起使用时的事件冲突问题


    源码点击下载 (点击联通下载,部分选择电信无法下载)


    ----------------------------------------------------------------------------------------------------------

    博主部分视频已经上线,如果你不喜欢枯燥的文本,请猛戳(初录,期待您的支持):

    1、高仿微信5.2.1主界面及消息提醒

    2、高仿QQ5.0侧滑





    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    Python导学基础(三)输入、格式化输出、基本运算符
    题解-FJOI2014 树的重心
    题解-CF1307G Cow and Exercise
    题解-SHOI2005 树的双中心
    笔记-CF643E Bear and Destroying Subtrees
    题解-CF643G Choosing Ads
    扩展Lucas
    线性筛筛积性函数
    整除分块(数论)
    2019暑假集训DAY17(problem2.b)(杜教筛)
  • 原文地址:https://www.cnblogs.com/dingxiaoyue/p/4924885.html
Copyright © 2020-2023  润新知