作为一名 Android 开发者,每天接触最多的就是 View 了。Android View 虽然不是四大组件,但其并不比四大组件的地位低。而 View 的核心知识点事件分发机制则是不少刚入门同学的拦路虎,也是面试过程中基本上都会问的。理解 View 的事件能够让你写出更好自定义 View 以及解决滑动冲突。

1、 View 事件认识

1.1 MotionEvent 事件

当你用手指轻触屏幕,这个过程在 Android 中主要可以分为以下三个过程:

  • ACTION_DOWN:手指刚接触屏幕,按下去的那一瞬间产生该事件

  • ACTION_MOVE:手指在屏幕上移动时候产生该事件

  • ACTION_UP:手指从屏幕上松开的瞬间产生该事件

从 ACTION_DOWN 开始到 ACTION_UP 结束我们称为一个事件序列

正常情况下,无论你手指在屏幕上有多么骚的操作,最终呈现在 MotionEvent 上来讲无外乎下面两种动作。

  • 点击(点击后抬起,也就是单击操作):ACTION_DOWN -> ACTION_UP

  • 滑动(点击后再滑动一段距离,再抬起):ACTION_DOWN -> ACTION_MOVE -> ... -> ACTION_MOVE -> ACTION_UP

1.2  理论知识

  • public boolean dispatchTouchEvent(MotionEvent ev)

    return true: 表示消耗了当前事件,有可能是当前 View 的 onTouchEvent 或者是子 View 的 dispatchTouchEvent 消费了,事件终止,不再传递。

    return false: 调用父 ViewGroup 或 Activity 的 onTouchEvent。 (不再往下传)。

    return super.dispatherTouchEvent: 则继续往下(子 View )传递,或者是调用当前 View 的 onTouchEvent 方法;

总结:用来分发事件,即事件序列的大门,如果事件传递到当前 View 的 onTouchEvent 或者是子 View 的 dispatchTouchEvent,即该方法被调用了。 另外如果不消耗 ACTION_DOWN 事件,那么 down, move, up 事件都与该 View 无关,交由父类处理(父类的 onTouchEvent 方法)

  • public boolean onInterceptTouchEvent(MotionEvent ev)

    return true: ViewGroup 将该事件拦截,交给自己的 onTouchEvent 处理。

    return false: 继续传递给子元素的 dispatchTouchEvent 处理。

    return super.dispatherTouchEvent: 事件默认不会被拦截。

总结:在 dispatchTouchEvent 内部调用,顾名思义就是判断是否拦截某个事件。(注:ViewGroup 才有的方法,View 因为没有子View了,所以不需要也没有该方法) 。而且这一个事件序列(当前和其它事件)都只能由该 ViewGroup 处理,并且不会再调用该 onInterceptTouchEvent 方法去询问是否拦截。

  • public boolean onTouchEvent(MotionEvent ev)

    return true: 事件消费,当前事件终止。

    return false: 交给父 View 的 onTouchEvent

    return super.dispatherTouchEvent: 默认处理事件的逻辑和返回 false 时相同。

总结:dispatchTouchEvent内部调用

上面三个方法之间的调用关系可以用下面的代码表示:

public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;//事件是否被消费
if (onInterceptTouchEvent(ev)){//调用 onInterceptTouchEvent 判断是否拦截事件
consume = onTouchEvent(ev);//如果拦截则调用自身的onTouchEvent方法
}else{
consume = child.dispatchTouchEvent(ev);//不拦截调用子View的dispatchTouchEvent方法
}
return consume;//返回值表示事件是否被消费,true事件终止,false调用父View的onTouchEvent方法

1.3 事件传递顺序

对于一个点击事件,Activity 会先收到事件的通知,接着再将其传给 DecorView(根 view),通过 DecorView 在将事件逐级进行传递。具体传递逻辑见下图:

可以看出事件的传递过程都是从父 View 到子 View,如果都没处理,最终还是会交由 activity 处理。但是这里有三点需要特别强调一下

  • 子 View 可以通过 requestDisallowInterceptTouchEvent 方法干预父 View 的事件分发过程( ACTION_DOWN 事件除外),而这就是我们处理滑动冲突常用的关键方法。

  • 对于 View(注意!ViewGroup 也是 View)而言,如果设置了onTouchListener,那么 OnTouchListener 方法中的 onTouch 方法会被回调。onTouch 方法返回 true,则 onTouchEvent 方法不会被调用(onClick 事件是在 onTouchEvent 中调用)所以三者优先级是 onTouch->onTouchEvent->onClick

  • View 的 onTouchEvent 方法默认都会消费掉事件(返回 true),除非它是不可点击的(clickable 和 longClickable 同时为 false),View 的longClickable 默认为 false,clickable 需要区分情况,如 Button 的 clickable 默认为 true,而TextView的 clickable 默认为 false。

2、View 事件分发源码

先从 Activity 中的 dispatchTouchEvent 方法出发:

    @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}

Activity 将事件传给父 Activity 来处理,下面看父 Activity 是怎么处理的。

 /**
* Called to process touch screen events. You can override this to
* intercept all touch screen events before they are dispatched to the
* window. Be sure to call this implementation for touch screen events
* that should be handled normally.
*
* @param ev The touch screen event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}

其中有个 onUserInteraction 方法,该方法是只要用户在 Activity 的任何一处点击或者滑动都会响应,一般不使用。接下去看getWindow().superDispatchTouchEvent(ev) 所代表的具体含义。getWindow() 返回对应的 Activity 的 window。一个Activity 对应一个 Window 也就是 PhoneWindow, 一个 PhoneWindow 持有一个 DecorView 的实例, DecorView 本身是一个 FrameLayout。这句话一定要牢记。

/**
* Retrieve the current {@link android.view.Window} for the activity.
* This can be used to directly access parts of the Window API that
* are not available through Activity/Screen.
*
* @return Window The current window, or null if the activity is not
* visual.
*/
public Window getWindow() {
return mWindow;
}

Window 的源码有说明 The only existing implementation of this abstract class is
android.view.PhoneWindow,Window 的唯一实现类是 PhoneWindow。那么去看 PhoneWindow 对应的代码。

public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}

PhoneWindow 又调用了 DecorView 的 superDispatchTouchEvent 方法。而这个 DecorView 就是 Window 的根 View,我们通过 setContentView 设置的 View 是它的子 View(Activity 的 setContentView,最终是调用 PhoneWindow 的 setContentView )

到这里事件已经被传递到根 View 中,而根 View 其实也是 ViewGroup。那么事件在 ViewGroup 中又是如何传递的呢?

2.1 ViewGroup 事件分发

public boolean dispatchTouchEvent(MotionEvent ev) {
...... final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
       // 当有 down 操作,会把之前的target 以及标志位都复位
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev); //清除 FLAG_DISALLOW_INTERCEPT,并且设置 mFirstTouchTarget 为 null
resetTouchState(){
if(mFirstTouchTarget!=null){mFirstTouchTarget==null;}
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
......
};
}
final boolean intercepted;//ViewGroup是否拦截事件 // mFirstTouchTarget是ViewGroup中处理事件(return true)的子View
//如果没有子View处理则mFirstTouchTarget=null,ViewGroup自己处理
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);//onInterceptTouchEvent
ev.setAction(action);
} else {
intercepted = false; //如果子类设置requestDisallowInterceptTouchEvent(true)
//ViewGroup将无法拦截MotionEvent.ACTION_DOWN以外的事件
}
} else {
intercepted = true; //actionMasked != MotionEvent.ACTION_DOWN并且没有子View处理事件,则将事件拦截
//并且不会再调用onInterceptTouchEvent询问是否拦截
} ......
......
}

先看标红的代码,这句话的意思是:当 ACTION_DOWN 事件到来时,或者有子元素处理事件( mFirstTouchTarget != null ),如果子 view 没有调用 requestDisallowInterceptTouchEvent 来阻止 ViewGroup 的拦截,那么 ViewGroup 的 onInterceptTouchEvent 就会被调用,来判断是否是要拦截。所以,当子 View 不让父 View 拦截事件的时候,即使父 View onInterceptTouchEvent 中返回true 也没用了。

这里需要注意的就是:onInterceptTouchEvent 默认返回 false。 当 ACTION_DOWN 事件到来时,此时 mFirstTouchTarget 为 null,此时其实也还未收到子 view requestDisallowInterceptTouchEvent。所以这时候,只要父 view 把 ACTION_DOWN 事件给拦截了,那么子 view 就收不到任何事件消息了。所以,一般在 ACTION_DOWN 的时候,父 view 不作拦截。

当 ACTION_MOVE 事件来临时,满足某些条件,父 view 想拦截的时候,这时候子 view 可以在 dispatchTouchEvent 中 ACTION_DOWN 事件来临的时候,调用 requestDisallowInterceptTouchEvent 就可以避免被父 view 拦截。

FLAG_DISALLOW_INTERCEPT 这个标记位就是通过子 View requestDisallowInterceptTouchEvent 方法设置的。 具体可参看如下代码。

    @Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
// We're already in this state, assume our ancestors are too
return;
} if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
} // Pass it up to our parent
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}

同时,如果这个 ViewGroup 有父 View 的时候,还得让父父 View 不能拦截。继续看 ViewGroup 的 dispatchTouchEvent 方法。

 public boolean dispatchTouchEvent(MotionEvent ev) {
final View[] children = mChildren; for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); ...... if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null))
{
ev.setTargetAccessibilityFocus(false);
//如果子View没有播放动画,而且点击事件的坐标在子View的区域内,继续下面的判断
continue;
}
//判断是否有子View处理了事件
newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) {
//如果已经有子View处理了事件,即mFirstTouchTarget!=null,终止循环。
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
} if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//点击dispatchTransformedTouchEvent代码发现其执行方法实际为
//return child.dispatchTouchEvent(event); (因为child!=null)
//所以如果有子View处理了事件,我们就进行下一步:赋值 ...... newTouchTarget = addTouchTarget(child, idBitsToAssign);
//addTouchTarget方法里完成了对mFirstTouchTarget的赋值
alreadyDispatchedToNewTouchTarget = true; break;
}
}
} private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
} private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
...... if (child == null) {
//如果没有子View处理事件,就自己处理
handled = super.dispatchTouchEvent(event);
} else {
//有子View,调用子View的dispatchTouchEvent方法
handled = child.dispatchTouchEvent(event); ...... return handled;
}

上面为 ViewGroup 对事件的分发,主要有 2 点

  • 如果有子 View,则调用子 View 的 dispatchTouchEvent 方法判断是否处理了事件,如果处理了便赋值 mFirstTouchTarget,赋值成功则跳出循环。

  • ViewGroup 的事件分发最终还是调用 View 的 dispatchTouchEvent 方法,具体如上代码所述。

onTouchEvent

该方法在 View 中实现,当然你也可以在子类中重写该方法。为了更好的理解 onTouchEvent 中对手势的处理规则。我们需要借助如下图:

 

图中 A 为根布局容器,B 是依赖于 A 的子布局容器,C 是 B 中的子 View,三者的位置关系是 A->B->C。
  关于处理规则需要注意两点:

  • 多个 View 间的 onTouchEvent 执行顺序
      当在 ViewGroup 中存在多个 View 或 ViewGroup 叠加显示时,gesture 事件将会以自上而下的顺序来执行,即最靠近屏幕的 C 先执行 onTouchEvent 方法,然后依次向下传递。即 C->B->A

  • 返回 Ture 还是 False:
      onTouchEvent 是带 boolean 型的返回参数,其返回值的意义在于是否消耗并处理该事件。在一个完整的 gesture 事件序列中,手势会从 DOWN 事件开始,如果在onTouchEvent 中返回的 true,那么该 gesture 的余下手势事件都会被该 View 处理消耗,不再向下传递相关事件。而当 onTouchEvent 在 DOWN 事件中返回了 false,即表示不关心该 gesture 事件序列,那么该 View 将不会接收到剩余的其它手势事件,并将其传递给下一个 View 进行处理。官方文档中给出的说明如下:

True if the event was handled, false otherwise.

onInterceptTouchEvent

上面说到 onTouchEvent 是至上而下传递的,靠近屏幕的 View 拥有处理手势事件的高优先级,那么有没有方法改变这种优先级顺序呢?比如图上 B 和 C 同时在 DOWN 事件中返回了ture,如何将事件分配给 B 而不是在最顶层的 C。ViewGroup 中提供了 onInterceptTouchEvent 方法,通过该方法,我们可以在 onTouchEvent 方法被调用之前去拦截该手势事件,拦截后的事件会被分配到该 View 下的 onTouchEvent 方法中去处理。与 onTouchEvent 相反,该方法的手势事件传递过程是自下而上,也就是从根 View 开始传递到上层的子 View。同时在 gesture 事件序列中,所有的手势事件都可以通过 onInterceptTouchEvent 方法被拦截,这样对于手势的处理有更大的自由度,而不是像 onTouchEvent 一样局限在 DOWN 事件下。
  通过 onTouchEvent 和 onInterceptTouchEvent 结合使用,手势事件实现了先上后下的一个过程,我们可以通过特定的方法在某个点进行事件的处理。下面总结了一张 Down 事件在上图中的传递过程的图,该过程中事件不被消耗,完整的被执行。

2.2 View 的事件分发

public boolean dispatchTouchEvent(MotionEvent event) {  

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

上述方法只有以下3个条件都为真,dispatchTouchEvent() 才返回 true;否则执行 onTouchEvent()。

  • mOnTouchListener != null

  • (mViewFlags & ENABLED_MASK) == ENABLED

  • mOnTouchListener.onTouch(this, event)

这也就说明如果调用了 setOnTouchListener 设置了 listener, 就会先调用 onTouch 方法。没有的话才会去调用 onTouchEvent 方法。接下去,我们看 onTouchEvent 源码。

public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
  // 如果进行了事件代理,就会被拦截,不会在往下面走了
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
} // 若该控件可点击,则进入switch判断中
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { // a. 若当前的事件 = 抬起View(主要分析)
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; ...// 经过种种判断,此处省略 // 执行performClick() ->>分析1
performClick();
break; // b. 若当前的事件 = 按下View
case MotionEvent.ACTION_DOWN:
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = false;
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
break; // c. 若当前的事件 = 结束事件(非人为原因)
case MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
removeTapCallback();
break; // d. 若当前的事件 = 滑动View
case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY(); int slop = mTouchSlop;
if ((x < 0 - slop) || (x >= getWidth() + slop) ||
(y < 0 - slop) || (y >= getHeight() + slop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
// Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
}
break;
}
// 若该控件可点击,就一定返回true
return true;
}
// 若该控件不可点击,就一定返回false
return false;
} /**
* 分析1:performClick()
*/
public boolean performClick() { if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
// 只要我们通过setOnClickListener()为控件View注册1个点击事件
// 那么就会给mOnClickListener变量赋值(即不为空)
// 则会往下回调onClick() & performClick()返回true
}
return false;
}

从上面的代码我们可以知道,当手指抬起的时候,也就是处于 MotionEvent.ACTION_UP 时,才会去调用 performClick()。而 performClick 中会调用 onClick  方法。

也就说明了:三者优先级是 onTouch->onTouchEvent->onClick

至此 View 的事件分发机制讲解完毕。

总结:

  1. 不要把拦截要做的事情放在 dispatchTouchEvent 中。如果 viewGroup 需要拦截事件,在 onInterceptTouchEvent 中在相应的事件返回 true 即可。拦截的操作写在 onTouchEvent 中。

  2. 如果 viewGroup 在 dispatchTouchEvent 返回 true 。这会导致 viewGroup 中的 dispatchTouchEvent 不执行。引起事件混乱。

  3. 只要 viewGroup 拦截 ACTION_DOWN,就所有的事件都会被拦截,从而响应子 view 的点击事件。

  4. 子 view 在 dispatchTouchEvent 中的 ACTION_DOWN 中调用 getParent().requestDisallowInterceptTouchEvent(true)  可避免被拦截。前提是父 view 不拦截 ACTION_DOWN 事件。

  5. 如果父 view 不拦截 ACTION_DOWN 事件,但拦截其他几个事件,这时候可以在 onTouchEvent 中写拦截后需要的操作。

  6. 父 view 开始不拦截 ACTION_DOWN 事件,然后在 ACTION_MOVE 的时候,进行拦截,这时候,子 view 的 dispatchTouchEvent 会收到 ACTION_CANCEL 事件,表示事件被终止了。

  7. 当父 view 拦截事件以后,其事件流程就跟普通的 view 一致了,不要在将其看做 ViewGroup。如果设置了 OnTouchListener 还会响应 onTouch 事件, 注意返回 false, 返回 true 将不会执行 onTouchEvent 。

滑动冲突解决方法

滑动冲突产生的原因:只要在界面中存在内外两层可以同时滑动,就会产生滑动冲突。如下所示:图1是左右滑动和上下滑动冲突,图二是两个view之间的上下滑动冲突;

                    

解决方案:根据实际情况,判断到底需要谁去响应滑动事件。

主要解决方式有两种,一种是外部拦截法,一种是内部拦截法。

外部拦截法:

看标题就应该可以知道,外部拦截法,就是通过父 view 来解决滑动冲突。 因为父 view 肯定属于 ViewGroup,所以父 view 根据自己需要来判断是否需要拦截事件。对于 ViewGroup,有个 onInterceptTouchEvent 方法,再需要拦截的时候,返回 true 即可。

    public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted=false;
int x= (int) event.getX();
int y= (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
intercepted=false;//必须不能拦截,否则后续的ACTION_MOME和ACTION_UP事件都会拦截。
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要当前点击事件){
intercepted=true;
}else {
intercepted=false;
}
break;
case MotionEvent.ACTION_UP:
intercepted=false;
break;
default:
break;
}
mLastXIntercept=x;
mLastXIntercept=y;
return intercepted;
}

内部拦截法

既然外部拦截法是子 view 主动处理拦截,那么内部拦截法就是需要子 view 来处理滑动冲突的情况。那么子view应该如何处理呢?首先子 view 在 dispatchTouchEvent 方法内部调用 requestDisallowInterceptTouchEvent 不让父 view 拦截事件,然后再 onTouchEvent 方法中处理需要拦截的情况。不拦截的时候返回 false,将事件交还给父 view 处理。

// 子 view   
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
//如果是左右滑动
if (Math.abs(deltaX) > Math.abs(deltaY)) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
case MotionEvent.ACTION_UP: {
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
}
mLastXIntercept = x;
mLastYIntercept = y;
return super.dispatchTouchEvent(ev);
}

同时为了避免父 view 消费事件,还需要在 DOWN 事件来临的时候,父 view 不会拦截,否则事件就不会传到子 view 了。

public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}

如果子 view 不处理,  父 view 会再次获得事件的处理权限。

参考文献:

1、Android View的事件分发机制和滑动冲突解决

2、一文读懂Android View事件分发机制

3、Android事件分发机制详解:史上最全面、最易懂

Android View 的事件分发原理解析的更多相关文章

  1. Android View的事件分发

    如果接触android开发时间足够长的话,或多或少都会遇到各种各样事件冲突的问题,要想解决这类问题,对深入理解事件分发机制是很有必要的,接下来几天都会尽自己所能尽可能将这方面讲清楚.  View的事件 ...

  2. Android View的事件分发机制

    准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup ...

  3. Android View的事件分发机制和滑动冲突解决方案

    这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...

  4. View的事件分发机制解析

    引言 Android事件构成 在Android中,事件主要包含点按.长按.拖拽.滑动等,点按又包含单击和双击,另外还包含单指操作和多指操作.全部这些都构成了Android中的事件响应.总的来说.全部的 ...

  5. Android view 的事件分发机制

    1 事件的传递顺序是 Activity -> Window -> 顶层View touch 事件产生后,最先由 activity 的 dispatchTouchEvent 处理 /** * ...

  6. Android View的事件分发机制探索

    概述 Android事件传递机制也是Android系统中比较重要的一块,事件类型有很多种,这里主要讨论TouchEvent的事件在framework层的传递处理机制.因为对于App开发人员来说,理解f ...

  7. 《Android View 的事件分发和滑动冲突》 —预习资料

    1. 阅读书籍<Android开发艺术探索>第三章 2. 提前阅读如下技术文章: http://blog.csdn.net/singwhatiwanna/article/details/3 ...

  8. 从源码的角度解析View的事件分发

    有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...

  9. Android事件分发机制详解(1)----探究View的事件分发

    探究View的事件分发 在Activity中,只有一个按钮,注册一个点击事件 [java] view plaincopy button.setOnClickListener(new OnClickLi ...

随机推荐

  1. docker之tomcat简单部署

    将apache-tomcat-8.0.36.tar.gz及jdk-7u79-linux-x64.gz拷贝到创建的tomcat8目录下 在tomcat8目录下创建Dockerfile文件 在Docker ...

  2. Python之路系列笔记

    备注:本套笔记内容来源于互联网,只做学习使用,如有侵权请联系本笔记作者. 资料内容 Python之路(一)——Python 初识 Python之路(二)——基础语法 Python之路(三)——函数 P ...

  3. python项目1:自动解压并删除压缩包

    目的:实现压缩包的自动解压及删除. 思路:获取压缩包 > 解压 > 删除压缩包 代码实现:此处代码实现前提为.py文件和压缩包在同一文件夹 # 导入需要的包 import os impor ...

  4. Java中的一个类型转换问题

    一.Object转Integer Java中hibernate或者ResultSetHandler查询sql语句, 返回的object类型其实是Long类型, 而不是Integer类型, 因此此时直接 ...

  5. openXML设置Excel行组合

    重要代码: row = new DOS.Row() { RowIndex = new DOX.UInt32Value(rowIndex), Spans = new DOX.ListValue<D ...

  6. 个人 WPF+EF(DBFirst) 简单应用开发习惯及EF学习测试(备忘) -- 2

    接上篇:个人 WPF+EF(DBFirst) 简单应用开发习惯及EF学习测试(备忘) -- 1 Step1 在主程序中设置连接数据库 从Model类库的 App.Config 把数据库字符串拷贝出来, ...

  7. poj 1050 最大子矩阵

    a11   a12   a13   a14   a15 a21   a22   a23   a24   a25 a31   a32   a33   a34   a35 a41   a42   a43  ...

  8. ACM计划

    原文 :http://027xbc.blog.163.com/blog/static/128159658201141371343475/ ACM主要是考算法的,主要时间是花在思考算法上,不是花在写程序 ...

  9. Android开发者的Anko使用指南(四)之Layouts

    为什么不使用xml绘制Andoird的UI? 类型不安全 非空不安全 xml迫使你在很多布局中写很多相同的代码 设备在解析xml时会耗费更多的cpu运行时间和电池 最重要的时,它允许任何代码重用 简单 ...

  10. 软件测试-homework3

    printPrime()代码: public static void printPrimes (int n) { int curPrime; // Value currently considered ...