在上一篇文章中,自定义的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. ES6展开运算符(...)

    数组字面量中使用展开运算符 我们可以这样合并数组: var arr1=['a','b','c']; var arr2=[...arr1,'d','e']; //['a','b','c','d','e' ...

  2. 在ASPNETCORE中获得所有Action

    在ASPNETCORE中获得所有Action 本文旨在记录自己在aspnetcore工作中需要获取所有Action,在查询了资料后进行了几种方法的记录.后期有发现其它方式再进行追加. 一.通过 反射 ...

  3. Hbuilder编辑App时,ajax跨域访问失败问题

    今天试着用Hbuilder写app的前段显示页面,在第一步时就被打住了,ajax异步调用服务器的登录接口时,报错, 显示这样的错误 XMLHttpRequest cannot loadhttp://w ...

  4. Require.js 源码分析

    本文将简单介绍下个人对require.js的源码分析,简单分析实现原理 一.require加载资源的流程 require中,根据AMD(Asynchronous Module Definition)的 ...

  5. Silverlight & Blend动画设计系列八:拖放(Drag-Drop)操作与拖放行为(DragBehavior)

    在Silverlight中自身并没有提供拖放功能的相关实现,要实现拖放功能得借助其事件支持(MouseLeftButtonDown.MouseLeftButtonUp和MouseMove)来完成,实际 ...

  6. Rabbit的直连交换机direct

    直连交换机类型为:direct.加入了路由键routingKey的概念. 就是说 生产者投递消息给指定交换机的指定路由键. 只有绑定了此交换机指定路由键的消息队列才可以收到消息. 生产者: packa ...

  7. Python基础学习总结(九)

    11测试代码 1.编写函数和类时,还可以编写测试函数,通过测试可以确定代码面对各种输入都能正常工作.在程序中添加新代码时,也可以对其进行测试,确定他们不会破坏程序的既有程序.要经常测试模块. 2.通过 ...

  8. Linux服务器安装tomcat、JDK、SVN等常用开发软件总结

    本来本文发布到首页的,该网站运营人员移除了,说我这篇博文太简单了,如果感觉我这篇博文有用的,大家给个推荐,打一下运营人员的脸 目录 一.Ubuntu 16.04下安装JDK(spring 3.2不支持 ...

  9. JS埋点 小结

    今天在看<大型网站性能监测.分析与优化>一书,提到性能监测方式,才知道有这个名词 “JS埋点”. 大概作用:通过在web页面中,添加js脚本,实现对页面性能监测(如加载时间.服务器响应时间 ...

  10. css3之弹性盒模型初探(一)

    什么是弹性盒模型? 弹性盒模型是指在父级改变大小的时候内部的自己元素也会相应的改变大小,即子集会按照父级的大小按比例自适应大小. 弹性盒模型的提出可以解决一些响应式布局的需求   如何使用弹性盒模型? ...