Android基础之Touch事件和手势处理

来源:互联网 发布:linux 删除路由表 编辑:程序博客网 时间:2024/06/03 21:52

Touch事件和手势处理

  • Touch事件
    ·Touch事件:用户的手势操作
    ·Android设备中的应用程序主要依赖于用户的各种手势操作实现交互,常见的手势操作有:单击、长按,滑动。

onTouchEvent()方法
·在view类中定义了onTouchEvent()方法,用于处理Touch事件,View的每个子类都可以重写该方法,以决定该view如何处理Touch事件,方法签名为:
- public boolean onTouchEvent(MotionEvent event)
·在Activity类中也定义了onTouchEvent()方法,则每个Activity的子孙类都可以重写该方法,用于决定当前Activity中如何处理Touch事件,该方法签名与View类定义的onTouchEvent()方法的签名完全相同。

View.OnTouchListener接口
·在View类中定义了setOnTouchListener()方法,用于为View对象指定Touch事件处理监听器,该方法的参数类型是View.OnTouchListener接口,接口中的抽象方法签名为:
- boolean onTouch(View v ,MotionEvent event);

  • MotionEvent类
    无论View类或Activity类中定义的onTouchEvent()方法,或View类中的OnTouchListener接口的抽象方法中,都使用了MotionEvent的对象作为参数,该类的常用方法有:
    ·public final int getAction() //用于获取Touch事件是属于哪类型的事件(如:按下或者离开)
    ·public final float getX() //当前正在操作的手触碰到频幕后,对应到点的X轴的坐标
    ·public final float getX(int pointerIndex) //多点触碰
    ·public final float getY() //当前正在操作的手触碰到频幕后,对应到点的Y轴的坐标
    ·public final float getY(int pointerIndex)
    ·MotionEvent类中定义了一系列的常量,用于标识Touch操作的类型,亦可用于尝试匹配getAction()方法的返回值,常用的常量有:
    ·public static final int ACTION_DOWN //当手刚好按到屏幕的那一刻
    ·public static final int ACTION_UP //手刚好从屏幕上离开的那一刻
    ·public static final int ACTION_MOVE //手按下屏幕正在移动时

  • 处理Touch事件方法的返回值
    ·无论View类或Activity类中定义的onTouchEvent()方法,或View类中的OnTouchListener接口的抽象方法,各方法的返回值均是boolean类型。
    ·由于同一次的某个操作可能导致多个处理Touch事件的方法被回调,当方法的返回值被指定为true时,表示当前方法已“消费”该事件,则后续尝试处理该Touch事件的方法不会被回调,如果返回值被指定为false时,则后续尝试处理该Touch事件的方法会正常执行。

  • 处理Touch事件:
    ·View.onTouchEvent()
    ·Activity.onTouchEvent()
    ·View.setOnTouchListener()

  • 简单的自定义View
    ·当需要自定义View时,需要创建新的Java类,继承自View或者View的子孙类。
    ·由于View没有无参数的构造方法,则自定义View需要显示的创建带参数的构造方法:
    ·如果希望在程序中使用new关键字创建自定义view的对象,则必须显示的创建带1个参数的构造方法;
    ·如果希望在res\layout\下的布局文件中使用自定义View设计布局,则必须显示的创建带2个参数或者带3个参数的构造方法。

  • Touch事件的派发和处理逻辑
    ·在Android系统中,视图系统大致分为View与ViewGroup:
    - View是一般独立显示了具体内容的控件,例如:TextView,ImageView等;
    - ViewGroup是用于承载其他控件的,例如RelativeLayout、ListView等。
    ·当出现Touch事件时,需要明确是由View或是ViewGroup处理事件。

    ·在Android系统中,视图系统中存在3个方法处理Touch事件相关逻辑:
    - public boolean dispatchTouchEvent(MotionEvent ev):处理事件分发;
    - public boolean onInterceptTouchEvent(MotionEvent ev): 处理事件拦截;
    - public boolean onTouchEvent(MotionEvent ev): 处理事件响应。
    ·由于简单的View对象不存在子级控件,所以并不存在事件拦截的操作,即没有OnInterceptEvent()方法。

    ·每个ViewGroup在接收到事件后,默认遵循“分发 –> 拦截 –> 处理”的流程执行相关的方法。
    ·在默认情况下:
    - 事件分发:“隧道式”,即从最外层控件依次传递,直至最内层子级控件,也就是说,越靠近根节点,分发的权力越大。
    - 事件处理:“冒泡式”,即从最内层子级控件开始处理,直到最外层的控件。

  • 事件分发(dispatchTouchEvent)的逻辑如下:
    - 当返回值true时,由dispatchTouchEvent()直接消费,事件将不再传递,即不会拦截,相应,更不会向分发到其它控件;
    - 当返回值为false时:
    ·如果事件是Activity获取的,则由Activity的onTouchEvent()消费;
    ·如果事件是父控件的,则由父控件的onTouchEvent()消费。

  • 事件拦截(onInterceptTouchEvent)的逻辑如下:
    - 当返回值为true时,表示当前控件拦截事件,且当前控件的onTouchEvent()会被执行,事件并不会向后传递到其他控件;
    - 当返回值为false时,表示不拦截,即放行,事件将交给子级控件去分发(即子级控件的dispatchTouchEvent()方法将被执行)。

  • 事件响应(onTouchEvent)的逻辑如下:
    - 当返回值为true时,表示当前控件已消费事件,则整个事件处理过程结束;
    - 当返回值为false时,表示当前控件未消费事件(可能开发人员已编写程序响应,但只要返回值为false,就不表示消费了该事件),将由父级控件继续响应,即父级控件的onTouchEvent()方法会被调用。

  • 特殊的单击事件
    ·在Android系统中,关于屏幕上的操作都属于Touch操作,例如单击则是由:“按下 -> 弹起”这2种动作组合完成的。
    ·单击事件是直接消费事件,因此,当点击界面上的Button时,一定是由Button处理事件,且其父级控件无法对单击事件进行相应。

  • GestureDetector

一个activity中new一个GestureDetector之后,里面会实现一下的方法:
onSingleTapUp
onShowPress
onScroll
onLongPress
onFling
onDown

说一下这些方法都是由那些MotionEvent触发的
今天的项目用到了onSingleTapUp和onShowPress和onLongPress和onFling;
1、onSingleTapUp、onSingleTapConfirmed
用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发
这个事件执行的顺序是onDown-》onShowPress-》onSingleTapUp
区别:
点击一下非常快的(不滑动)Touchup:onDown->onSingleTapUp->onSingleTapConfirmed
点击一下稍微慢点的(不滑 动)Touchup:onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
2、onShowPress
用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发
它与onDown()的区别,强调的是没有松开或者拖动的状态
3、onDown
而onDown也是由一个MotionEventACTION_DOWN触发的,但是他没有任何限制,也就是说当用户点击的时候,首先MotionEventACTION_DOWN,onDown就会执行,如果在按下的瞬间没有松开或者是拖动的时候onShowPress就会执行,如果是按下的时间超过瞬间(这块我也不太清楚瞬间的时间差是多少,一般情况下都会执行onShowPress),拖动了,就不执行onShowPress。
4、onLongPress
用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发
这个事件执行的顺序是onDown-》onShowPress-》onLongPress
5、onFling
用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE,1个ACTION_UP触发
6、onScroll
Touch了 滑动时触发。

  • 示例:

activity:

package com.example.touch_event;import android.os.Bundle;import android.app.Activity;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.view.View.OnTouchListener;public class MainActivity extends Activity {    private String Tag = "com.example.touch_event";    private TouchView touchView;    private GestureDetector gestureDetector;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        touchView = (TouchView) findViewById(R.id.touchView1);        gestureDetector = new GestureDetector(new InnerGestureDetector());        touchView.setOnTouchListener(new OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                switch (event.getAction()) {                case MotionEvent.ACTION_DOWN:                    Log.d(Tag  , "setOnTouchListener.onTouchEvent--->ACTION_DOWN");                    break;                case MotionEvent.ACTION_UP:                    Log.d(Tag, "setOnTouchListener.onTouchEvent--->ACTION_UP");                    break;                case MotionEvent.ACTION_MOVE:                    Log.d(Tag, "setOnTouchListener.onTouchEvent--->ACTION_MOVE");                    break;                }                return gestureDetector.onTouchEvent(event);            }        });    }    private class InnerGestureDetector implements GestureDetector.OnGestureListener{        //用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发        @Override        public boolean onDown(MotionEvent e) {            // TODO Auto-generated method stub            return false;        }        //用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE,1个ACTION_UP触发        @Override        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,                float velocityY) {            // TODO Auto-generated method stub            return false;        }        //用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发        @Override        public void onLongPress(MotionEvent e) {            // TODO Auto-generated method stub        }        //Touch了 滑动时触发        @Override        public boolean onScroll(MotionEvent e1, MotionEvent e2,                float distanceX, float distanceY) {            // TODO Auto-generated method stub            return false;        }        //用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发        @Override        public void onShowPress(MotionEvent e) {            // TODO Auto-generated method stub        }        //用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发        @Override        public boolean onSingleTapUp(MotionEvent e) {            // TODO Auto-generated method stub            return false;        }    }    private long artionDownTime;    private float artionDownX,artionDownY;    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            artionDownTime = System.currentTimeMillis();            artionDownX = event.getX();            artionDownY = event.getY();            break;        case MotionEvent.ACTION_UP:            if(System.currentTimeMillis() - artionDownTime < 500){                if(Math.abs(event.getX() - artionDownX) < 30 && Math.abs(event.getY() - artionDownY) < 30){                    Log.d(Tag, "单击!!!...");                }            }            break;        }        /*switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            Log.i(Tag  , "MainActivity.onTouchEvent--->ACTION_DOWN");            break;        case MotionEvent.ACTION_UP:            Log.i(Tag, "MainActivity.onTouchEvent--->ACTION_UP");            break;        case MotionEvent.ACTION_MOVE:            Log.i(Tag, "MainActivity.onTouchEvent--->ACTION_MOVE");            break;        }*/        return gestureDetector.onTouchEvent(event);    }}

TouchView:

package com.example.touch_event;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.widget.TextView;public class TouchView extends TextView{    public TouchView(Context context) {        super(context);        // TODO Auto-generated constructor stub    }    public TouchView(Context context, AttributeSet attrs) {        super(context, attrs);        // TODO Auto-generated constructor stub    }    public TouchView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        // TODO Auto-generated constructor stub    }    @Override    public boolean onTouchEvent(MotionEvent event) {        /*switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            Log.w(Tag, "");            break;        case MotionEvent.ACTION_UP:            Log.w(Tag, "");            break;        case MotionEvent.ACTION_MOVE:            Log.w(Tag, "");            break;        }*/        return super.onTouchEvent(event);    }}

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: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=".MainActivity" >    <com.example.touch_event.TouchView        android:id="@+id/touchView1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:background="#000000"        android:textSize="24sp"        android:layout_centerHorizontal="true"        android:layout_centerVertical="true"        android:text="TouchView" /></RelativeLayout>
0 0