6.Android学习之Android事件处理和手势(二)

本文详细介绍了Android中的手势检测,包括GestureDetector类的使用和OnGestureListener监听器。通过创建GestureDetector对象并绑定监听器来处理触摸事件。此外,还深入探讨了单击事件与触摸事件的区别,指出当触摸事件被消耗时将不再触发单击事件。最后,讲解了如何识别双指缩放手势,利用MotionEvent ACTION_POINTER_DOWN和ACTION_POINTER_UP常量以及计算两点间距离的方法来实现。

目录

6.Android事件处理和手势(二)

4.手势检测

5.难点解答

5-1.单击事件与触摸事件的区别

5-2.如何识别双指缩放手势


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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值