上一篇介绍安卓Gesture手势初步使用,这一篇用Gesture来实现多点触控达到控制图片放大缩小。
上文中写道,进行手势监听的Activity要实现对应的OnGestureListener接口,重写其中的几个方法,其中最关键的两个方法:
@Override
/* 手指滑动触发 */
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
return false;
}
@Override
/* 手指抛掷(快速划屏幕后松开) */
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}
这两个方法都传入了四个参数,分别代表:
e1: 第一个按下时的事件,可以当做第一次按下时的点
e2: 移动时的事件,分别用于触发scroll和fling
在onScroll中;
dsitanceX表示每次滑动的X方向的偏移量
dsitanceY表示每次滑动的Y方向的偏移量
在onFling中:
velocityX表示X方向的速度
velocityY表示Y方向的速度
知道了这个以后,我们要实现多点触控,那么怎么获取触摸时的多个点呢,这里有一个坑,大部分人认为是通过onScroll的e1.getPointCount( ),测试以后会发现,触摸点永远是1,其实e1代表的是第一个触摸到屏幕的那个手指的事件,即使你看上去两个手指是同时按下的,也有一个手指是最先碰到屏幕的,而这个手指就产生了这个MotionEvent e1,所以触摸点数永远是1。
真正的做法是,在onScroll()中使用
e2.getPointCount( )
来获取触摸点的数量,一般来说控制图片的放大缩小两个点就够了,这里以两个触摸点为例。
获取到触摸点以后,接下来就通过获取到的触摸点来判断监听事件,对图片进行相关操作.一般我们使用相册什么的对图片进行放大缩小是用两根手指,根据两点的距离变化来判断是要放大还是要缩小图片,其中对于的手指滑动的手势,会回调我们的onScroll和onFling,其中onScroll会随着手指滑动一直调用,就类似onTouch里监听到MotionEvent为Action_Move一样。所以我们重点重写这两个方法,并通过一个全局变量来保存每次滑动的距离,方便比较大小。
先贴代码,然后总结下里面容易踩到的坑。
代码:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.chan.gesturepointstouch.MainActivity">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/iv"
android:layout_centerInParent="true"
android:src="@mipmap/ic_launcher"/>
</RelativeLayout>
MainActivity:
package com.chan.gesturepointstouch;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;
/**
* 多点触控 ,控制图片缩放
*/
public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener {
private GestureDetector detector;
private ImageView iv;
private float lastDistance = -1f;// 记录上一次两点的距离
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
detector = new GestureDetector (this, this);
iv = (ImageView) findViewById (R.id.iv);
}
@Override
public boolean onTouchEvent (MotionEvent event) {
return detector.onTouchEvent (event);
}
@Override
public boolean onDown (MotionEvent e) {
// 每次按下后重新复位-1f
lastDistance = -1f;
return false;
}
@Override
public void onShowPress (MotionEvent e) {
}
@Override
public boolean onSingleTapUp (MotionEvent e) {
return false;
}
@Override
public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// 从e2获取触点数量
int pointCount = e2.getPointerCount ();
if (pointCount == 2) {
float deltaX = e2.getX (1) - e2.getX (0);
float deltaY = e2.getY (1) - e2.getY (0);
float distance = (float) Math.sqrt (deltaX * deltaX + deltaY * deltaY);
// 误差值
float slop = 5;
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) iv.getLayoutParams ();
if (lastDistance < 0) {
lastDistance = distance;
} else {
// 真正的比较
if (distance - lastDistance > slop) {
// 放大,最大放大到500
Log.d ("tag", "放大");
if (layoutParams.width < 800) {
layoutParams.width = (int) (iv.getWidth () * 1.1f);
layoutParams.height = (int) (iv.getHeight () * 1.1f);
iv.setLayoutParams (layoutParams);
} else {
Toast.makeText (MainActivity.this, "不能再放大了", Toast.LENGTH_SHORT).show ();
}
} else if (distance - lastDistance < slop) {
// 缩小,最小缩小到100
Log.d ("tag", "缩小");
if (layoutParams.width > 50) {
layoutParams.width = (int) (iv.getWidth () * 0.9f);
layoutParams.height = (int) (iv.getHeight () * 0.9f);
iv.setLayoutParams (layoutParams);
} else {
Toast.makeText (MainActivity.this, "不能再缩小了", Toast.LENGTH_SHORT).show ();
}
}
}
}
return false;
}
@Override
public void onLongPress (MotionEvent e) {
}
@Override
public boolean onFling (MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
}
其中重点:
每次回调onScroll都计算出两触摸点的距离(利用勾股定理),判断下如果lastDistance=-1,即没有保存状态,就保存当前的状态,继续滑动就继续回调onScroll,此时lastDistance保存的是上一次的两触摸点的距离,执行比较距离的代码~
比较的时候给了一个临界值slop,这个值用于避免手指很微小的误操作都被捕捉到,并且这个值也不能太大,否则手指很难滑到这个临界值。
比较时添加了判断图片大小,如果图片大到一定值就不允许继续放大,如果小到一定值就不允许继续缩小。
-改变图片大小用LayoutParams布局参数,每次判断完是放大还是缩小后按一定比例设置LayoutParams的宽高,再重新赋值给图片ImageView
- 每次执行完一个完整的流程后要将全局的lastDistance复位为-1f,否则会保存上一次的触摸点的距离。
下一篇:安卓Gesture手势(3)-实现自定义手势。(待续)