android事件学习
一、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/9097463,http://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事件学习的更多相关文章
- Android 进阶学习:事件分发机制全然解析,带你从源代码的角度彻底理解(上)
http://blog.csdn.net/guolin_blog/article/details/9097463 事实上我一直准备写一篇关于Android事件分发机制的文章,从我的第一篇博客開始,就零 ...
- Android事件分发机制浅谈(一)
---恢复内容开始--- 一.是什么 我们首先要了解什么是事件分发,通俗的讲就是,当一个触摸事件发生的时候,从一个窗口到一个视图,再到一个视图,直至被消费的过程. 二.做什么 在深入学习android ...
- 讲讲Android事件拦截机制
简介 什么是触摸事件?顾名思义,触摸事件就是捕获触摸屏幕后产生的事件.当点击一个按钮时,通常会产生两个或者三个事件--按钮按下,这是事件一,如果滑动几下,这是事件二,当手抬起,这是事件三.所以在And ...
- Android自动化学习笔记之MonkeyRunner:官方介绍和简单实例
---------------------------------------------------------------------------------------------------- ...
- Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition
Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition Property animation系统还提供了对ViewGroup中的View改变 ...
- Android Animation学习(三) ApiDemos解析:XML动画文件的使用
Android Animation学习(三) ApiDemos解析:XML动画文件的使用 可以用XML文件来定义Animation. 文件必须有一个唯一的根节点: <set>, <o ...
- Android Animation学习(二) ApiDemos解析:基本Animators使用
Android Animation学习(二) ApiDemos解析:基本Animatiors使用 Animator类提供了创建动画的基本结构,但是一般使用的是它的子类: ValueAnimator.O ...
- Android Testing学习01 介绍 测试测什么 测试的类型
Android Testing学习01 介绍 测试测什么 测试的类型 Android 测试 测什么 1.Activity的生命周期事件 应该测试Activity的生命周期事件处理. 如果你的Activ ...
- Android动画学习笔记-Android Animation
Android动画学习笔记-Android Animation 3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中 ...
随机推荐
- python 面向对象 类方法,静态方法,property
property 内置装饰器函数 只在面向对象使用 把方法当初属性使用(方法不加参数) 例子: class Rectangle: def __init__(self,long,wide,color): ...
- C/C++中相对路径与绝对路径以及斜杠与反斜杠的区别
1 绝对路径与相对路径 绝对路径表示相对容易得多,依次将文件所在盘符文件夹逐级展开就是绝对路径: ofstream infile("E:\\MyDoc\\file.txt", io ...
- XUtils3框架的基本用法(一)
本文为作者原创,转载请指明出处: http://blog.csdn.net/a1002450926/article/details/50341173 今天给大家带来XUtils3的基本介绍.本文章的案 ...
- 8.6 First_value和Last_value
8.6 First_value和Last_value正在更新内容.请稍后
- spark groupByKey 也是可以filter的
>>> v=sc.parallelize(["one", "two", "two", "three", ...
- channels2.X 学习笔记
- No module named 'asgiref.sync' 报错解决: # 报错原因: """ django版本过低, 卸载最新版本的 channels 使用2.x ...
- Java 类和对象12
构造一辆汽车,油箱容量100L,当前里程数0,当前油量0,可以根据道路状况确定油耗,根据行驶速度与行驶时间, 输出当前油量与总里程数. public class Car_1 { // 车牌 priva ...
- Java中MySQL事务处理举例
实例(以sql语句中的insert语句为例) import java.sql.Connection; import java.sql.DriverManager; import java.sql.Pr ...
- Asp.Net 中使用 水晶报表(上)
Asp.Net中使用水晶报表(上) 在我们对VS.Net中的水晶报表(Crystal Reports)进行研究之前,我和我朋友对如何将这个复杂的东东加入我们的Web应用有着非常的好奇心.一周以后,在阅 ...
- Audio / Video Playback
For Developers > Design Documents > Audio / Video Playback Interested in helping out? Ch ...