在上一篇文章中,自定义的ZoomImageView已经实现了自由缩放,自由移动以及双击放大与缩小的功能。已经可以投入使用这个控件了。下面我们就在ViewPager中使用这个控件。如果你还没读过上一篇文章,可以点击下面的链接:

http://www.cnblogs.com/fuly550871915/p/4940193.html

一、在ViewPager中使用自定义的ZoomImageView

快速的代建起ViewPager吧。修改activity_main.xml中的代码,如下:

<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"
> <android.support.v4.view.ViewPager
android:id="@+id/id_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
> </android.support.v4.view.ViewPager> </RelativeLayout>

然后修改MainActivity中的代码,如下:

 package com.example.zoom;

 import java.util.ArrayList;
import java.util.List; import com.example.view.ZoomImageView; import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.app.Activity; public class MainActivity extends Activity { private ViewPager mViewPager;
private int[] imgIds = new int[]{R.drawable.mingxing0403,R.drawable.qw,
R.drawable.ic_launcher}; private List<ImageView> mImageViews =new ArrayList<ImageView>(); protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); mViewPager = (ViewPager) findViewById(R.id.id_viewpager); for(int i=0;i<imgIds.length;i++)
{
ZoomImageView ziv = new ZoomImageView(getApplicationContext());
ziv.setImageResource(imgIds[i]);
mImageViews.add(ziv);
} mViewPager.setAdapter(new PagerAdapter() { public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1;
} public int getCount() { return mImageViews.size();
} public void destroyItem(ViewGroup container, int position,
Object object) { container.removeView(mImageViews.get(position));
} public Object instantiateItem(ViewGroup container, int position) {
container.addView(mImageViews.get(position));
return mImageViews.get(position);
} });
} }

代码很简单,我就不多说了。为了兼容ViewPager,我们还要修改ZoomImageView中的代码,如下:

 package com.example.view;

 import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.View.OnTouchListener;
import android.widget.ImageView; public class ZoomImageView extends ImageView implements OnGlobalLayoutListener,
OnScaleGestureListener, OnTouchListener
{
private boolean mOnce = false;//是否执行了一次 /**
* 初始缩放的比例
*/
private float initScale;
/**
* 缩放比例
*/
private float midScale;
/**
* 可放大的最大比例
*/
private float maxScale;
/**
* 缩放矩阵
*/
private Matrix scaleMatrix; /**
* 缩放的手势监控类
*/
private ScaleGestureDetector mScaleGestureDetector; //==========================下面是自由移动的成员变量======================================
/**
* 上一次移动的手指个数,也可以说是多点个数
*/
private int mLastPoint;
/**
* 上次的中心点的x位置
*/
private float mLastX;
/**
* 上一次中心点的y位置
*/
private float mLastY;
/**
* 一个临界值,即是否触发移动的临界值
*/
private int mScaleSlop;
/**
* 是否可移动
*/
private boolean isCanDrag = false; //===================下面是双击放大与缩小功能的成员变量=============== /**
* 监测各种手势事件,例如双击
*/
private GestureDetector mGestureDetector;
/**
* 是否正在执行双击缩放
*/
private boolean isAutoScale ; public ZoomImageView(Context context)
{
this(context,null);
}
public ZoomImageView(Context context, AttributeSet attrs)
{
this(context, attrs,0); }
public ZoomImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle); scaleMatrix = new Matrix(); setScaleType(ScaleType.MATRIX); mScaleGestureDetector = new ScaleGestureDetector(context, this);
//触摸回调
setOnTouchListener(this);
//获得系统给定的触发移动效果的临界值
mScaleSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mGestureDetector = new GestureDetector(context,new GestureDetector.SimpleOnGestureListener()
{
public boolean onDoubleTap(MotionEvent e)
{
if(isAutoScale)//如果正在执行双击缩放,直接跳过
{
return true;
} float x = e.getX();
float y = e.getY();
//获得当前的缩放比例
float scale = getDrawableScale(); if(scale<midScale)//如果比midScale小,一律放大,否则一律缩小为initScale
{
// scaleMatrix.postScale(midScale/scale,midScale/scale, x, y);
// setImageMatrix(scaleMatrix);
postDelayed(new AutoScaleRunnable(midScale, x, y), 16); isAutoScale = true; }else
{
// scaleMatrix.postScale(initScale/scale,initScale/scale, x, y);
// setImageMatrix(scaleMatrix);
postDelayed(new AutoScaleRunnable(initScale, x, y), 16); isAutoScale = true;
} return true; };
}
);
}
/**
*将 双击缩放使用梯度
* @author fuly1314
*
*/
private class AutoScaleRunnable implements Runnable
{ private float targetScale;//缩放的目标值
private float x;
private float y;//缩放的中心点 private float tempScale; private float BIGGER = 1.07F;
private float SMALL = 0.93F;//缩放的梯度 public AutoScaleRunnable(float targetScale, float x, float y) {
super();
this.targetScale = targetScale;
this.x = x;
this.y = y; if(getDrawableScale()<targetScale)
{
tempScale = BIGGER;
}
if(getDrawableScale()>targetScale)
{
tempScale = SMALL;
}
} public void run()
{ scaleMatrix.postScale(tempScale, tempScale, x, y);
checkBoderAndCenter();
setImageMatrix(scaleMatrix); float scale = getDrawableScale(); if((scale<targetScale&&tempScale>1.0f)||(scale>targetScale&&tempScale<1.0f))
{
postDelayed(this, 16);
}else
{
scaleMatrix.postScale(targetScale/scale, targetScale/scale, x, y);
checkBoderAndCenter();
setImageMatrix(scaleMatrix); isAutoScale = false;
} } } /**
* 该方法在view与window绑定时被调用,且只会被调用一次,其在view的onDraw方法之前调用
*/
protected void onAttachedToWindow()
{
super.onAttachedToWindow();
//注册监听器
getViewTreeObserver().addOnGlobalLayoutListener(this);
} /**
* 该方法在view被销毁时被调用
*/
@SuppressLint("NewApi") protected void onDetachedFromWindow()
{
super.onDetachedFromWindow();
//取消监听器
getViewTreeObserver().removeOnGlobalLayoutListener(this);
} /**
* 当一个view的布局加载完成或者布局发生改变时,OnGlobalLayoutListener会监听到,调用该方法
* 因此该方法可能会被多次调用,需要在合适的地方注册和取消监听器
*/
public void onGlobalLayout()
{
if(!mOnce)
{
//获得当前view的Drawable
Drawable d = getDrawable(); if(d == null)
{
return;
} //获得Drawable的宽和高
int dw = d.getIntrinsicWidth();
int dh = d.getIntrinsicHeight(); //获取当前view的宽和高
int width = getWidth();
int height = getHeight(); //缩放的比例,scale可能是缩小的比例也可能是放大的比例,看它的值是大于1还是小于1
float scale = 1.0f; //如果仅仅是图片宽度比view宽度大,则应该将图片按宽度缩小
if(dw>width&&dh<height)
{
scale = width*1.0f/dw;
}
//如果图片和高度都比view的大,则应该按最小的比例缩小图片
if(dw>width&&dh>height)
{
scale = Math.min(width*1.0f/dw, height*1.0f/dh);
}
//如果图片宽度和高度都比view的要小,则应该按最小的比例放大图片
if(dw<width&&dh<height)
{
scale = Math.min(width*1.0f/dw, height*1.0f/dh);
}
//如果仅仅是高度比view的大,则按照高度缩小图片即可
if(dw<width&&dh>height)
{
scale = height*1.0f/dh;
} //初始化缩放的比例
initScale = scale;
midScale = initScale*2;
maxScale = initScale*4; //移动图片到达view的中心
int dx = width/2 - dw/2;
int dy = height/2 - dh/2;
scaleMatrix.postTranslate(dx, dy); //缩放图片
scaleMatrix.postScale(initScale, initScale, width/2, height/2); setImageMatrix(scaleMatrix);
mOnce = true;
} }
/**
* 获取当前已经缩放的比例
* @return 因为x方向和y方向比例相同,所以只返回x方向的缩放比例即可
*/
private float getDrawableScale()
{ float[] values = new float[9];
scaleMatrix.getValues(values); return values[Matrix.MSCALE_X]; } /**
* 缩放手势进行时调用该方法
*
* 缩放范围:initScale~maxScale
*/
public boolean onScale(ScaleGestureDetector detector)
{ if(getDrawable() == null)
{
return true;//如果没有图片,下面的代码没有必要运行
} float scale = getDrawableScale();
//获取当前缩放因子
float scaleFactor = detector.getScaleFactor(); if((scale<maxScale&&scaleFactor>1.0f)||(scale>initScale&&scaleFactor<1.0f))
{
//如果缩小的范围比允许的最小范围还要小,就重置缩放因子为当前的状态的因子
if(scale*scaleFactor<initScale)
{
scaleFactor = initScale/scale;
}
//如果缩小的范围比允许的最小范围还要小,就重置缩放因子为当前的状态的因子
if(scale*scaleFactor>maxScale)
{
scaleFactor = maxScale/scale;
} // scaleMatrix.postScale(scaleFactor, scaleFactor, getWidth()/2, getHeight()/2);
scaleMatrix.postScale(scaleFactor, scaleFactor,detector.getFocusX(),
detector.getFocusY()); checkBoderAndCenter();//处理缩放后图片边界与屏幕有间隙或者不居中的问题 setImageMatrix(scaleMatrix);//千万不要忘记设置这个,我总是忘记
} return true;
}
/**
* 处理缩放后图片边界与屏幕有间隙或者不居中的问题
*/
private void checkBoderAndCenter()
{
RectF rectf = getDrawableRectF(); int width = getWidth();
int height = getHeight(); float delaX =0;
float delaY = 0; if(rectf.width()>=width)
{
if(rectf.left>0)
{
delaX = - rectf.left;
} if(rectf.right<width)
{
delaX = width - rectf.right;
}
} if(rectf.height()>=height)
{
if(rectf.top>0)
{
delaY = -rectf.top;
}
if(rectf.bottom<height)
{
delaY = height - rectf.bottom;
}
} if(rectf.width()<width)
{
delaX = width/2 - rectf.right+ rectf.width()/2;
} if(rectf.height()<height)
{
delaY = height/2 - rectf.bottom+ rectf.height()/2;
} scaleMatrix.postTranslate(delaX, delaY);
}
/**
* 获取图片根据矩阵变换后的四个角的坐标,即left,top,right,bottom
* @return
*/
private RectF getDrawableRectF()
{
Matrix matrix = scaleMatrix;
RectF rectf = new RectF();
Drawable d = getDrawable();
if(d != null)
{ rectf.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
} matrix.mapRect(rectf);
return rectf;
}
/**
* 缩放手势开始时调用该方法
*/
public boolean onScaleBegin(ScaleGestureDetector detector)
{
//返回为true,则缩放手势事件往下进行,否则到此为止,即不会执行onScale和onScaleEnd方法
return true;
}
/**
* 缩放手势完成后调用该方法
*/
public void onScaleEnd(ScaleGestureDetector detector)
{ } /**
* 监听触摸事件
*/
public boolean onTouch(View v, MotionEvent event)
{ if(mGestureDetector.onTouchEvent(event))
{
return true;
} if(mScaleGestureDetector != null)
{
//将触摸事件传递给手势缩放这个类
mScaleGestureDetector.onTouchEvent(event);
} //获得多点个数,也叫屏幕上手指的个数
int pointCount = event.getPointerCount(); float x =0;
float y =0;//中心点的x和y for(int i=0;i<pointCount;i++)
{
x+=event.getX(i);
y+=event.getY(i);
} //求出中心点的位置
x/= pointCount;
y/= pointCount; //如果手指的数量发生了改变,则不移动
if(mLastPoint != pointCount)
{
isCanDrag = false;
mLastX = x;
mLastY = y; }
mLastPoint = pointCount; RectF rectf = getDrawableRectF();
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN: 482 if(rectf.width()>getWidth()+0.01||rectf.height()>getHeight()+0.01)
483 {
484
485 //请求父类不要拦截ACTION_DOWN事件
486 if(getParent() instanceof ViewPager)
487 this.getParent().requestDisallowInterceptTouchEvent(true);
488 } break;
case MotionEvent.ACTION_MOVE: if(rectf.width()>getWidth()+0.01||rectf.height()>getHeight()+0.01)
496 {
497
498 //请求父类不要拦截ACTION_MOVE事件
499 if(getParent() instanceof ViewPager)
500 this.getParent().requestDisallowInterceptTouchEvent(true);
501 } //求出移动的距离
float dx = x - mLastX;
float dy = y- mLastY; if(!isCanDrag)
{
isCanDrag = isCanDrag(dx,dy);
} if(isCanDrag)
{
//如果图片能正常显示,就不需要移动了
if(rectf.width()<=getWidth())
{
dx = 0;
}
if(rectf.height()<=getHeight())
{
dy = 0;
} //开始移动
scaleMatrix.postTranslate(dx, dy);
//处理移动后图片边界与屏幕有间隙或者不居中的问题
checkBoderAndCenterWhenMove();
setImageMatrix(scaleMatrix);
} mLastX = x;
mLastY = y; break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mLastPoint = 0;
break; } return true;
}
/**
* 处理移动后图片边界与屏幕有间隙或者不居中的问题
* 这跟我们前面写的代码很像
*/
private void checkBoderAndCenterWhenMove() { RectF rectf = getDrawableRectF(); float delaX = 0;
float delaY = 0;
int width = getWidth();
int height = getHeight(); if(rectf.width()>width&&rectf.left>0)
{
delaX = - rectf.left;
}
if(rectf.width()>width&&rectf.right<width)
{
delaX = width - rectf.right;
}
if(rectf.height()>height&&rectf.top>0)
{
delaY = - rectf.top;
}
if(rectf.height()>height&&rectf.bottom<height)
{
delaY = height - rectf.bottom;
} scaleMatrix.postTranslate(delaX, delaY);
}
/**
* 判断是否触发移动效果
* @param dx
* @param dy
* @return
*/
private boolean isCanDrag(float dx, float dy) { return Math.sqrt(dx*dx+dy*dy)>mScaleSlop;
} }

红色代码是我们添加的。在这里,只需要请求父类ViewPager不要拦截触摸事件即可。然后我们运行程序,效果如下图:

依然使用真机测试的,效果完全符合我们的预期。至此,本项目完结了。一个支持多点触控的ImageView做了出来。

二、小结

在拖动图片的时候会与ViewPager发生冲突,因为ViewPager也会处理拖动事件。因此为了解决这个冲突,必须在ZoomImageView中添加代码:

    if(rectf.width()>getWidth()+0.01||rectf.height()>getHeight()+0.01)
{ //请求父类不要拦截ACTION_DOWN事件
if(getParent() instanceof ViewPager)
this.getParent().requestDisallowInterceptTouchEvent(true);
}

注意红色代码是核心。

(五)多点触控之兼容ViewPager的更多相关文章

  1. 【朝花夕拾】Android自定义View篇之(八)多点触控(上)MotionEvent简介

    前言 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2 ...

  2. 关于android多点触控

    最近项目需要一个多点触控缩放的功能.然后上网查了下资料 总结一下: 首先android sdk版本很重要,比如你在AndroidManifest.xml中指定android:minSdkVersion ...

  3. Android多点触控手势基础

    处理多点触控手势 多点触控就是同时把一根以上的手指放在屏幕上. 再继续往下以前需要补充一些名词: 触控手势:就是把一根或者几根手指放在屏幕上做各种动作,其中包括保留一根手指的前提下,拿起或者放下其余的 ...

  4. (干货) Android实现ImageVIew多点触控及双击缩放

    支持多点触控,放大自由移动,双击可以放大缩小.直接上代码: package com.cbt.view; import android.content.Context; import android.g ...

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

    首先推荐一下鸿洋大大的打造个性的图片预览与多点触控视频教程,这套教程教我们一步一步实现了多点触控实现对图片的平移和缩放的功能.这篇文章我将在鸿洋大大的基础之上做了一些扩展功能: 1.图片的惯性滑动 2 ...

  6. (一)自定义ImageView,初步实现多点触控、自由缩放

    真心佩服那些一直专注于技术共享的大神们,正是因为他们无私的分享精神,我才能每天都有进步.近日又算是仔细学了android的自定义控件技术,跟着大神的脚步实现了一个自定义的ImageView.里面涉及到 ...

  7. [示例] Firemonkey OnTouch 多点触控应用

    说明:Firemonkey OnTouch 多点触控应用,可同时多指移动多个不同控件 原码下载:[原创]TestMultitouchMove_多点触控应用_by_Aone.zip 运行展示:

  8. ccc 多点触控2

    经过不断的思考发现,如果是两个sprite都添加触控的时候,往往直接成单点触控, 但是如果是两个node的时候在node上面点击就会变成多点触控的形式 cc.Class({ extends: cc.C ...

  9. ccc 多点触控

    研究了一天,多点触控的点无法保存,只能模拟多点触控了 cc.Class({ extends: cc.Component, properties: { wheelStick:{ default:null ...

随机推荐

  1. Codeforces 494E. Sharti

    Description 有一个 \(n*n\) 的矩形,给出 \(m\) 个子矩形,这些矩形内部的点都是白色的,其余的点都是黑色,每一次你可以选择一个变长不超过 \(k\) 的正方形,满足这个正方形的 ...

  2. vue中添加swiper轮播插件

    网上找了很多,最后还是官网最完整. https://github.com/surmon-china/vue-awesome-swiper 安装: 1.npm install vue-awesome-s ...

  3. ios audio不能自动播放

    今天做了一个简单的落地页项目,就是类似于手机微信上经常看到的滑动效果.因为公司要求需要自己开发,所以我就用swiper+swiper.animate开发,开发速度很快,只不过最后音乐哪里出现了一点小b ...

  4. 朝圣Java(问题集锦)之:The Apache Tomcat installation at this directory is version 8.5.32. A Tomcat 8.0 inst

    最近开始学Java了.有C#底子,但是学起来Java还是很吃力,感觉别人架好了各种包,自己只要调用就行了,结果还有各种bug出现.掩面中. 启动Tomcat的时候,报错The Apache Tomca ...

  5. 读EntityFramework.DynamicFilters源码_心得_单元测试03

    上个星期我们只是显示了一个示例,怎么在EF的框架内,注入我们拓展的动态过滤器 第一步:安装EntityFramework.DynamicFilters 第二步:重写OnModelCreating方法 ...

  6. About custom Theme and Style

    For android system, of course you can custom your own style and theme, but you can't break compatibi ...

  7. Maven 配置tomcat和findbug插件(在eclipse建立的项目中)

    tomcat插件 a)        tomcat的maven插件可以在tomcat的官网上寻找,这就是tomcat插件的plugin b)        将tomcat的plugin配置到项目的po ...

  8. C#中的Sealed和J#中的Final比较(转载)

    Sealed与Final修饰符其实并不是一个语言平台的产物,他们有着各自所属的语言环境,但这两个关键字都是.Net平台中不可或缺的,那么二者用法几何,随本文一探究竟. 一.Sealed sealed ...

  9. 花1台的钱入手2台【最能抗DDoS】阿里云主机【攻略】

    花1台的钱入手2台[最能抗DDoS]阿里云主机[攻略]: 第一步:先申请0元半年 http://click.aliyun.com/m/335/:注:0元机器只有新帐号可申请第二步:再买6折37/月 h ...

  10. python小练习2

    结果 代码 鞋子价格=0 男孩价格=0 爆米花价格=0 计算完毕=0 for 鞋子动态价格 in range(0,20): if (计算完毕==1): break; #print("鞋子动态 ...