一、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. python 面向对象 类方法,静态方法,property

    property 内置装饰器函数 只在面向对象使用 把方法当初属性使用(方法不加参数) 例子: class Rectangle: def __init__(self,long,wide,color): ...

  2. C/C++中相对路径与绝对路径以及斜杠与反斜杠的区别

    1 绝对路径与相对路径 绝对路径表示相对容易得多,依次将文件所在盘符文件夹逐级展开就是绝对路径: ofstream infile("E:\\MyDoc\\file.txt", io ...

  3. XUtils3框架的基本用法(一)

    本文为作者原创,转载请指明出处: http://blog.csdn.net/a1002450926/article/details/50341173 今天给大家带来XUtils3的基本介绍.本文章的案 ...

  4. 8.6 First_value和Last_value

    8.6 First_value和Last_value正在更新内容.请稍后

  5. spark groupByKey 也是可以filter的

    >>> v=sc.parallelize(["one", "two", "two", "three", ...

  6. channels2.X 学习笔记

    - No module named 'asgiref.sync' 报错解决: # 报错原因: """ django版本过低, 卸载最新版本的 channels 使用2.x ...

  7. Java 类和对象12

    构造一辆汽车,油箱容量100L,当前里程数0,当前油量0,可以根据道路状况确定油耗,根据行驶速度与行驶时间, 输出当前油量与总里程数. public class Car_1 { // 车牌 priva ...

  8. Java中MySQL事务处理举例

    实例(以sql语句中的insert语句为例) import java.sql.Connection; import java.sql.DriverManager; import java.sql.Pr ...

  9. Asp.Net 中使用 水晶报表(上)

    Asp.Net中使用水晶报表(上) 在我们对VS.Net中的水晶报表(Crystal Reports)进行研究之前,我和我朋友对如何将这个复杂的东东加入我们的Web应用有着非常的好奇心.一周以后,在阅 ...

  10. Audio / Video Playback

    For Developers‎ > ‎Design Documents‎ > ‎ Audio / Video Playback Interested in helping out?  Ch ...