目录
6.Android事件处理和手势(二)
4.手势检测
手势是指用户手指或触摸笔在屏幕上的连续触碰行为。例如,在屏幕上从左到右或从上到下划出的一个动作就是手势。Android 为手势行为提供了支持,最常用的就是手势检测。
Android为手势检测提供了一个GestureDetector类,该类代表了一个手势检测器。在创建GestureDetector时,需要传入一个GestureDetector.OnGestureListener实例。GestureDetector.OnGestureListener代表一个监听器,负贵对用户的手势行为做出响应。GestureDetector.OnGestureListener中包含的事件处理方法如表6.3所示。
表6.3 GestureDetector.OnGestureListener中的事件处理方法
| 方法 | 说明 |
|---|---|
| bolean onDown(MotionEvent e) | 当触摸事件按下时触发 |
| bolean onFling(MotionEvent e1, MotionEvent e2,float velocityX, float velocityY) | 当用户手指在触模屏上“滑过”时触发,其中, velocityX、 velocityY代表“滑过”动作在横向、纵向上的速度 |
| abstract void onLongPress(MotionEvent e) | 当用户手指在触摸屏上长按时触发 |
| bolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) | 当用户手指在触摸屏上连续向上或向下时触发 |
| void onShowPress(MotionEvent e) | 当用户手指在触摸屏上按下,并且未移动和松开时触发 |
| boolean onSingleTapUp(MotionEvent e) | 当用户手指在触摸屏上的轻击事件发生时触发 |
使用Android的手势检测只需要以下两个步骤:
(1)创建一个GestureDetector对象。在创建该对象时必须要实现一个GestureDetector.OnGestureListener监听器实例。
(2)为应用程序的Activity的TouchEvent事件绑定监听器,在事件处理中指定把Activity上的TouchEvent事件交给GestureDetector处理。这样GestureDetector 就会检测是否触发了特定的手势动作。
例:

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
tools:context=".MainActivity">
<ViewFlipper
android:id="@+id/flipper"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
MainActivity.java
package com.example.gesturedetection;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.ViewFlipper;
public class MainActivity extends AppCompatActivity
implements GestureDetector.OnGestureListener {
ViewFlipper flipper;//定义ViewFlipper
GestureDetector detector;//定义手势检测器
Animation[] animation = new Animation[4];//定义动画数组,为ViewFlipper指定切换动画
final int distance=50;//定义手势动作两点之间最小距离
//定义图片数组
private int[] images=new int[]{R.drawable.img01,R.drawable.img02,R.drawable.img03,
R.drawable.img04,R.drawable.img05,R.drawable.img06,R.drawable.img07,
R.drawable.img08,R.drawable.img09};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar=getSupportActionBar();
actionBar.hide();
detector=new GestureDetector(this,this);//创建手势检测器
flipper =(ViewFlipper) findViewById(R.id.flipper);//获取ViewFlipper组件
for(int i=0;i<images.length;i++){
ImageView imageView=new ImageView(this);//创建图像组件
imageView.setImageResource(images[i]);//设置图片资源
flipper.addView(imageView);
}
//初始化动画数组
animation[0]= AnimationUtils.loadAnimation(this,R.anim.slide_in_left);
animation[1]= AnimationUtils.loadAnimation(this,R.anim.slide_out_left);
animation[2]= AnimationUtils.loadAnimation(this,R.anim.slide_in_right);
animation[3]= AnimationUtils.loadAnimation(this,R.anim.slide_out_right);
}
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@Override
public void onShowPress(MotionEvent motionEvent) {
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return false;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
@Override
public void onLongPress(MotionEvent motionEvent) {
}
@Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
/*
如果第一个触点事件的X坐标到第二个触点事件的X坐标的距离超过distance就是从右向左滑动
*/
if(motionEvent.getX()-motionEvent1.getX()>distance){
//为flipper设置切换的动画效果
flipper.setInAnimation(animation[2]);
flipper.setOutAnimation(animation[1]);
flipper.showPrevious();
return true;
/*
如果第二个触点事件的X坐标到第一个触点事件的X坐标的距离超过distance就是从左向右滑动
*/
}else if(motionEvent1.getX()-motionEvent.getX()>distance){
//为flipper设置切换的动画效果
flipper.setInAnimation(animation[0]);
flipper.setOutAnimation(animation[3]);
flipper.showPrevious();
return true;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event){
//将该Activity上的触摸事件交给GestureDetector处理
return detector.onTouchEvent(event);
}
}
anim-slide_in_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="-100%p"
android:toXDelta="0"
android:duration="500"/>
</set>
anim-slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="100%p"
android:toXDelta="0"/>
</set>
anim-slide_out_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="0"
android:toXDelta="-100%p"/>
</set>
anim-slide_out_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="0"
android:toXDelta="100%p"/>
</set>
5.难点解答
5-1.单击事件与触摸事件的区别
针对屏幕上的一个View组件,Android 是如何区分应当触发onTouch事件还是onClick事件?在Android中,一次用户操作可以被不同的View组件按次序分别处理,并将完全响应了用户的一次UI操作称之为消耗了该事件(consume),那么Android是按什么次序将事件传递的,又在什么情况下判定为消耗了该事件?下面通过一段具体的代码进行说明。
在这段代码中,先为按钮添加单击事件监听器,并通过Log.i()方法输出onClick(单击事件)。然后为按钮添加触摸事件,通过判断方式输出当前手指是按下还是抬起。具体代码如下:
package com.example.demo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
public class MainActivity2 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button button=(Button) findViewById(R.id.main2_btn1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.i("onClick","单击事件");
}
});
//为按钮添加触摸事件监听器
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
//手指按下时
if(motionEvent.getAction()==MotionEvent.ACTION_DOWN){
Log.i("onTouch","按下");
}else if(motionEvent.getAction()==MotionEvent.ACTION_UP){//手指抬起时
Log.i("onTouch","抬起");
}
return false;//表示未消耗掉这个事件
}
});
}
}
执行上面的代码后,单击屏幕中的按钮,将会在LogCat面板中看到如图所示的结果。

注:为一个组件同时设置单击事件与触摸事件时,触摸事件首先被执行然后执行单击事件。当触摸事件监听器返回值为true时,说明消耗掉了这个事件,将不再执行单击事件。
5-2.如何识别双指缩放手势
在使用手机时,经常会通过双指缩放手势缩放图片或者缩放页面。
在Android中,对多点触摸的支持使用MotionEvent对象处理。通过表达式“event.getAction()&MotionEvent.ACTION_MASK”可以根据触摸状态得到不同的常量, 通过这些常量可以判断触发的是什么事件,这些常量及其作用如表6.4所示。
表6.4 GestureDetector.OnGestureListener中的事件处理方法
| 常量 | 作用 |
|---|---|
| ACTION_DOWN | 第一个点被按下时触发 |
| ACTION_UP | 唯一的点被放开时触发 |
| ACTION_POINTER_UP | 当屏幕上有多个点被按住,松开其中一个点时触发 |
| ACTION_POINTER_DOWN | 当屏幕上已经有一个点被按住,此时再按下其他点时触发 |
例如,要识别双指缩放手势,可以通过以下代码实现。
@Override
public boolean onTouchEvent(MotionEvent event){
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN://第一个点被按下时
model = 1;
break;
case MotionEvent.ACTION_UP://唯一的点被放开时
model = 0;
break;
case MotionEvent.ACTION_POINTER_UP://当屏幕上有多个点被按住,松开其中一个点时
model -= 1;
break;
case MotionEvent.ACTION_POINTER_DOWN://当屏幕上已有一个点被按住,再按下其他点时
oldDistance = distance(event);//两点按下时的距离
model += 1;
break;
case MotionEvent.ACTION_MOVE:
if (model >= 2) {
double newDistance = distance(event);
if (newDistance > oldDistance + 1) {
Toast.makeText(MainActivity.this,"放大",Toast.LENGTH_SHORT).show();
}
if (newDistance < oldDistance) {
Toast.makeText(MainActivity.this,"缩小",Toast.LENGTH_SHORT).show();
}
break;
}
}
return true;
}
其中,distance()方法用于获取两个点间的距离,具体代码如下:
//计算两点间的距离
private double distance(MotionEvent event) {
float x = event.getX(0) - event.getX(1);//计算X轴上的距离
float y = event.getY(0) - event.getY(1);//计算Y轴上的距离
return Math.sqrt(x*x+y*y);
}
本文详细介绍了Android中的手势检测,包括GestureDetector类的使用和OnGestureListener监听器。通过创建GestureDetector对象并绑定监听器来处理触摸事件。此外,还深入探讨了单击事件与触摸事件的区别,指出当触摸事件被消耗时将不再触发单击事件。最后,讲解了如何识别双指缩放手势,利用MotionEvent ACTION_POINTER_DOWN和ACTION_POINTER_UP常量以及计算两点间距离的方法来实现。
585

被折叠的 条评论
为什么被折叠?



