文章将在原有基础之上做了一些扩展功能:

1.图片的惯性滑动

2.图片缩放小于正常比例时,松手会自动回弹成正常比例

3.图片缩放大于最大比例时,松手会自动回弹成最大比例

实现图片的缩放,平移,双击缩放等基本功能的代码如下,每一行代码我都做了详细的注释

public class ZoomImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,

View.OnTouchListener , ViewTreeObserver.OnGlobalLayoutListener{

/**

* 缩放手势的监测

*/

private ScaleGestureDetector mScaleGestureDetector;

/**

* 监听手势

*/

private GestureDetector mGestureDetector;

/**

* 对图片进行缩放平移的Matrix

*/

private Matrix mScaleMatrix;

/**

* 第一次加载图片时调整图片缩放比例,使图片的宽或者高充满屏幕

*/

private boolean mFirst;

/**

* 图片的初始化比例

*/

private float mInitScale;

/**

* 图片的最大比例

*/

private float mMaxScale;

/**

* 双击图片放大的比例

*/

private float mMidScale;

/**

* 是否正在自动放大或者缩小

*/

private boolean isAutoScale;

//-----------------------------------------------

/**

* 上一次触控点的数量

*/

private int mLastPointerCount;

/**

* 是否可以拖动

*/

private boolean isCanDrag;

/**

* 上一次滑动的x和y坐标

*/

private float mLastX;

private float mLastY;

/**

* 可滑动的临界值

*/

private int mTouchSlop;

/**

* 是否用检查左右边界

*/

private boolean isCheckLeftAndRight;

/**

* 是否用检查上下边界

*/

private boolean isCheckTopAndBottom;

public ZoomImageView(Context context) {

this(context, null, 0);

}

public ZoomImageView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

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

super(context, attrs, defStyleAttr);

//一定要将图片的ScaleType设置成Matrix类型的

setScaleType(ScaleType.MATRIX);

//初始化缩放手势监听器

mScaleGestureDetector = new ScaleGestureDetector(context,this);

//初始化矩阵

mScaleMatrix = new Matrix();

setOnTouchListener(this);

mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

//初始化手势检测器,监听双击事件

mGestureDetector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener(){

@Override

public boolean onDoubleTap(MotionEvent e) {

//如果是正在自动缩放,则直接返回,不进行处理

if (isAutoScale) return true;

//得到点击的坐标

float x = e.getX();

float y = e.getY();

//如果当前图片的缩放值小于指定的双击缩放值

if (getScale() < mMidScale){

//进行自动放大

post(new AutoScaleRunnable(mMidScale,x,y));

}else{

//当前图片的缩放值大于初试缩放值,则自动缩小

post(new AutoScaleRunnable(mInitScale,x,y));

}

return true;

}

});

}

/**

* 当view添加到window时调用,早于onGlobalLayout,因此可以在这里注册监听器

*/

@Override

protected void onAttachedToWindow() {

super.onAttachedToWindow();

getViewTreeObserver().addOnGlobalLayoutListener(this);

}

/**

* 当view从window上移除时调用,因此可以在这里移除监听器

*/

@Override

protected void onDetachedFromWindow() {

super.onDetachedFromWindow();

getViewTreeObserver().removeGlobalOnLayoutListener(this);

}

/**

* 当布局树发生变化时会调用此方法,我们可以在此方法中获得控件的宽和高

*/

@Override

public void onGlobalLayout() {

//只有当第一次加载图片的时候才会进行初始化,用一个变量mFirst控制

if (!mFirst){

mFirst = true;

//得到控件的宽和高

int width = getWidth();

int height = getHeight();

//得到当前ImageView中加载的图片

Drawable d = getDrawable();

if(d == null){//如果没有图片,则直接返回

return;

}

//得到当前图片的宽和高,图片的宽和高不一定等于控件的宽和高

//因此我们需要将图片的宽和高与控件宽和高进行判断

//将图片完整的显示在屏幕中

int dw = d.getIntrinsicWidth();

int dh = d.getIntrinsicHeight();

//我们定义一个临时变量,根据图片与控件的宽高比例,来确定这个最终缩放值

float scale = 1.0f;

//如果图片宽度大于控件宽度,图片高度小于控件高度

if (dw>width && dh

//我们需要将图片宽度缩小,缩小至控件的宽度

//至于为什么要这样计算,我们可以这样想

//我们调用matrix.postScale(scale,scale)时,宽和高都要乘以scale的

//当前我们的图片宽度是dw,dw*scale=dw*(width/dw)=width,这样就等于控件宽度了

//我们的高度同时也乘以scale,这样能够保证图片的宽高比不改变,图片不变形

scale = width * 1.0f / dw;

}

//如果图片的宽度小于控件宽度,图片高度大于控件高度

if (dwheight){

//我们就应该将图片的高度缩小,缩小至控件的高度,计算方法同上

scale = height * 1.0f / dh;

}

//如果图片的宽度小于控件宽度,高度小于控件高度时,我们应该将图片放大

//比如图片宽度是控件宽度的1/2 ,图片高度是控件高度的1/4

//如果我们将图片放大4倍,则图片的高度是和控件高度一样了,但是图片宽度就超出控件宽度了

//因此我们应该选择一个最小值,那就是将图片放大2倍,此时图片宽度等于控件宽度

//同理,如果图片宽度大于控件宽度,图片高度大于控件高度,我们应该将图片缩小

//缩小的倍数也应该为那个最小值

if ((dw < width && dh < height) || (dw > width && dh > height)){

scale = Math.min(width * 1.0f / dw , height * 1.0f / dh);

}

//我们还应该对图片进行平移操作,将图片移动到屏幕的居中位置

//控件宽度的一半减去图片宽度的一半即为图片需要水平移动的距离

//高度同理,大家可以画个图看一看

int dx = width/2 - dw/2;

int dy = height/2 - dh/2;

//对图片进行平移,dx和dy分别表示水平和竖直移动的距离

mScaleMatrix.postTranslate(dx, dy);

//对图片进行缩放,scale为缩放的比例,后两个参数为缩放的中心点

mScaleMatrix.postScale(scale, scale, width / 2, height / 2);

//将矩阵作用于我们的图片上,图片真正得到了平移和缩放

setImageMatrix(mScaleMatrix);

//初始化一下我们的几个缩放的边界值

mInitScale = scale;

//最大比例为初始比例的4倍

mMaxScale = mInitScale * 4;

//双击放大比例为初始化比例的2倍

mMidScale = mInitScale * 2;

}

}

/**

* 获得图片当前的缩放比例值

*/

private float getScale(){

//Matrix为一个3*3的矩阵,一共9个值

float[] values = new float[9];

//将Matrix的9个值映射到values数组中

mScaleMatrix.getValues(values);

//拿到Matrix中的MSCALE_X的值,这个值为图片宽度的缩放比例,因为图片高度

//的缩放比例和宽度的缩放比例一致,我们取一个就可以了

//我们还可以 return values[Matrix.MSCALE_Y];

return values[Matrix.MSCALE_X];

}

/**

* 获得缩放后图片的上下左右坐标以及宽高

*/

private RectF getMatrixRectF(){

//获得当钱图片的矩阵

Matrix matrix = mScaleMatrix;

//创建一个浮点类型的矩形

RectF rectF = new RectF();

//得到当前的图片

Drawable d = getDrawable();

if (d != null){

//使这个矩形的宽和高同当前图片一致

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

//将矩阵映射到矩形上面,之后我们可以通过获取到矩阵的上下左右坐标以及宽高

//来得到缩放后图片的上下左右坐标和宽高

matrix.mapRect(rectF);

}

return rectF;

}

/**

* 当缩放时检查边界并且使图片居中

*/

private void checkBorderAndCenterWhenScale(){

if (getDrawable() == null){

return;

}

//初始化水平和竖直方向的偏移量

float deltaX = 0.0f;

float deltaY = 0.0f;

//得到控件的宽和高

int width = getWidth();

int height = getHeight();

//拿到当前图片对应的矩阵

RectF rectF = getMatrixRectF();

//如果当前图片的宽度大于控件宽度,当前图片处于放大状态

if (rectF.width() >= width){

//如果图片左边坐标是大于0的,说明图片左边离控件左边有一定距离,

//左边会出现一个小白边

if (rectF.left > 0){

//我们将图片向左边移动

deltaX = -rectF.left;

}

//如果图片右边坐标小于控件宽度,说明图片右边离控件右边有一定距离,

//右边会出现一个小白边

if (rectF.right

//我们将图片向右边移动

deltaX = width - rectF.right;

}

}

//上面是调整宽度,这是调整高度

if (rectF.height() >= height){

//如果上面出现小白边,则向上移动

if (rectF.top > 0){

deltaY = -rectF.top;

}

//如果下面出现小白边,则向下移动

if (rectF.bottom < height){

deltaY = height - rectF.bottom;

}

}

//如果图片的宽度小于控件的宽度,我们要对图片做一个水平的居中

if (rectF.width() < width){

deltaX = width/2f - rectF.right + rectF.width()/2f;

}

//如果图片的高度小于控件的高度,我们要对图片做一个竖直方向的居中

if (rectF.height() < height){

deltaY = height/2f - rectF.bottom + rectF.height()/2f;

}

//将平移的偏移量作用到矩阵上

mScaleMatrix.postTranslate(deltaX, deltaY);

}

/**

* 平移时检查上下左右边界

*/

private void checkBorderWhenTranslate() {

//获得缩放后图片的相应矩形

RectF rectF = getMatrixRectF();

//初始化水平和竖直方向的偏移量

float deltaX = 0.0f;

float deltaY = 0.0f;

//得到控件的宽度

int width = getWidth();

//得到控件的高度

int height = getHeight();

//如果是需要检查左和右边界

if (isCheckLeftAndRight){

//如果左边出现的白边

if (rectF.left > 0){

//向左偏移

deltaX = -rectF.left;

}

//如果右边出现的白边

if (rectF.right < width){

//向右偏移

deltaX = width - rectF.right;

}

}

//如果是需要检查上和下边界

if (isCheckTopAndBottom){

//如果上面出现白边

if (rectF.top > 0){

//向上偏移

deltaY = -rectF.top;

}

//如果下面出现白边

if (rectF.bottom < height){

//向下偏移

deltaY = height - rectF.bottom;

}

}

mScaleMatrix.postTranslate(deltaX,deltaY);

}

/**

* 自动放大缩小,自动缩放的原理是使用View.postDelay()方法,每隔16ms调用一次

* run方法,给人视觉上形成一种动画的效果

*/

private class AutoScaleRunnable implements Runnable{

//放大或者缩小的目标比例

private float mTargetScale;

//可能是BIGGER,也可能是SMALLER

private float tempScale;

//放大缩小的中心点

private float x;

private float y;

//比1稍微大一点,用于放大

private final float BIGGER = 1.07f;

//比1稍微小一点,用于缩小

private final float SMALLER = 0.93f;

//构造方法,将目标比例,缩放中心点传入,并且判断是要放大还是缩小

public AutoScaleRunnable(float targetScale , float x , float y){

this.mTargetScale = targetScale;

this.x = x;

this.y = y;

//如果当前缩放比例小于目标比例,说明要自动放大

if (getScale() < mTargetScale){

//设置为Bigger

tempScale = BIGGER;

}

//如果当前缩放比例大于目标比例,说明要自动缩小

if (getScale() > mTargetScale){

//设置为Smaller

tempScale = SMALLER;

}

}

@Override

public void run() {

//这里缩放的比例非常小,只是稍微比1大一点或者比1小一点的倍数

//但是当每16ms都放大或者缩小一点点的时候,动画效果就出来了

mScaleMatrix.postScale(tempScale, tempScale, x, y);

//每次将矩阵作用到图片之前,都检查一下边界

checkBorderAndCenterWhenScale();

//将矩阵作用到图片上

setImageMatrix(mScaleMatrix);

//得到当前图片的缩放值

float currentScale = getScale();

//如果当前想要放大,并且当前缩放值小于目标缩放值

//或者 当前想要缩小,并且当前缩放值大于目标缩放值

if ((tempScale > 1.0f) && currentScale < mTargetScale

||(tempScale < 1.0f) && currentScale > mTargetScale){

//每隔16ms就调用一次run方法

postDelayed(this,16);

}else {

//current*scale=current*(mTargetScale/currentScale)=mTargetScale

//保证图片最终的缩放值和目标缩放值一致

float scale = mTargetScale / currentScale;

mScaleMatrix.postScale(scale, scale, x, y);

checkBorderAndCenterWhenScale();

setImageMatrix(mScaleMatrix);

//自动缩放结束,置为false

isAutoScale = false;

}

}

}

/**

* 这个是OnScaleGestureListener中的方法,在这个方法中我们可以对图片进行放大缩小

*/

@Override

public boolean onScale(ScaleGestureDetector detector) {

//当我们两个手指进行分开操作时,说明我们想要放大,这个scaleFactor是一个稍微大于1的数值

//当我们两个手指进行闭合操作时,说明我们想要缩小,这个scaleFactor是一个稍微小于1的数值

float scaleFactor = detector.getScaleFactor();

//获得我们图片当前的缩放值

float scale = getScale();

//如果当前没有图片,则直接返回

if (getDrawable() == null){

return true;

}

//如果scaleFactor大于1,说明想放大,当前的缩放比例乘以scaleFactor之后小于

//最大的缩放比例时,允许放大

//如果scaleFactor小于1,说明想缩小,当前的缩放比例乘以scaleFactor之后大于

//最小的缩放比例时,允许缩小

if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxScale)

|| scaleFactor < 1.0f && scale * scaleFactor > mInitScale){

//边界控制,如果当前缩放比例乘以scaleFactor之后大于了最大的缩放比例

if (scale * scaleFactor > mMaxScale + 0.01f){

//则将scaleFactor设置成mMaxScale/scale

//当再进行matrix.postScale时

//scale*scaleFactor=scale*(mMaxScale/scale)=mMaxScale

//最后图片就会放大至mMaxScale缩放比例的大小

scaleFactor = mMaxScale / scale;

}

//边界控制,如果当前缩放比例乘以scaleFactor之后小于了最小的缩放比例

//我们不允许再缩小

if (scale * scaleFactor < mInitScale + 0.01f){

//计算方法同上

scaleFactor = mInitScale / scale;

}

//前两个参数是缩放的比例,是一个稍微大于1或者稍微小于1的数,形成一个随着手指放大

//或者缩小的效果

//detector.getFocusX()和detector.getFocusY()得到的是多点触控的中点

//这样就能实现我们在图片的某一处局部放大的效果

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

//因为图片的缩放点不是图片的中心点了,所以图片会出现偏移的现象,所以进行一次边界的检查和居中操作

checkBorderAndCenterWhenScale();

//将矩阵作用到图片上

setImageMatrix(mScaleMatrix);

}

return true;

}

/**

* 一定要返回true

*/

@Override

public boolean onScaleBegin(ScaleGestureDetector detector) {

return true;

}

@Override

public void onScaleEnd(ScaleGestureDetector detector) {

}

@Override

public boolean onTouch(View v, MotionEvent event) {

//当双击操作时,不允许移动图片,直接返回true

if (mGestureDetector.onTouchEvent(event)){

return true;

}

//将事件传递给ScaleGestureDetector

mScaleGestureDetector.onTouchEvent(event);

//用于存储多点触控产生的坐标

float x = 0.0f;

float y = 0.0f;

//得到多点触控的个数

int pointerCount = event.getPointerCount();

//将所有触控点的坐标累加起来

for(int i=0 ; i

x += event.getX(i);

y += event.getY(i);

}

//取平均值,得到的就是多点触控后产生的那个点的坐标

x /= pointerCount;

y /= pointerCount;

//如果触控点的数量变了,则置为不可滑动

if (mLastPointerCount != pointerCount){

isCanDrag = false;

mLastX = x;

mLastY = y;

}

mLastPointerCount = pointerCount;

RectF rectF = getMatrixRectF();

switch (event.getAction()){

case MotionEvent.ACTION_DOWN:

isCanDrag = false;

//当图片处于放大状态时,禁止ViewPager拦截事件,将事件传递给图片,进行拖动

if (rectF.width() > getWidth() + 0.01f || rectF.height() > getHeight() + 0.01f){

if (getParent() instanceof ViewPager){

getParent().requestDisallowInterceptTouchEvent(true);

}

}

break;

case MotionEvent.ACTION_MOVE:

//当图片处于放大状态时,禁止ViewPager拦截事件,将事件传递给图片,进行拖动

if (rectF.width() > getWidth() + 0.01f || rectF.height() > getHeight() + 0.01f){

if (getParent() instanceof ViewPager){

getParent().requestDisallowInterceptTouchEvent(true);

}

}

//得到水平和竖直方向的偏移量

float dx = x - mLastX;

float dy = y - mLastY;

//如果当前是不可滑动的状态,判断一下是否是滑动的操作

if (!isCanDrag){

isCanDrag = isMoveAction(dx,dy);

}

//如果可滑动

if (isCanDrag){

if (getDrawable() != null){

isCheckLeftAndRight = true;

isCheckTopAndBottom = true;

//如果图片宽度小于控件宽度

if (rectF.width() < getWidth()){

//左右不可滑动

dx = 0;

//左右不可滑动,也就不用检查左右的边界了

isCheckLeftAndRight = false;

}

//如果图片的高度小于控件的高度

if (rectF.height() < getHeight()){

//上下不可滑动

dy = 0;

//上下不可滑动,也就不用检查上下边界了

isCheckTopAndBottom = false;

}

}

mScaleMatrix.postTranslate(dx,dy);

//当平移时,检查上下左右边界

checkBorderWhenTranslate();

setImageMatrix(mScaleMatrix);

}

mLastX = x;

mLastY = y;

break;

case MotionEvent.ACTION_UP:

//当手指抬起时,将mLastPointerCount置0,停止滑动

mLastPointerCount = 0;

break;

case MotionEvent.ACTION_CANCEL:

break;

}

return true;

}

/**

* 判断是否是移动的操作

*/

private boolean isMoveAction(float dx , float dy){

//勾股定理,判断斜边是否大于可滑动的一个临界值

return Math.sqrt(dx*dx + dy*dy) > mTouchSlop;

}

}

实现图片缩小后,松手回弹的效果

实现这个功能很简单,我们先添加一个mMinScale作为可缩小到的最小值,我们指定为初试比例的1/4

/**

* 最小缩放比例

*/

private float mMinScale;

//在onGlobalLayout中进行初始化

@Override

public void onGlobalLayout() {

...

//最小缩放比例为初试比例的1/4倍

mMinScale = mInitScale / 4;

...

}

//在onScale中,修改如下代码

@Override

public boolean onScale(ScaleGestureDetector detector) {

...

if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxScale)

|| scaleFactor < 1.0f && scale * scaleFactor > mMinScale){

//边界控制,如果当前缩放比例乘以scaleFactor之后小于了最小的缩放比例

//我们不允许再缩小

if (scale * scaleFactor < mMinScale + 0.01f){

scaleFactor = mMinScale / scale;

}

...

}

这样我们的图片最小就可以缩放到初始化比例的1/4大小了,然后我们还需要添加一个松手后回弹至初试化大小的动画效果,然后我们需要在onTouch的ACTION_UP中添加如下代码

@Override

public boolean onTouch(View v, MotionEvent event) {

...

case MotionEvent.ACTION_UP:

//当手指抬起时,将mLastPointerCount置0,停止滑动

mLastPointerCount = 0;

//如果当前图片大小小于初始化大小

if (getScale() < mInitScale){

//自动放大至初始化大小

post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2));

}

break;

...

}

现在我们看一下效果

实现图片放大后,松手回弹效果

这个功能实现起来和上面那个功能基本一致,大家可以先试着自己写一下。

同理,我们需要先定义一个mMaxOverScale作为放大到最大值后,还能继续放大到的值。

/**

* 最大溢出值

*/

private float mMaxOverScale;

//在onGlobalLayout中进行初始化

@Override

public void onGlobalLayout() {

...

//最大溢出值为最大值的5倍,可以随意调

mMaxOverScale = mMaxScale * 5;

...

}

//在onScale中,修改如下代码

@Override

public boolean onScale(ScaleGestureDetector detector) {

...

if ((scaleFactor > 1.0f && scale * scaleFactor < mMaxOverScale)

|| scaleFactor < 1.0f && scale * scaleFactor > mMinScale){

if (scale * scaleFactor > mMaxOverScale + 0.01f){

scaleFactor = mMaxOverScale / scale;

}

...

}

这样当我们图片放大至最大比例后还可以继续放大,然后我们同样需要在onTouch中的ACTION_UP中添加自动缩小的功能

case MotionEvent.ACTION_UP:

//当手指抬起时,将mLastPointerCount置0,停止滑动

mLastPointerCount = 0;

//如果当前图片大小小于初始化大小

if (getScale() < mInitScale){

//自动放大至初始化大小

post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2));

}

//如果当前图片大小大于最大值

if (getScale() > mMaxScale){

//自动缩小至最大值

post(new AutoScaleRunnable(mMaxScale,getWidth()/2,getHeight()/2));

}

break;

然后我们看一下效果

实现图片的惯性滑动

要实现图片的惯性滑动,我们需要借助VelocityTracker来帮我们检测当我们手指离开图片时的一个速度,然后根据这个速度以及图片的位置来调用Scroller的fling方法来计算惯性滑动过程中的x和y的坐标

@Override

public boolean onTouch(View v, MotionEvent event) {

...

switch (event.getAction()){

case MotionEvent.ACTION_DOWN:

//初始化速度检测器

mVelocityTracker = VelocityTracker.obtain();

if (mVelocityTracker != null){

//将当前的事件添加到检测器中

mVelocityTracker.addMovement(event);

}

//当手指再次点击到图片时,停止图片的惯性滑动

if (mFlingRunnable != null){

mFlingRunnable.cancelFling();

mFlingRunnable = null;

}

...

}

...

case MotionEvent.ACTION_MOVE:

...

//如果可滑动

if (isCanDrag){

if (getDrawable() != null){

if (mVelocityTracker != null){

//将当前事件添加到检测器中

mVelocityTracker.addMovement(event);

}

...

}

...

case MotionEvent.ACTION_UP:

//当手指抬起时,将mLastPointerCount置0,停止滑动

mLastPointerCount = 0;

//如果当前图片大小小于初始化大小

if (getScale() < mInitScale){

//自动放大至初始化大小

post(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2));

}

//如果当前图片大小大于最大值

if (getScale() > mMaxScale){

//自动缩小至最大值

post(new AutoScaleRunnable(mMaxScale,getWidth()/2,getHeight()/2));

}

if (isCanDrag){//如果当前可以滑动

if (mVelocityTracker != null){

//将当前事件添加到检测器中

mVelocityTracker.addMovement(event);

//计算当前的速度

mVelocityTracker.computeCurrentVelocity(1000);

//得到当前x方向速度

final float vX = mVelocityTracker.getXVelocity();

//得到当前y方向的速度

final float vY = mVelocityTracker.getYVelocity();

mFlingRunnable = new FlingRunnable(getContext());

//调用fling方法,传入控件宽高和当前x和y轴方向的速度

//这里得到的vX和vY和scroller需要的velocityX和velocityY的负号正好相反

//所以传入一个负值

mFlingRunnable.fling(getWidth(),getHeight(),(int)-vX,(int)-vY);

//执行run方法

post(mFlingRunnable);

}

}

break;

case MotionEvent.ACTION_CANCEL:

//释放速度检测器

if (mVelocityTracker != null){

mVelocityTracker.recycle();

mVelocityTracker = null;

}

break;

/**

* 惯性滑动

*/

private class FlingRunnable implements Runnable{

private Scroller mScroller;

private int mCurrentX , mCurrentY;

public FlingRunnable(Context context){

mScroller = new Scroller(context);

}

public void cancelFling(){

mScroller.forceFinished(true);

}

/**

* 这个方法主要是从onTouch中或得到当前滑动的水平和竖直方向的速度

* 调用scroller.fling方法,这个方法内部能够自动计算惯性滑动

* 的x和y的变化率,根据这个变化率我们就可以对图片进行平移了

*/

public void fling(int viewWidth , int viewHeight , int velocityX ,

int velocityY){

RectF rectF = getMatrixRectF();

if (rectF == null){

return;

}

//startX为当前图片左边界的x坐标

final int startX = Math.round(-rectF.left);

final int minX , maxX , minY , maxY;

//如果图片宽度大于控件宽度

if (rectF.width() > viewWidth){

//这是一个滑动范围[minX,maxX],详情见下图

minX = 0;

maxX = Math.round(rectF.width() - viewWidth);

}else{

//如果图片宽度小于控件宽度,则不允许滑动

minX = maxX = startX;

}

//如果图片高度大于控件高度,同理

final int startY = Math.round(-rectF.top);

if (rectF.height() > viewHeight){

minY = 0;

maxY = Math.round(rectF.height() - viewHeight);

}else{

minY = maxY = startY;

}

mCurrentX = startX;

mCurrentY = startY;

if (startX != maxX || startY != maxY){

//调用fling方法,然后我们可以通过调用getCurX和getCurY来获得当前的x和y坐标

//这个坐标的计算是模拟一个惯性滑动来计算出来的,我们根据这个x和y的变化可以模拟

//出图片的惯性滑动

mScroller.fling(startX,startY,velocityX,velocityY,minX,maxX,minY,maxY);

}

}

关于startX,minX,maxX做一个解释

我们从图中可以看出,当前图片可滑动的一个区间就是左边多出来的那块区间,所以minX和maxX代表的是区间的最小值和最大值,startX就是屏幕左边界的坐标值,我们可以想象成是startX在区间[minX,maxX]的移动。Y轴方向同理。

现在我们看一下效果

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

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

  1. Android开发实例之多点触控程序

    智能终端设备的多点触控操作为我们带来了种种炫酷体验,这也使得很多Android开发者都对多点触控程序的开发感兴趣.实际上多点触控程序的实现并不是那么遥不可及,而是比较容易.本文就主要通过一个实例具体讲 ...

  2. 【Android 应用开发】多点触控 ( 多点触控事件 | PointerId | PointerIndex | 坐标获取 | 触摸点个数 )

    文章目录 I . 多点触控事件 II . PointerId 与 PointerIndex 简介 III . 本次 PointerId 与 PointerIndex 获取 IV . PointerId ...

  3. Android自定义控件ImageViwe(四)——多点触控实现图片的自由移动

    效果图: 功能 : 可以随手指进行自由移动图片 按照适当的比例设置图片的显示 首先将图片按照适当的比例显示在自定义控件中(当图片的宽度或者高度大于控件的宽度或者高度的时候,会对图片进行适当的缩放,当图 ...

  4. android如何怎么禁止多点触控

    不积跬步无以至千里 在一个页面里做了个对讲的操作,对讲需要长按操作,但是发现碰触界面其他位置,会中断,通过监听这个View的onTouchEvent,其中的MotionEvent中的action,发现 ...

  5. Android自定义View的多点触控

    在Android游戏开发中,自定义View的多点触控技术必不可少,本文主要简单讲解下Android中多点触控技术的基础知识. 所谓多点触控技术,就是手机屏幕上支持同时处理多个触控点的触屏或移动事件.多 ...

  6. Android:禁用全局多点触控

    在application引用的Theme中添加以下代码: <item name="android:windowEnableSplitTouch">false</i ...

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

    在系统的相册中,观看相片就可以用多个手指进行缩放. 要实现这个功能,只需要这几步: 1.新建项目,在项目中新建一个ZoomImage.java public class ZoomImageView e ...

  8. android 多点触控缩放,Android多点触控(图片的缩放Demo)

    本文主要介绍Android的多点触控,使用了一个图片缩放的实例,来更好的说明其原理.需要实现OnTouchListener接口,重写其中的onTouch方法. 实现效果图: 源代码: 布局文件: ac ...

  9. Iwfu-安卓Gesture手势(2)-实现多点触控控制图片的放大缩小。

    上一篇介绍安卓Gesture手势初步使用,这一篇用Gesture来实现多点触控达到控制图片放大缩小. 上文中写道,进行手势监听的Activity要实现对应的OnGestureListener接口,重写 ...

  10. Android笔记:触摸事件的分析与总结----多点触控

       其他相关博文:    Android笔记:触摸事件的分析与总结----MotionEvent对象    Android笔记:触摸事件的分析与总结----TouchEvent处理机制     An ...

最新文章

  1. John的农场(最小生成树)
  2. jsp员工管理系统mysql_简单的员工管理系统(Mysql+jdbc+Servlet+JSP)
  3. Tushare的安装
  4. cas 4.2.7 官方手册_海城市地区,保险手册核验的简单流程
  5. mac里打开隐藏的 library文件夹
  6. python绘制动态图-数据可视化
  7. tomcat 9 无法启动_运维常见问题汇总tomcat篇
  8. 【Elasticsearch】zen discovery集群发现机制
  9. Android 自定义View自定义属性的声明
  10. 计算机组成原理mw,计算机组成原理 存储器
  11. windows命令行设置和系统快捷键
  12. Android APK反编译就这么简单 详解(附图)
  13. 具有聚集诱导发射(AIE)特性的新型TADF分子SBF-BP-DMAC的定制合成
  14. JavaScript history对象(back()、forward()、go())
  15. 深圳-数据岗位面试不完全记录(回忆版)
  16. 为什么要学数学?因为它真的没用啊!
  17. Ubuntu18.04打开关闭DNS服务
  18. 评测:Logitech ConferenceCam CC3000e-一款出色的摇摄变焦摄像头和扬声器,适合远程工作者使用
  19. h5上传图片html5,h5图片上传简易版(FileReader+FormData+ajax)
  20. mysql-基础-视图,存储过程,触发器

热门文章

  1. 联想台式主机拆机教程_联想一体机拆机图解 联想一体机电脑该怎么拆机
  2. 机顶盒及编解码器的音视频质量评价测试
  3. java httpclient 调用webservice_HttpClient调用WebService接口
  4. MacOS 10.15 Catalina:13个问题和修复
  5. python之windrose风向玫瑰图的用法
  6. 计算机入职规划,入职后的工作生涯规划范文
  7. 分时操作系统与分布式操作系统
  8. 计算机主机组件图,电脑主机结构图大全
  9. WinCC语音报警实现方法
  10. 北京内推 | 启元世界招聘虚拟人算法工程师/实习生