Android实现手势滑动多点触摸缩放平移图片效果

现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位。
一、概述
想要做到图片支持多点触控,自由的进行缩放、平移,需要了解几个知识点: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
嗯,有点像继承来的,其实不是的,独立的一个类~用于检测缩放的手势~~~用法会在例子中
二、实战
为了大家更好的理解,我会独立出每个功能,最后再整合到一起~~也方面大家对每个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一起使用时的事件冲突问题

以上就是本文的全部内容,希望对大家学习Android软件编程有所帮助。

(0)

相关推荐

  • android 图片操作(缩放移动) 实例代码

    view_show.xml 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?><LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"  android:orientation="vertical"  android:layout_width="match_par

  • Android开发实现图片平移、缩放、倒影及旋转功能的方法

    本文实例讲述了Android开发实现图片平移.缩放.倒影及旋转功能的方法.分享给大家供大家参考,具体如下: 解析: 1)根据原来的图片创建新的图片 Bitmap modBm = Bitmap.createBitmap(bm.getWidth()+20, bm.getHeight()+20, bm.getConfig()); 2)设置到画布 Canvas canvas = new Canvas(modBm); 3)使用矩阵进行平移- Matrix matrix = new Matrix(); ma

  • Android单点触控实现图片平移、缩放、旋转功能

    相信大家使用多点对图片进行缩放,平移的操作很熟悉了,大部分大图的浏览都具有此功能,有些app还可以对图片进行旋转操作,QQ的大图浏览就可以对图片进行旋转操作,大家都知道对图片进行缩放,平移,旋转等操作可以使用Matrix来实现,Matrix就是一个3X3的矩阵,对图片的处理可分为四个基础变换操作,Translate(平移变换).Rotate(旋转变换).Scale (缩放变换).Skew(错切变换),如果大家对Matrix不太了解的话可以看看这篇文章(点击查看),作者对每一种Matrix的变换写

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

    文章将在原有基础之上做了一些扩展功能: 1.图片的惯性滑动 2.图片缩放小于正常比例时,松手会自动回弹成正常比例 3.图片缩放大于最大比例时,松手会自动回弹成最大比例 实现图片的缩放,平移,双击缩放等基本功能的代码如下,每一行代码我都做了详细的注释 public class ZoomImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener , V

  • Android 图片缩放与旋转的实现详解

    本文使用Matrix实现Android实现图片缩放与旋转.示例代码如下: 复制代码 代码如下: package com.android.matrix;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Matrix;import android.graphics.drawable.BitmapDrawable

  • Android实现对图片放大、平移和旋转的功能

    先来看看要实现的效果图 在讲解中,需要大家提前了解一些关于图片绘制的原理的相关知识. 关于实现的流程 1.自定义View 2.获得操作图片的Bitmap 3.复写View的onTouchEvent()方法中的ACTION_DOWN,ACTION_POINTER_DOWN,ACTION_MOVE,ACTION_POINTER_UP以及ACTION_UP事件. 4.定义相应图片变化的Matrix矩阵,通过手势操作的变化来设置相应的Matrix. 5.完成最终的Matrix设置时,通过invalida

  • 基于Android 实现图片平移、缩放、旋转同时进行

    前言 之前因为项目需求,其中使用到了图片的单击显示取消,图片平移缩放功能,昨天突然想再加上图片的旋转功能,在网上看了很多相关的例子,可是没看到能同时实现我想要的功能的. 需求: (1)图片平移.缩放.旋转等一系列操作后,图片需要自动居中显示. (2)图片旋转后选自动水平显示或者垂直显示 (3)图片在放大缩小的同时都能旋转 Demo实现部分效果截图 Demo主要代码 Java MainActivity.java package com.practice.noyet.rotatezoomimagev

  • Android编程实现图片的浏览、缩放、拖动和自动居中效果

    本文实例讲述了Android编程实现图片的浏览.缩放.拖动和自动居中效果的方法.分享给大家供大家参考,具体如下: Touch.java /** * 图片浏览.缩放.拖动.自动居中 */ public class Touch extends Activity implements OnTouchListener { Matrix matrix = new Matrix(); Matrix savedMatrix = new Matrix(); DisplayMetrics dm; ImageVie

  • Android中利用matrix 控制图片的旋转、缩放、移动

    本文主要讲解利用android中Matrix控制图形的旋转缩放移动,具体参见一下代码: 复制代码 代码如下: /**  * 使用矩阵控制图片移动.缩放.旋转  */  public class CommonImgEffectView extends View { private Context context ;      private Bitmap mainBmp , controlBmp ;      private int mainBmpWidth , mainBmpHeight , c

  • Android实现动态向Gallery中添加图片及倒影与3D效果示例

    本文实例讲述了Android实现动态向Gallery中添加图片及倒影与3D效果的方法.分享给大家供大家参考,具体如下: 在Android中gallery可以提供一个很好的显示图片的方式,实现上面的效果以及动态添加数据库或者网络上下载下来的图片资源.我们首先实现一个自定义的Gallery类. MyGallery.java: package nate.android.Service; import android.content.Context; import android.graphics.Ca

  • Android实现手势滑动多点触摸缩放平移图片效果(二)

    上一篇已经带大家实现了自由的放大缩小图片,简单介绍了下Matrix:具体请参考:Android实现手势滑动多点触摸缩放平移图片效果,本篇继续完善我们的ImageView. 首先加入放大后的移动. 1.自由的进行移动 我们在onTouchEvent里面,加上移动的代码,当然了,必须长或宽大于屏幕才可以移动~~~ @Override public boolean onTouch(View v, MotionEvent event) { mScaleGestureDetector.onTouchEve

随机推荐