(五)多点触控之兼容ViewPager
在上一篇文章中,自定义的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的更多相关文章
- 【朝花夕拾】Android自定义View篇之(八)多点触控(上)MotionEvent简介
前言 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2 ...
- 关于android多点触控
最近项目需要一个多点触控缩放的功能.然后上网查了下资料 总结一下: 首先android sdk版本很重要,比如你在AndroidManifest.xml中指定android:minSdkVersion ...
- Android多点触控手势基础
处理多点触控手势 多点触控就是同时把一根以上的手指放在屏幕上. 再继续往下以前需要补充一些名词: 触控手势:就是把一根或者几根手指放在屏幕上做各种动作,其中包括保留一根手指的前提下,拿起或者放下其余的 ...
- (干货) Android实现ImageVIew多点触控及双击缩放
支持多点触控,放大自由移动,双击可以放大缩小.直接上代码: package com.cbt.view; import android.content.Context; import android.g ...
- Android多点触控技术,实现对图片的放大缩小平移,惯性滑动等功能
首先推荐一下鸿洋大大的打造个性的图片预览与多点触控视频教程,这套教程教我们一步一步实现了多点触控实现对图片的平移和缩放的功能.这篇文章我将在鸿洋大大的基础之上做了一些扩展功能: 1.图片的惯性滑动 2 ...
- (一)自定义ImageView,初步实现多点触控、自由缩放
真心佩服那些一直专注于技术共享的大神们,正是因为他们无私的分享精神,我才能每天都有进步.近日又算是仔细学了android的自定义控件技术,跟着大神的脚步实现了一个自定义的ImageView.里面涉及到 ...
- [示例] Firemonkey OnTouch 多点触控应用
说明:Firemonkey OnTouch 多点触控应用,可同时多指移动多个不同控件 原码下载:[原创]TestMultitouchMove_多点触控应用_by_Aone.zip 运行展示:
- ccc 多点触控2
经过不断的思考发现,如果是两个sprite都添加触控的时候,往往直接成单点触控, 但是如果是两个node的时候在node上面点击就会变成多点触控的形式 cc.Class({ extends: cc.C ...
- ccc 多点触控
研究了一天,多点触控的点无法保存,只能模拟多点触控了 cc.Class({ extends: cc.Component, properties: { wheelStick:{ default:null ...
随机推荐
- linux 查看服务器系统资源和负载,以及性能监控
1.查看磁盘 df -h 2.查看内存大小 free [-m|g]#按MB,GB显示内存 3.查看每个进程的情况 cat /proc/5346/status PID 4.查看负载 uptime 5.查 ...
- Win7 开机启动
1.注册表里面写代码,设置程序以开机启动; 但这样会需要管理员权限,添加程序以管理员权限启动后,又无法直接进入到软件启动界面,UAC控制 代码一: /// <summary> /// 设置 ...
- 使用sqlcmd进行MS-dos方式查询
在windows选择‘运行’vista需要以管理员身份运行,打开命令提示符窗口 要连接到sql server服务器,必须指定服务器名称,安装命名实例中的,还必须指定实例名.默认情况下,sqlcmd使用 ...
- Git - 远程库的创建与认证
前些日子因为某些原因,需要在windows上创建一个remote仓库. 由于实在是太麻烦而且时间紧急,就直接用了gitstack. 发现这个东西居然需要付费,未认证时只能创建两个用户. 其实对我而言足 ...
- ngnix优化【转】
nginx的优化 1. gzip压缩优化 2. expires缓存有还 3. 网络IO事件模型优化 4. 隐藏软件名称和版本号 5. 防盗链优化 6. 禁止恶意域名解析 7. 禁止通过IP地址访问网站 ...
- sublime快捷键大全(转)
文件 File 新建文件 Ctrl + N 打开文件 Ctrl + O 打开最近关闭的文件 Ctrl + Shift + T 保存 Ctrl + S 另存为… Ctrl + Shift + S 关闭文 ...
- request方法总结
1.获得指定的头 String header = response.getHeader("user-agent"); 2.获得所有头的名称 Enumeration<Stri ...
- SQL常用函数总结
SQL常用函数总结 这是我在项目开发中使用db2数据库写存储过程的时候经常用到的sql函数.希望对大家有所帮助: sql cast函数 (1).CAST()函数的参数是一个表达式,它包括用AS关键字分 ...
- 详细解析 HTTP 与 HTTPS 的区别
详细解析 HTTP 与 HTTPS 的区别 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览 ...
- Java并发编程:volatile关键字解析(学习总结-海子)
博文地址:Java并发编程:volatile关键字解析