一、android处理事件有两种形式.

  1、基于监听的事件处理,就是通过setXxxListenter()进行处理的。

  2、基于回调的事件处理,就是View类内部的onTouchEvent(),一般是在自定义控件时重写的。

关于这些方法是在什么时候被触发的,下面是对部分源码的分析:

  1、首先:触摸事件会触发Activity的dispatchTouchEvent,

 public boolean dispatchTouchEvent(MotionEvent ev) {
// onUserInteraction默认不执行任何动作。
// 它是提供给使用者的接口。
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
// 这里会调用到ViewGroup的dispatchTouchEvent(),
// 即会调用Activity包含的根视图的dispatchTouchEvent()。
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
// 如果superDispatchTouchEvent()返回false,
// 即Activity的根视图以及根视图的子视图都没有拦截该事件的话,则调用Activity的onTouchEvent()
return onTouchEvent(ev);
}

Activity的dispatchTouchEvent

    对于getWindow().superDispatchTouchEvent(ev)一句追踪源码可以发现()这里调用了DecorView的superDispatchTouchEvent(),而DecorView是PhoneWindow的内部类,并且是Window界面的顶级View。

 private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
... public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
} ...
}

DecorView

    从上面的DecorView源码部分可以看到:通过DecorView,Activity的触摸事件将转发给GroupView的dispatchTouchEvent()进行处理。

    在Activity的dispatchTouchEvent()中,如果GroupView的dispatchTouchEvent()返回true则会拦截消耗事件,否则将执行Activity的onTouchEvent。

 public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
} return false;
} //上面会调用Window的shouldCloseOnTouch()
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
return true;
}
return false;
}

onTouchEvent

    上面可以看到:这个方法是对ctivity自身触摸事件的默认处理,可以用于关闭Dialog主题的Activity。

    !!!详询参见http://wangkuiwu.github.io/2015/01/02/TouchEvent-Activity/

   2、接着:由GroupView的dispatchTouchEvent()继续进行事件的分发

 public boolean dispatchTouchEvent(MotionEvent ev) {
// mInputEventConsistencyVerifier是调试用的,不会理会
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
} // 第1步:是否要分发该触摸事件
//
// onFilterTouchEventForSecurity()表示是否要分发该触摸事件。
// 如果该View不是位于顶部,并且有设置属性使该View不在顶部时不响应触摸事件,则不分发该触摸事件,即返回false。
// 否则,则对触摸事件进行分发,即返回true。
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK; // 第2步:检测是否需要清空目标和状态
//
// 如果是ACTION_DOWN(即按下事件),则清空之前的触摸事件处理目标和状态。
// 这里的情况状态包括:
// (01) 清空mFirstTouchTarget链表,并设置mFirstTouchTarget为null。
// mFirstTouchTarget是"接受触摸事件的View"所组成的单链表
// (02) 清空mGroupFlags的FLAG_DISALLOW_INTERCEPT标记
// 如果设置了FLAG_DISALLOW_INTERCEPT,则不允许ViewGroup对触摸事件进行拦截。
// (03) 清空mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVEN标记
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState();
} // 第3步:检查当前ViewGroup是否想要拦截触摸事件
//
// 是的话,设置intercepted为true;否则intercepted为false。
// 如果是"按下事件(ACTION_DOWN)" 或者 mFirstTouchTarget不为null;就执行if代码块里面的内容。
// 否则的话,设置intercepted为true。
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
// 检查禁止拦截标记:FLAG_DISALLOW_INTERCEPT
// 如果调用了requestDisallowInterceptTouchEvent()标记的话,则FLAG_DISALLOW_INTERCEPT会为true。
// 例如,ViewPager在处理触摸事件的时候,就会调用requestDisallowInterceptTouchEvent()
// ,禁止它的父类对触摸事件进行拦截
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 如果禁止拦截标记为false的话,则调用onInterceptTouchEvent();并返回拦截状态。
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
} // 第4步:检查当前的触摸事件是否被取消
//
// (01) 对于ACTION_DOWN而言,mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVENT位肯定是0;因此,canceled=false。
// (02) 当前的View或ViewGroup要被从父View中detach时,PFLAG_CANCEL_NEXT_UP_EVENT就会被设为true;
// 此时,它就不再接受触摸事情。
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL; // 第5步:将触摸事件分发给"当前ViewGroup的子View和子ViewGroup"
//
// 如果触摸"没有被取消",同时也"没有被拦截"的话,则将触摸事件分发给它的子View和子ViewGroup。
// 如果当前ViewGroup的孩子有接受触摸事件的话,则将该孩子添加到mFirstTouchTarget链表中。
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// 这是获取触摸事件的序号 以及 触摸事件的id信息。
// (01) 对于ACTION_DOWN,actionIndex肯定是0
// (02) 而getPointerId()是获取的该触摸事件的id,并将该id信息保存到idBitsToAssign中。
// 这个触摸事件的id是为多指触摸而添加的;对于单指触摸,getActionIndex()返回的肯定是0;
// 而对于多指触摸,第一个手指的id是0,第二个手指的id是1,第三个手指的id是2,...依次类推。
final int actionIndex = ev.getActionIndex();
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS; // 清空这个手指之前的TouchTarget链表。
// 一个TouchTarget,相当于一个可以被触摸的对象;它中记录了接受触摸事件的View
removePointersFromTouchTargets(idBitsToAssign); // 获取该ViewGroup包含的View和ViewGroup的数目,
// 然后递归遍历ViewGroup的孩子,对触摸事件进行分发。
// 递归遍历ViewGroup的孩子:是指对于当前ViewGroup的所有孩子,都会逐个遍历,并分发触摸事件;
// 对于逐个遍历到的每一个孩子,若该孩子是ViewGroup类型的话,则会递归到调用该孩子的孩子,...
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
final View[] children = mChildren; final boolean customOrder = isChildrenDrawingOrderEnabled();
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ?
getChildDrawingOrder(childrenCount, i) : i;
final View child = children[childIndex];
// 如果child可以接受触摸事件,
// 并且触摸坐标(x,y)在child的可视范围之内的话;
// 则继续往下执行。否则,调用continue。
// child可接受触摸事件:是指child的是可见的(VISIBLE);或者虽然不可见,但是位于动画状态。
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
} // getTouchTarget()的作用是查找child是否存在于mFirstTouchTarget的单链表中。
// 是的话,返回对应的TouchTarget对象;否则,返回null。
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
} // 重置child的mPrivateFlags变量中的PFLAG_CANCEL_NEXT_UP_EVENT位。
resetCancelNextUpFlag(child); // 调用dispatchTransformedTouchEvent()将触摸事件分发给child。
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 如果child能够接受该触摸事件,即child消费或者拦截了该触摸事件的话;
// 则调用addTouchTarget()将child添加到mFirstTouchTarget链表的表头,并返回表头对应的TouchTarget
// 同时还设置alreadyDispatchedToNewTouchTarget为true。
mLastTouchDownTime = ev.getDownTime();
mLastTouchDownIndex = childIndex;
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
} // 如果newTouchTarget为null,并且mFirstTouchTarget不为null;
// 则设置newTouchTarget为mFirstTouchTarget链表中第一个不为空的节点。
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
} // 第6步:进一步的对触摸事件进行分发
//
// (01) 如果mFirstTouchTarget为null,意味着还没有任何View来接受该触摸事件;
// 此时,将当前ViewGroup看作一个View;
// 将会调用"当前的ViewGroup的父类View的dispatchTouchEvent()"对触摸事件进行分发处理。
// 即,会将触摸事件交给当前ViewGroup的onTouch(), onTouchEvent()进行处理。
// (02) 如果mFirstTouchTarget不为null,意味着有ViewGroup的子View或子ViewGroup中,
// 有可以接受触摸事件的。那么,就将触摸事件分发给这些可以接受触摸事件的子View或子ViewGroup。
if (mFirstTouchTarget == null) {
// 注意:这里的第3个参数是null
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
} // 第7步:再次检查取消标记,并进行相应的处理
//
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
} // mInputEventConsistencyVerifier是调试用的,不会理会
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}

GroupView的dispatchTouchEvent

    上面还需注意的就是第5步:在分发事件时,除了判断是否被拦截或取消后,内部又判断了是否是Down事件,也就是只有在Down事件没有被拦截或取消,才会向其分发事件。

参考:http://wangkuiwu.github.io/2015/01/04/TouchEvent-ViewGroup/

    3、最后,触摸事件会触发view中的dispatchTouchEvent(),如果没有子类中没有重写,则会向上寻找一个执行。下面是View中的部分源码,就是具体执行事件的部分

  ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
} if (!result && onTouchEvent(event)) {
result = true;
}

顶级View的dispatchTouchEvent

  上面是23版本的片段,这部分代码写的比较巧妙,第一个if中:

    1、li != null,这是ListenerInfo对象,保存各种监听器实例的对象,只要有一个监听器被设置,则该对象不为空。

     static class ListenerInfo {
protected OnFocusChangeListener mOnFocusChangeListener; private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners; protected OnScrollChangeListener mOnScrollChangeListener; private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners; public OnClickListener mOnClickListener; protected OnLongClickListener mOnLongClickListener; protected OnContextClickListener mOnContextClickListener; protected OnCreateContextMenuListener mOnCreateContextMenuListener; private OnKeyListener mOnKeyListener; private OnTouchListener mOnTouchListener; private OnHoverListener mOnHoverListener; private OnGenericMotionListener mOnGenericMotionListener; private OnDragListener mOnDragListener; private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener; OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
} ListenerInfo mListenerInfo; public void setOnTouchListener(OnTouchListener l) {
//在设置监听器实例时,这里会调用getListenerInfo()判断是否有ListenerInfo实例存在
getListenerInfo().mOnTouchListener = l;
} ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}

mListenerInfo部分

    2、li.mOnTouchListener != null,这是判断是否有TouchListener,这是通过setOnTouchListener()设置的。

    3、(mViewFlags & ENABLED_MASK) == ENABLED,这是判断View的mViewFlags是否是ENABLED。注意:这和View是否可点击不同,可点击是CLICKABLE=true

     //检测是否是ENABLED,获取和设置都与mViewFlags有关
public boolean isEnabled() {
return (mViewFlags & ENABLED_MASK) == ENABLED;
} public void setEnabled(boolean enabled) {
if (enabled == isEnabled()) return; setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK); //下面是与点击有关的获取和设置
public boolean isClickable() {
return (mViewFlags & CLICKABLE) == CLICKABLE;
} public void setClickable(boolean clickable) {
setFlags(clickable ? CLICKABLE : 0, CLICKABLE);
} public boolean isLongClickable() {
return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
} public void setLongClickable(boolean longClickable) {
setFlags(longClickable ? LONG_CLICKABLE : 0, LONG_CLICKABLE);
} public boolean isContextClickable() {
return (mViewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
} public void setContextClickable(boolean contextClickable) {
setFlags(contextClickable ? CONTEXT_CLICKABLE : 0, CONTEXT_CLICKABLE);
} //注意
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}

Enable和Clickable的区别

    !!!注意:在setOnClickListener中会修改Clickable属性,即原本不能点击的View,如果添加点击的监听器,则可以点击。

    4、li.mOnTouchListener.onTouch(this, event),就是判断添加的监听事件额返回值。

    !!!只有当上面四个都是true时,设置result=true。而这个值又影响到下一个判断的执行,因为用到&&(这个操作符比较特殊,如果前一个操作数为true,则继续向后判断;如果前一个为false,则不执行后续判断直接返回false),所以前面是false这直接退出执行,否则会触发View的onTouchEvent(event)。该方法比较重要的两步见下面说明。需要注意的是:这个方法的返回值为true,则事件被消耗,否则将继续!!向后(其他的点击事件)也包括向!!父布局传递。

 public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags; // 如果View被禁用的话,则返回它是否可以点击。
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
} // 如果该View的mTouchDelegate不为null的话,将触摸消息分发给mTouchDelegate。
// mTouchDelegate的默认值是null。
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
} // 如果View可以被点击的话,则执行if里面的内容。
// 这其中涉及到的主要是获取焦点,设置按下状态,触发onClick(), onLongClick()事件等等。
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
。。。
}
return true;
} return false;
}

View的onTouchEvent

    这个方法的返回值总结:

      a、该View不可用,即(viewFlags & ENABLED_MASK) == DISABLED,则返回是否可点击或长击。如果可用则向下判断

      b、mTouchDelegate如果不为null,则由其进行处理并返回结果,否则继续向下判断

      c、到这这一步,只要View可以点击或长击则返回true,否则返回false。

下图是View事件分发的全过程图示,重点是事件首先是由顶级布局获取的,而不是其中的子View.

 package com.dqxst.first.view;

 import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout; import com.dqxst.first.R;
import com.dqxst.first.myui.MyBtn; public class EventActivity extends Activity{
private final static String TAG="EventActivity";
private LinearLayout parent;
private Button btn;
private MyBtn myBtn;
private ImageView image; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event);
findView();
event();
Log.i(TAG, "enabled="+image.isEnabled());
Log.i(TAG, "LongClickable="+image.isLongClickable());
} private void event() {
parent.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "父布局的OnClickListener被触发。。。");
}
});
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "点击btn,OnClickListener。。。");
}
});
btn.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(TAG, "点击btn,OnTouchListener。。。,事件action="+event.getAction());
return false;
}
}); myBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "点击myBtn,OnClickListener。。。");
}
});
myBtn.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(TAG, "点击myBtn,OnTouchListener。。。,事件action="+event.getAction());
return false;
}
});
image.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "点击image,OnClickListener。。。");
}
});
image.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(TAG, "点击image,OnTouchListener。。。,事件action="+event.getAction());
return false;
}
});
} //上面已经有setOnClickListener,所以该方法不执行
public void btn_click(View view){
Log.i(TAG, "点击btn,通过xml绑定的onclick事件处理");
} private void findView() {
parent=(LinearLayout) findViewById(R.id.parent);
btn=(Button) findViewById(R.id.btn);
myBtn=(MyBtn) findViewById(R.id.mybtn);
image=(ImageView) findViewById(R.id.event_image);
}
}

事件处理的例子

参考:http://blog.csdn.net/guolin_blog/article/details/9097463http://blog.csdn.net/guolin_blog/article/details/9153747

二、MotionEvent触摸事件,这是上面事件分发之后最终在OnTouchListener()执行的事件对象。通过这个对象可以获取当前的触摸事件。

  1、单手操作:通过调用getAction()可以获取事件标识,可以和MotionEvent中定义的事件标识进行匹配。

  2、多手操作:如果需要判断多手操作需要event.getAction() & MotionEvent.ACTION_MASK来获取事件。

在MotionEvent对象中最重要的属性就是触发事件的X,Y坐标。通过getX()/getY()获取。多点触控通过getX(int pointerIndex) ,来获得对应手指事件的发生位置. 获得Y轴用getY(int pointerIndex)

参考:http://www.runoob.com/w3cnote/android-tutorial-touchlistener-ontouchevent.html

http://my.oschina.net/banxi/blog/56421

三、手势事件:其实就是android提供的封装了对MotionEvent事件的处理,把一些连续的操作响应为手势(当然也可以手动处理,但是比较麻烦)。主要用到的是GestureDetector类,使用过程分三步:

  1、创建手势监听器对象,这是通过集成/实现该类中的接口/实现类来完成的,比如OnGestureListener接口/SimpleOnGestureListener类。

  2、创建GestureDetector对象,需要通过构造器传入上面的手势监听器对象。

  3、将事件转交给该对象,通过GestureDetector.onTouch()进行事件的处理。

需要注意的是,手势事件可以针对整个activity或者其中一个View。

  1、如果针对Activity,那就不需要为其中的View添加事件监听,直接重写onTouchEvent()将事件转发即可。

  2、如果是针对特定View,那就需要给其添加OnTouchListener监听器,在该监听器中进行转发,注意:需要返回值必须为true才能正确响应。

 package com.dqxst.first.view;

 import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout; import com.dqxst.first.R;
import com.dqxst.first.myui.MyBtn;
import com.dqxst.first.util.CommonUtils; public class EventActivity extends Activity implements OnTouchListener{
private final static String TAG="EventActivity";
private LinearLayout parent;
private View view;
private Button btn;
private MyBtn myBtn;
private ImageView image; private MyGestureListener mgListener;
private GestureDetector mDetector; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event); //实例化GestureListener与GestureDetector对象
mgListener = new MyGestureListener();
mDetector = new GestureDetector(this, mgListener); findView(); // image.setOnTouchListener(this);
} private void findView() {
parent=(LinearLayout) findViewById(R.id.parent);
view=findViewById(R.id.event_test);
btn=(Button) findViewById(R.id.btn);
myBtn=(MyBtn) findViewById(R.id.mybtn);
image=(ImageView) findViewById(R.id.event_image);
} //如果是针对某一View,则使用下面的监听器,然后给相应的View进行set即可
@Override
public boolean onTouchEvent(MotionEvent event) {
return mDetector.onTouchEvent(event);
} @Override
// public boolean onTouch(View v, MotionEvent event) {
// mDetector.onTouchEvent(event);
// return true;
// } //自定义一个GestureListener,这个是View类下的,别写错哦!!!
private class MyGestureListener implements OnGestureListener { @Override
public boolean onDown(MotionEvent motionEvent) {
Log.d(TAG, "onDown:按下");
CommonUtils.toastShow(getApplicationContext(), "onDown:按下");
return false;
} @Override
public void onShowPress(MotionEvent motionEvent) {
Log.d(TAG, "onShowPress:手指按下一段时间,不过还没到长按");
CommonUtils.toastShow(getApplicationContext(), "onShowPress:手指按下一段时间,不过还没到长按");
} @Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
Log.d(TAG, "onSingleTapUp:手指离开屏幕的一瞬间");
CommonUtils.toastShow(getApplicationContext(), "onSingleTapUp:手指离开屏幕的一瞬间");
return false;
} @Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
Log.d(TAG, "onScroll:在触摸屏上滑动");
CommonUtils.toastShow(getApplicationContext(), "onScroll:在触摸屏上滑动");
return false;
} @Override
public void onLongPress(MotionEvent motionEvent) {
Log.d(TAG, "onLongPress:长按并且没有松开");
CommonUtils.toastShow(getApplicationContext(), "onLongPress:长按并且没有松开");
} @Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
Log.d(TAG, "onFling:迅速滑动,并松开");
CommonUtils.toastShow(getApplicationContext(), "onFling:迅速滑动,并松开");
return false;
}
} }

手势操作实例

上面是基本使用。除此之外还可以使用GestureOverlayView编辑手势,使用GestureLibraries(手势库)来添加(刚才编辑的手势)或者获取手势。

参考:http://www.runoob.com/w3cnote/android-tutorial-gestures.html

package com.dqxst.first.view;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.dqxst.first.R;
import com.dqxst.first.myui.MyBtn;
import com.dqxst.first.util.CommonUtils;

public class EventActivity extends Activity implements OnTouchListener{
    private final static String TAG="EventActivity";
    private LinearLayout parent;
    private View view;
    private Button btn;
    private MyBtn myBtn;
    private ImageView image;
    
    private MyGestureListener mgListener;
    private GestureDetector mDetector;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event);
        
        //实例化GestureListener与GestureDetector对象
        mgListener = new MyGestureListener();
        mDetector = new GestureDetector(this, mgListener);
        
        findView();
//        event();
        
        Log.i(TAG, "enabled="+view.isEnabled());
        Log.i(TAG, "click="+view.isClickable());
        Log.i(TAG, "Longclick="+view.isLongClickable());
        image.setOnTouchListener(this);
    }

private void event() {
        parent.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "父布局的OnClickListener被触发。。。");
            }
        });
        btn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "点击btn,OnClickListener。。。");
            }
        });
        btn.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "点击btn,OnTouchListener。。。,事件action="+event.getAction());
                return false;
            }
        });

myBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "点击myBtn,OnClickListener。。。");
            }
        });
        myBtn.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "点击myBtn,OnTouchListener。。。,事件action="+event.getAction());
                return false;
            }
        });
        image.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "点击image,OnClickListener。。。");
            }
        });
        image.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "点击image,OnTouchListener。。。,事件action="+event.getAction());
                return false;
            }
        });
    }
    
    //上面已经有setOnClickListener,所以该方法不执行
    public void btn_click(View view){
        Log.i(TAG, "点击btn,通过xml绑定的onclick事件处理");
    }

private void findView() {
        parent=(LinearLayout) findViewById(R.id.parent);
        view=findViewById(R.id.event_test);
        btn=(Button) findViewById(R.id.btn);
        myBtn=(MyBtn) findViewById(R.id.mybtn);
        image=(ImageView) findViewById(R.id.event_image);
    }

//    @Override
//    public boolean onTouchEvent(MotionEvent event) {
//        return mDetector.onTouchEvent(event);
//    }

@Override
    public boolean onTouch(View v, MotionEvent event) {
        mDetector.onTouchEvent(event);
        return true;
    }
    
    //自定义一个GestureListener,这个是View类下的,别写错哦!!!
    private class MyGestureListener implements OnGestureListener {

@Override
        public boolean onDown(MotionEvent motionEvent) {
            Log.d(TAG, "onDown:按下");
            CommonUtils.toastShow(getApplicationContext(), "onDown:按下");
            return false;
        }

@Override
        public void onShowPress(MotionEvent motionEvent) {
            Log.d(TAG, "onShowPress:手指按下一段时间,不过还没到长按");
            CommonUtils.toastShow(getApplicationContext(), "onShowPress:手指按下一段时间,不过还没到长按");
        }

@Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            Log.d(TAG, "onSingleTapUp:手指离开屏幕的一瞬间");
            CommonUtils.toastShow(getApplicationContext(), "onSingleTapUp:手指离开屏幕的一瞬间");
            return false;
        }

@Override
        public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            Log.d(TAG, "onScroll:在触摸屏上滑动");
            CommonUtils.toastShow(getApplicationContext(), "onScroll:在触摸屏上滑动");
            return false;
        }

@Override
        public void onLongPress(MotionEvent motionEvent) {
            Log.d(TAG, "onLongPress:长按并且没有松开");
            CommonUtils.toastShow(getApplicationContext(), "onLongPress:长按并且没有松开");
        }

@Override
        public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            Log.d(TAG, "onFling:迅速滑动,并松开");
            CommonUtils.toastShow(getApplicationContext(), "onFling:迅速滑动,并松开");
            return false;
        }
    }

}

android事件学习的更多相关文章

  1. Android 进阶学习:事件分发机制全然解析,带你从源代码的角度彻底理解(上)

    http://blog.csdn.net/guolin_blog/article/details/9097463 事实上我一直准备写一篇关于Android事件分发机制的文章,从我的第一篇博客開始,就零 ...

  2. Android事件分发机制浅谈(一)

    ---恢复内容开始--- 一.是什么 我们首先要了解什么是事件分发,通俗的讲就是,当一个触摸事件发生的时候,从一个窗口到一个视图,再到一个视图,直至被消费的过程. 二.做什么 在深入学习android ...

  3. 讲讲Android事件拦截机制

    简介 什么是触摸事件?顾名思义,触摸事件就是捕获触摸屏幕后产生的事件.当点击一个按钮时,通常会产生两个或者三个事件--按钮按下,这是事件一,如果滑动几下,这是事件二,当手抬起,这是事件三.所以在And ...

  4. Android自动化学习笔记之MonkeyRunner:官方介绍和简单实例

    ---------------------------------------------------------------------------------------------------- ...

  5. Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition

    Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition Property animation系统还提供了对ViewGroup中的View改变 ...

  6. Android Animation学习(三) ApiDemos解析:XML动画文件的使用

    Android Animation学习(三) ApiDemos解析:XML动画文件的使用 可以用XML文件来定义Animation. 文件必须有一个唯一的根节点: <set>, <o ...

  7. Android Animation学习(二) ApiDemos解析:基本Animators使用

    Android Animation学习(二) ApiDemos解析:基本Animatiors使用 Animator类提供了创建动画的基本结构,但是一般使用的是它的子类: ValueAnimator.O ...

  8. Android Testing学习01 介绍 测试测什么 测试的类型

    Android Testing学习01 介绍 测试测什么 测试的类型 Android 测试 测什么 1.Activity的生命周期事件 应该测试Activity的生命周期事件处理. 如果你的Activ ...

  9. Android动画学习笔记-Android Animation

    Android动画学习笔记-Android Animation   3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中 ...

随机推荐

  1. 常见VPS buy地址

    ***,也是最适合新手使用的: https://bwh1.net/ (支持支付宝) vultr,以下是我的分享链接: https://www.vultr.com/(支持支付宝) SugarHosts: ...

  2. libTIFF 图像读取与保存

    本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/YhL_Leo/article/details/49848391 1 头文件 libtif ...

  3. Mybaties下的分页功能的实现

    jsp页面 <!-- 页码 --> <div class="ipRListNav2"> <a href="zyxx.do?findZyxx& ...

  4. Fiddler(Web/HTTP调试利器)

    简述 Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的"进出"Fiddler的数据(指cookie.htm ...

  5. [Webpack + React] Import CSS Modules with TypeScript and webpack

    If you try to use CSS Modules in TypeScript the same way you would use them in JavaScript, with webp ...

  6. opencv矩阵运算(2)

    简单介绍 本篇承接上一篇.继续opencv下矩阵计算的函数使用. 计算矩阵的逆 注意:矩阵A是可逆矩阵的充分必要条件是行列式detA不等于0. 详细代码 double x[3][3] = {{1, 2 ...

  7. vue2.0 路由学习笔记

    昨天温故了一下vue2.0的路由 做个笔记简单记录一下! 1.首相和vue1.0一样 要使用vuejs的路由功能需要先引入vue-router.js 2.然后修改原有a标签处代码 这里以一个ul li ...

  8. nyoj--311--完全背包(动态规划,完全背包)

    完全背包 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描述 直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用.第i种物品的体积是c,价值是w. ...

  9. Hadoop的目录结构

  10. css常用的阴影

    一.box-shadow: 0 2px 15px 0 rgba(0,0,0,.15)!important 二. box-shadow: 0 2px 6px 0 rgba(0,0,0,.4); 三. . ...