版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[+]

转载请说明出处:http://blog.csdn.net/ff20081528/article/details/17353869
一、准备知识

1、视图坐标与布局坐标的区别如下图所示:

 上图是一个坐标系,这个坐标系是无边无际的。这个无边无际的坐标系即视图坐标。手机屏幕可视范围内的坐标即手机屏幕的布局坐标(坐标原点是屏幕的左上方的(0,0)位置)即A点。屏幕里面的子视图里面可视范围内的坐标即子视图的布局坐标(坐标原点是子视图的左上方的(0,0)位置)即B点。
2、android中布局关系
 
二、例子说明事件分发过程
这里我写了一个布局文件,展示效果如上图。当我点击View1,事件的分发过程是这样的:
 1、ViewGroup3的dispatchTouchEvent()方法会被调用。
  2、ViewGroup3调用ViewGroup2的dispatchTouchEvent()方法。
  3、ViewGroup2调用ViewGroup1的dispatchTouchEvent()方法。
  4、ViewGroup1会调用View1的dispatchTouchEvent()方法。
    5、View1的dispatchTouchEvent()方法调用自己的onTouchEvent()方法。在onTouchEvent方法中处理点击事件。处理完了后会返回一个true给调用它的dispatchTouchEvent()方法。
   6、ViewGroup1的dispatchTouchEvent()方法会返回一个true值给ViewGroup2的dispatchTouchEvent()方法。这样一直将则个true值返回到ViewGroup3的dispatchTouchEvent()方法。ViewGroup3在将这个值返回给调用它的方法。这样一个事件分发过程结束。
转载请说明出处:http://blog.csdn.net/ff20081528/article/details/17353869
 
三、ViewGroup中处理消息的详细过程
    通过上面的列子对事件分发的过程有个大概的了解之后,我们通过ViewGroup中的dispatchTouchEvent()方法源码和View中的dispatchTouchEvent()方法及touchEvent方法源码来详细了解android是怎样处理这个过程的。
  ViewGroup中的dispatchTouchEvent()方法源码如下:
[java] view plaincopy
  1. @Override
  2. public boolean dispatchTouchEvent(MotionEvent ev) {
  3. if (!onFilterTouchEventForSecurity(ev)) {
  4. return false;
  5. }
  6. final int action = ev.getAction();
  7. final float xf = ev.getX();
  8. final float yf = ev.getY();
  9. final float scrolledXFloat = xf + mScrollX;
  10. final float scrolledYFloat = yf + mScrollY;
  11. final Rect frame = mTempRect;
  12. boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
  13. if (action == MotionEvent.ACTION_DOWN) {
  14. if (mMotionTarget != null) {
  15. // this is weird, we got a pen down, but we thought it was
  16. // already down!
  17. // XXX: We should probably send an ACTION_UP to the current
  18. // target.
  19. mMotionTarget = null;
  20. }
  21. // If we're disallowing intercept or if we're allowing and we didn't
  22. // intercept
  23. if (disallowIntercept || !onInterceptTouchEvent(ev)) {
  24. // reset this event's action (just to protect ourselves)
  25. ev.setAction(MotionEvent.ACTION_DOWN);
  26. // We know we want to dispatch the event down, find a child
  27. // who can handle it, start with the front-most child.
  28. final int scrolledXInt = (int) scrolledXFloat;
  29. final int scrolledYInt = (int) scrolledYFloat;
  30. final View[] children = mChildren;
  31. final int count = mChildrenCount;
  32. for (int i = count - 1; i >= 0; i--) {
  33. final View child = children[i];
  34. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
  35. || child.getAnimation() != null) {
  36. child.getHitRect(frame);
  37. if (frame.contains(scrolledXInt, scrolledYInt)) {
  38. // offset the event to the view's coordinate system
  39. final float xc = scrolledXFloat - child.mLeft;
  40. final float yc = scrolledYFloat - child.mTop;
  41. ev.setLocation(xc, yc);
  42. child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  43. if (child.dispatchTouchEvent(ev))  {
  44. // Event handled, we have a target now.
  45. mMotionTarget = child;
  46. return true;
  47. }
  48. // The event didn't get handled, try the next view.
  49. // Don't reset the event's location, it's not
  50. // necessary here.
  51. }
  52. }
  53. }
  54. }
  55. }
  56. boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
  57. (action == MotionEvent.ACTION_CANCEL);
  58. if (isUpOrCancel) {
  59. // Note, we've already copied the previous state to our local
  60. // variable, so this takes effect on the next event
  61. mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
  62. }
  63. // The event wasn't an ACTION_DOWN, dispatch it to our target if
  64. // we have one.
  65. final View target = mMotionTarget;
  66. if (target == null) {
  67. // We don't have a target, this means we're handling the
  68. // event as a regular view.
  69. ev.setLocation(xf, yf);
  70. if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
  71. ev.setAction(MotionEvent.ACTION_CANCEL);
  72. mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  73. }
  74. return super.dispatchTouchEvent(ev);
  75. }
  76. // if have a target, see if we're allowed to and want to intercept its
  77. // events
  78. if (!disallowIntercept && onInterceptTouchEvent(ev)) {
  79. final float xc = scrolledXFloat - (float) target.mLeft;
  80. final float yc = scrolledYFloat - (float) target.mTop;
  81. mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  82. ev.setAction(MotionEvent.ACTION_CANCEL);
  83. ev.setLocation(xc, yc);
  84. if (!target.dispatchTouchEvent(ev)) {
  85. // target didn't handle ACTION_CANCEL. not much we can do
  86. // but they should have.
  87. }
  88. // clear the target
  89. mMotionTarget = null;
  90. // Don't dispatch this event to our own view, because we already
  91. // saw it when intercepting; we just want to give the following
  92. // event to the normal onTouchEvent().
  93. return true;
  94. }
  95. if (isUpOrCancel) {
  96. mMotionTarget = null;
  97. }
  98. // finally offset the event to the target's coordinate system and
  99. // dispatch the event.
  100. final float xc = scrolledXFloat - (float) target.mLeft;
  101. final float yc = scrolledYFloat - (float) target.mTop;
  102. ev.setLocation(xc, yc);
  103. if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
  104. ev.setAction(MotionEvent.ACTION_CANCEL);
  105. target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  106. mMotionTarget = null;
  107. }
  108. return target.dispatchTouchEvent(ev);
  109. }

代码说明:

1、(代码2-4行)处理窗口处于模糊显示状态下的消息。所谓的模糊显示是指,应用程序可以设置当前窗口为模糊状态,此时窗口内部的所有视图将显示为模糊效果。这样的目的是为了隐藏窗口中的内容,对于其中的各个视图而言,可以设置改视图的FILTER_TOUCHERS_WHEN_OBSCURED标识,如存在该标识,则意味着用户希望不要处理该消息。
2、(代码6-11行)将ViewGroup的布局坐标转换成视图坐标。
3、(代码16-58行)处理ACTION_DOWN消息,其作用是判断该视图坐标落到了哪个子视图中。首先判断该ViewGroup本身是否被禁止获取TOUCH消息,如果没有禁止,并且回调函数onInterceptTouchEvent中没有消耗该消息,则开始寻找子视图。调用child.getHitRect(frame)方法获取该子视图在父视图中的布局坐标,即ViewGroup将child放在什么位置,这个位置相对于该child来讲是布局坐标,而对于该ViewGroup来讲却是视图坐标,参数frame是执行完毕后的位置输出矩形。得到位置后,就可以调用frame.contain()方法判断该消息位置是否被包含到了该child中,如果包含,并且该child也是一个ViewGroup,则准备递归调用该child的dispatchTouchEvent(),在调用之前,首先需要把坐标重新转换到child的坐标系中。接下来判断该child是否是ViewGroup类。如果是,则诋毁调用ViewGroup的dispatchTouchEvent(),重新从第一步开始执行;如果child不是ViewGroup,而是一个View,则意味着诋毁调用的结束。

4、(代码61-68行)如果是ACTION_UP或者是ACTION_CANCEL消息,则清除mGroupFlags中的FLAG_DISALLOW_INTERCEPT标识,即允许该ViewGroup截获消息。也就是说,常见的情况就是当用户释放手指,下一次按下时,该ViewGroup本身可以重新截获消息,而在按下还没有释放期间,ViewGroup本身是不允许截获消息的。

5、(代码70-82)判断target变量是否为空。空代表了所有子窗口都没有消耗该消息,所以该ViewGroup本事需要处理该消息。然后还原消息的原始位置,将视图坐标重新转换成布局坐标,接着调用super.dispatchTouchEvent(ev),即View类的中的dispatchTouchEvent()方法(该方法会判断是否进行调用touchEvent()方法来处理,到后面的View类的源码中再介绍)。

6、(代码84-121)处理target存在,并且变量disallowIntercept为false,即允许截获,在默认情况下ViewGroup都是允许截获消息的,只有当该ViewGroup的子视图调用父视图的requestDisallowdInterceptTouchEvent()方法时,方可禁止父视图再次截获消息,但每次ACTION_UP消息或者ACTION_CANCEL消息之后,该ViewGroup又会重新截获消息。ViewGroup本身不允许截获消息或者允许截获但是却没有消耗消息,于是调用target.dispatchTouchEvent(ev)方法把该消息继续交给目标视图处理,在调用该方法前需要检查target中是否申明过要取消随后的消息,即mPrivateFlags中包含 CANCEL_NEXT_UP_EVENT,如果是,则把消息action值修改为ACTION_CANCEL,将mMotionTarget变为空,因为target不想处理接下来的消息了,那么就可以认为没有target了。

四、View中处理消息的详细过程

dispatchTouchEvent()方法的源码

[java] view plaincopy
  1. /**
  2. * Pass the touch screen motion event down to the target view, or this
  3. * view if it is the target.
  4. *
  5. * @param event The motion event to be dispatched.
  6. * @return True if the event was handled by the view, false otherwise.
  7. */
  8. public boolean dispatchTouchEvent(MotionEvent event) {
  9. if (!onFilterTouchEventForSecurity(event)) {
  10. return false;
  11. }
  12. if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
  13. mOnTouchListener.onTouch(this, event)) {
  14. return true;
  15. }
  16. return onTouchEvent(event);
  17. }

处理窗口处于模糊显示状态下的消息。然后回调视图监听着的onTouch()方法,如果监听者消耗了该消息,则直接返回。如果没有则调用View的onTouchEvent()方法。

onTouchEvent()方法源码

[java] view plaincopy
  1. /**
  2. * Implement this method to handle touch screen motion events.
  3. *
  4. * @param event The motion event.
  5. * @return True if the event was handled, false otherwise.
  6. */
  7. public boolean onTouchEvent(MotionEvent event) {
  8. final int viewFlags = mViewFlags;
  9. if ((viewFlags & ENABLED_MASK) == DISABLED) {
  10. // A disabled view that is clickable still consumes the touch
  11. // events, it just doesn't respond to them.
  12. return (((viewFlags & CLICKABLE) == CLICKABLE ||
  13. (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
  14. }
  15. if (mTouchDelegate != null) {
  16. if (mTouchDelegate.onTouchEvent(event)) {
  17. return true;
  18. }
  19. }
  20. if (((viewFlags & CLICKABLE) == CLICKABLE ||
  21. (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
  22. switch (event.getAction()) {
  23. case MotionEvent.ACTION_UP:
  24. boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
  25. if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
  26. // take focus if we don't have it already and we should in
  27. // touch mode.
  28. boolean focusTaken = false;
  29. if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
  30. focusTaken = requestFocus();
  31. }
  32. if (!mHasPerformedLongPress) {
  33. // This is a tap, so remove the longpress check
  34. removeLongPressCallback();
  35. // Only perform take click actions if we were in the pressed state
  36. if (!focusTaken) {
  37. // Use a Runnable and post this rather than calling
  38. // performClick directly. This lets other visual state
  39. // of the view update before click actions start.
  40. if (mPerformClick == null) {
  41. mPerformClick = new PerformClick();
  42. }
  43. if (!post(mPerformClick)) {
  44. performClick();
  45. }
  46. }
  47. }
  48. if (mUnsetPressedState == null) {
  49. mUnsetPressedState = new UnsetPressedState();
  50. }
  51. if (prepressed) {
  52. mPrivateFlags |= PRESSED;
  53. refreshDrawableState();
  54. postDelayed(mUnsetPressedState,
  55. ViewConfiguration.getPressedStateDuration());
  56. } else if (!post(mUnsetPressedState)) {
  57. // If the post failed, unpress right now
  58. mUnsetPressedState.run();
  59. }
  60. removeTapCallback();
  61. }
  62. break;
  63. case MotionEvent.ACTION_DOWN:
  64. if (mPendingCheckForTap == null) {
  65. mPendingCheckForTap = new CheckForTap();
  66. }
  67. mPrivateFlags |= PREPRESSED;
  68. mHasPerformedLongPress = false;
  69. postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
  70. break;
  71. case MotionEvent.ACTION_CANCEL:
  72. mPrivateFlags &= ~PRESSED;
  73. refreshDrawableState();
  74. removeTapCallback();
  75. break;
  76. case MotionEvent.ACTION_MOVE:
  77. final int x = (int) event.getX();
  78. final int y = (int) event.getY();
  79. // Be lenient about moving outside of buttons
  80. int slop = mTouchSlop;
  81. if ((x < 0 - slop) || (x >= getWidth() + slop) ||
  82. (y < 0 - slop) || (y >= getHeight() + slop)) {
  83. // Outside button
  84. removeTapCallback();
  85. if ((mPrivateFlags & PRESSED) != 0) {
  86. // Remove any future long press/tap checks
  87. removeLongPressCallback();
  88. // Need to switch from pressed to not pressed
  89. mPrivateFlags &= ~PRESSED;
  90. refreshDrawableState();
  91. }
  92. }
  93. break;
  94. }
  95. return true;
  96. }
  97. return false;
  98. }
  99. <p>   </p>

1、代码10-15)处理该视图是否为disable状态,如果是,什么都不处理,返回true,即消耗该消息。

2、(代码17-21)消息代理处理消息。所谓的消息代理是指,可以给某个View指定一个消息处理代理,当View收到消息时,首先将该消息派发给其代理进行处理。如果代理内部消耗了该消息,则View不需要在进行任何处理;如果代理没有处理,则view继续处理。在这里不用多管,不会影响事件处理的逻辑和结果。

3、(代码22-102)判断该视图是否可以点击,如果不可以点击,则直接返回false,即不处理该消息。否则,真正开始执行触摸消息的默认处理逻辑,该逻辑分别处理了ACTION_DOWN、ACTION_MOVE、ACTION_UP消息.

(26-70)处理ACTION_UP消息,判断该UP消息是否发生在前面所讲的哪一个监测时间段中,并据此进行不同的处理。

(71-78)在ACTION_DOWN消息中,给mPrivateFlags变量添加PRESSED标识,并将变量mHasPerformLongPress设置为false,然后启动tap监测,即发送一个异步延迟消息。

对于触摸消息而言,消息本身没有repeat的属性,一次触摸只有一个ACTION_DOWN消息,按下来就是连续的ACTION_MOVE消息,并最终以处理ACTION_CANCEL消息.

(80-85)ACTION_CANCLE消息处理,这里只需清除PRESSED标识,刷新视图状态,然后再关闭tap监测即可。

(86-105)对ACTION_MOVE消息进行处理。具体逻辑包括,判断是否移动到了视图区域以外,如果是,则删除tap或者longPress的监测,并清除mPrivateFlags中的PRESSED标识,然后调用refreshDrawableState()刷新视图状态,这将导致对背景状态进行重绘。

五、例子程序

通过代码可能完全理解事件的分发过程有点难,下面通过一个例子程序来巩固下事件分发的过程。

例子程序的UI如下:

布局如下:

[java] view plaincopy
  1. <org.sunday.main.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical" >
  6. <org.sunday.main.MyLinearLayout
  7. android:id="@+id/ll2"
  8. android:layout_width="match_parent"
  9. android:layout_height="100dp"
  10. android:background="@drawable/ll_selector"
  11. android:orientation="vertical" >
  12. </org.sunday.main.MyLinearLayout>
  13. <org.sunday.main.MyLinearLayout
  14. android:id="@+id/ll1"
  15. android:layout_width="match_parent"
  16. android:layout_marginTop="20dp"
  17. android:layout_height="100dp"
  18. android:background="@drawable/ll_selector"
  19. android:orientation="vertical" >
  20. <org.sunday.main.MyLinearLayout
  21. android:layout_width="80dp"
  22. android:layout_height="50dp"
  23. android:layout_marginTop="20dp"
  24. android:clickable="true"
  25. android:background="@drawable/ll_selector2"
  26. android:orientation="vertical" >
  27. </org.sunday.main.MyLinearLayout>
  28. </org.sunday.main.MyLinearLayout>
  29. <org.sunday.main.MyLinearLayout
  30. android:id="@+id/ll3"
  31. android:layout_width="match_parent"
  32. android:layout_height="100dp"
  33. android:layout_marginTop="20dp"
  34. android:background="@drawable/ll_selector"
  35. android:orientation="vertical" >
  36. <org.sunday.main.MyButton
  37. android:layout_width="wrap_content"
  38. android:layout_height="60dp"
  39. android:layout_margin="20dp"
  40. android:background="@drawable/btn_selector"
  41. android:text="button1" />
  42. </org.sunday.main.MyLinearLayout>
  43. <org.sunday.main.MyLinearLayout
  44. android:id="@+id/ll4"
  45. android:layout_width="match_parent"
  46. android:layout_height="100dp"
  47. android:layout_marginTop="20dp"
  48. android:background="@drawable/ll_selector"
  49. android:orientation="vertical" >
  50. <org.sunday.main.MyButtonWithOutEvent
  51. android:layout_width="wrap_content"
  52. android:layout_height="60dp"
  53. android:layout_margin="20dp"
  54. android:background="@drawable/btn_selector"
  55. android:text="button2" />
  56. </org.sunday.main.MyLinearLayout>
  57. </org.sunday.main.MyLinearLayout>

MyLinearLayout.java

[java] view plaincopy
  1. package org.sunday.main;
  2. import android.content.Context;
  3. import android.util.AttributeSet;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.widget.LinearLayout;
  7. public class MyLinearLayout extends LinearLayout {
  8. private final static String TAG = "MyLinearLayout";
  9. public MyLinearLayout(Context context) {
  10. super(context);
  11. }
  12. public MyLinearLayout(Context context, AttributeSet attrs) {
  13. super(context, attrs);
  14. }
  15. @Override
  16. public boolean dispatchTouchEvent(MotionEvent ev) {
  17. Log.e(TAG, "dispatchTouchEvent()----------------" + this.toString());
  18. boolean isTrue = super.dispatchTouchEvent(ev);
  19. Log.e(TAG, "dispatchTouchEvent()  isTrue = " + isTrue + " --------------- ;"+ this.toString());
  20. return isTrue;
  21. }
  22. @Override
  23. public boolean onTouchEvent(MotionEvent event) {
  24. Log.e(TAG, "onTouchEvent()----------------" + this.toString());
  25. boolean isTrue = super.onTouchEvent(event);
  26. Log.e(TAG, "onTouchEvent()  isTrue = " + isTrue+ "-----------------  ;"+ this.toString());
  27. return isTrue;
  28. }
  29. }

MyButton.java

[java] view plaincopy
  1. package org.sunday.main;
  2. import android.content.Context;
  3. import android.util.AttributeSet;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.widget.Button;
  7. public class MyButton extends Button {
  8. private final static String TAG = "MyButton";
  9. public MyButton(Context context) {
  10. super(context);
  11. }
  12. public MyButton(Context context, AttributeSet attrs) {
  13. super(context, attrs);
  14. }
  15. @Override
  16. public boolean dispatchTouchEvent(MotionEvent event) {
  17. Log.e(TAG, "dispatchTouchEvent()----------------");
  18. boolean isTrue = super.dispatchTouchEvent(event);
  19. Log.e(TAG, "dispatchTouchEvent  isTrue = " + isTrue);
  20. return isTrue;
  21. }
  22. @Override
  23. public boolean onTouchEvent(MotionEvent event) {
  24. Log.e(TAG, "onTouchEvent()----------------");
  25. boolean isTrue = super.onTouchEvent(event);
  26. Log.e(TAG, "onTouchEvent  isTrue = " + isTrue);
  27. return isTrue;
  28. }
  29. }

MyButtonWithOutEvent.java

[java] view plaincopy
  1. package org.sunday.main;
  2. import android.content.Context;
  3. import android.util.AttributeSet;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.widget.Button;
  7. public class MyButtonWithOutEvent extends Button {
  8. private final static String TAG = "MyButtonWithOutEvent";
  9. public MyButtonWithOutEvent(Context context) {
  10. super(context);
  11. }
  12. public MyButtonWithOutEvent(Context context, AttributeSet attrs) {
  13. super(context, attrs);
  14. }
  15. @Override
  16. public boolean dispatchTouchEvent(MotionEvent event) {
  17. Log.e(TAG, "dispatchTouchEvent()----------------"+ this.toString());
  18. //      boolean isTrue = super.dispatchTouchEvent(event);
  19. //      Log.e(TAG, "isTrue = " + isTrue);
  20. return false;
  21. }
  22. @Override
  23. public boolean onTouchEvent(MotionEvent event) {
  24. Log.e(TAG, "onTouchEvent()----------------");
  25. boolean isTrue = super.onTouchEvent(event);
  26. Log.e(TAG, "onTouchEvent  isTrue = " + isTrue);
  27. return isTrue;
  28. }
  29. }

点击例子一,后台打印的日志如下:

解释:例子一的布局中只有两个ViewGroup子类对象,当点击例子一区域,根ViewGroup首先分发touch事件,因为它有child,所以接着会将事件传递到它的孩子ViewGroup。孩子ViewGroup发现自己没有child了,所以它就得自己处理这个touch事件。所以会调用OnTouchEvent()方法来处理这个事件。处理完之后会返回一个true,一直返回给根ViewGroup。

点击例子二的绿色区域,后台打印日志如下:

解释:例子二和例子一差不多,只不过多了一个ViewGroup而已。

点击例子三得黄色区域,后台打印日志如下:

解释:例子三相比于例子二只是将最上层的ViewGroup换成了View,原理一样。

点击例子四的黄色区域,打印日志如下:

解释:在这个程序中,对于MyLinearLayout和MyButton的dispachTouchEvent()和onTouchEvent()发放的逻辑并没有进行任何的修改。而对于MyButtonWithOutEvent,在dispatchTouchEvent()方法中,直接返回的false(即不进行事件的任何处理),那么事件传递到这里时,它将会把事件返回给调用它的父ViewGroup进行处理,所以这里我们看到的是MyLinearLayout调用了onTouchEvent()。

点击例子三得button和点击例子四的button效果图如下:

例子三中是button响应了这个touch事件,而在例子四中则是LinearLayout响应了这个touch事件。

demo:点击打开链接

View工作原理(一)事件传递原理详解相关推荐

  1. Android事件传递机制详解

    总结 dispatchTouchEvent方法:分发点击事件 onInterceptTouchEvent方法:拦截事件(只存在于ViewGroup,View没有此方法), 在dispatchTouch ...

  2. InheritableThreadLocal类原理简介使用 父子线程传递数据详解 多线程中篇(十八)...

    上一篇文章中对ThreadLocal进行了详尽的介绍,另外还有一个类: InheritableThreadLocal 他是ThreadLocal的子类,那么这个类又有什么作用呢? 测试代码 publi ...

  3. View的事件体系之三 android事件分发机制详解(下)

    接着上一篇来分析事件分发机制,在看了各位大牛的关于事件分发机制的分析后茅塞顿开,之前看过好几遍郭霖,弘扬以及玉刚大神关于事件体系的讲解,一直看不懂,比较模糊,最近复习时,看到一篇博文,写的相当精彩,看 ...

  4. 安卓自定义View进阶-事件分发机制详解

    原文地址:http://www.gcssloop.com/customview/dispatch-touchevent-source Android 事件分发机制详解,在上一篇文章 事件分发机制原理  ...

  5. Sniff网络基础原理和软件实现技巧详解

    Sniff网络基础原理和软件实现技巧详解 前言 SNIFF真是一个古老的话题,关于在网络上采用SNIFF来获取敏感信息已经不是什么新鲜事,也不乏很多成功的案例,那么,SNIFF究竟是什么呢? SNIF ...

  6. 2. IMU原理及姿态融合算法详解

    文章目录 2. IMU原理及姿态融合算法详解 一.组合 二. 原理 a) 陀螺仪 b) 加速度计 c) 磁力计 三. 旋转的表达 a) 欧拉角 b) 旋转矩阵 c) 四元数 d) 李群 SO(3)\t ...

  7. 【 卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10)】

    卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10) 本章主要进行卷积神经网络的相关数学原理和pytorch的对应模块进行推导分析 代码也是通过demo实 ...

  8. 强化学习教程(四):从PDG到DDPG的原理及tf代码实现详解

    强化学习教程(四):从PDG到DDPG的原理及tf代码实现详解 原创 lrhao 公众号:ChallengeHub 收录于话题 #强化学习教程 前言 在前面强化学习教程(三)中介绍了基于策略「PG」算 ...

  9. 第41课:Checkpoint彻底解密:Checkpoint的运行原理和源码实现彻底详解

    第41课:Checkpoint彻底解密:Checkpoint的运行原理和源码实现彻底详解 一:Checkpoint到底是什么? 1,  Spark在生产环境下经常会面临Tranformations的R ...

  10. 【转】什么是场效应管(FET)-场效应管(FET)分类、原理、用途等知识详解

    什么是场效应管(FET)-场效应管(FET)分类.原理.用途等知识详解 场效应管和双极晶体管不同,它属于仅以电子或空穴中的一种载子动作的晶体管.按照结构.原理可以分为:1.接合型场效应管 2.MOS型 ...

最新文章

  1. Java 线程池详解学习:FixedThreadPool,CachedThreadPool,ScheduledThreadPool...
  2. 大数据互联网架构阶段 Redis(二)
  3. 光耦的CTR(Current Transfer Ratio)值概念及计算方法
  4. HDU - 5381 The sum of gcd(莫队/线段树区间合并)
  5. Django完整配置settings.py
  6. 匈牙利命名法、骆驼命名法、帕斯卡(pascal)命名法 C#命名规范
  7. cl编译C文件的环境变量修改
  8. 前端js代码多文件混淆
  9. JAVA常见异常解析
  10. Python解答蓝桥杯省赛真题之从入门到真题
  11. shell清空数据库表
  12. Python笔记:第三方IP代理服务与爬虫IP代理
  13. SpringCloud之熔断器Hystrix(二)
  14. 10分钟教你用python做个打飞机(超详细超入门教程)附源代码下载
  15. 其他总结(三)--window关闭或打开资源管理器
  16. 使用Nhibernate.Tool.Hbm2Net来根据HBML文件生成POJO类的代码
  17. 背景动态星空H5特效源代码
  18. 前女友让我撸个植物大战僵尸,我一怒之下把代码开源了...
  19. python怎么求商_如何用python求差商?
  20. Xcode 下载加速及安装指南

热门文章

  1. JavaScript中this的五种绑定方式详解
  2. 【数字信号处理】序列傅里叶变换 ( 基本序列的傅里叶变换 | 求 cosωn 的傅里叶变换 | 复变函数欧拉公式 )
  3. 【数字信号处理】线性常系数差分方程 ( 根据 “ 线性常系数差分方程 “ 与 “ 边界条件 “ 确定系统是否是 “ 线性时不变系统 “ 案例 | 根据 “ 线性时不变系统 “ 定义证明 )
  4. 【Binder 机制】AIDL 分析 ( 创建 AIDL 文件 | 创建 Parcelable 类 | AIDL 中使用 Parcelable 类 | 编译工程生成 AIDL 对应的Java源文件 )
  5. 使用Mybatis Generator自动生成代码
  6. js获取浏览器宽度和高度值
  7. Unity C# Sting.Format的学习
  8. JVM crash at ForUtil.readBlock
  9. 病毒软件,导致DNS失效的解决办法
  10. [LeetCode]Add Binary