首先我们先写个简单的例子来测试View的事件转发的流程~

1、案例

为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志~

MyButton

  1. package com.example.zhy_event03;
  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. {
  9. private static final String TAG = MyButton.class.getSimpleName();
  10. public MyButton(Context context, AttributeSet attrs)
  11. {
  12. super(context, attrs);
  13. }
  14. @Override
  15. public boolean onTouchEvent(MotionEvent event)
  16. {
  17. int action = event.getAction();
  18. switch (action)
  19. {
  20. case MotionEvent.ACTION_DOWN:
  21. Log.e(TAG, "onTouchEvent ACTION_DOWN");
  22. break;
  23. case MotionEvent.ACTION_MOVE:
  24. Log.e(TAG, "onTouchEvent ACTION_MOVE");
  25. break;
  26. case MotionEvent.ACTION_UP:
  27. Log.e(TAG, "onTouchEvent ACTION_UP");
  28. break;
  29. default:
  30. break;
  31. }
  32. return super.onTouchEvent(event);
  33. }
  34. @Override
  35. public boolean dispatchTouchEvent(MotionEvent event)
  36. {
  37. int action = event.getAction();
  38. switch (action)
  39. {
  40. case MotionEvent.ACTION_DOWN:
  41. Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
  42. break;
  43. case MotionEvent.ACTION_MOVE:
  44. Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
  45. break;
  46. case MotionEvent.ACTION_UP:
  47. Log.e(TAG, "dispatchTouchEvent ACTION_UP");
  48. break;
  49. default:
  50. break;
  51. }
  52. return super.dispatchTouchEvent(event);
  53. }
  54. }

在onTouchEvent和dispatchTouchEvent中打印了日志~

然后把我们自定义的按钮加到主布局文件中;

布局文件:

  1. <LinearLayout 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. tools:context=".MainActivity" >
  6. <com.example.zhy_event03.MyButton
  7. android:id="@+id/id_btn"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:text="click me" />
  11. </LinearLayout>

最后看一眼MainActivity的代码

  1. package com.example.zhy_event03;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.view.View;
  7. import android.view.View.OnTouchListener;
  8. import android.widget.Button;
  9. public class MainActivity extends Activity
  10. {
  11. protected static final String TAG = "MyButton";
  12. private Button mButton ;
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState)
  15. {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.activity_main);
  18. mButton = (Button) findViewById(R.id.id_btn);
  19. mButton.setOnTouchListener(new OnTouchListener()
  20. {
  21. @Override
  22. public boolean onTouch(View v, MotionEvent event)
  23. {
  24. int action = event.getAction();
  25. switch (action)
  26. {
  27. case MotionEvent.ACTION_DOWN:
  28. Log.e(TAG, "onTouch ACTION_DOWN");
  29. break;
  30. case MotionEvent.ACTION_MOVE:
  31. Log.e(TAG, "onTouch ACTION_MOVE");
  32. break;
  33. case MotionEvent.ACTION_UP:
  34. Log.e(TAG, "onTouch ACTION_UP");
  35. break;
  36. default:
  37. break;
  38. }
  39. return false;
  40. }
  41. });
  42. }
  43. }

在MainActivity中,我们还给MyButton设置了OnTouchListener这个监听~

好了,跟View事件相关一般就这三个地方了,一个onTouchEvent,一个dispatchTouchEvent,一个setOnTouchListener;

下面我们运行,然后点击按钮,查看日志输出:

  1. 08-31 06:09:39.030: E/MyButton(879): dispatchTouchEvent ACTION_DOWN
  2. 08-31 06:09:39.030: E/MyButton(879): onTouch ACTION_DOWN
  3. 08-31 06:09:39.049: E/MyButton(879): onTouchEvent ACTION_DOWN
  4. 08-31 06:09:39.138: E/MyButton(879): dispatchTouchEvent ACTION_MOVE
  5. 08-31 06:09:39.138: E/MyButton(879): onTouch ACTION_MOVE
  6. 08-31 06:09:39.147: E/MyButton(879): onTouchEvent ACTION_MOVE
  7. 08-31 06:09:39.232: E/MyButton(879): dispatchTouchEvent ACTION_UP
  8. 08-31 06:09:39.248: E/MyButton(879): onTouch ACTION_UP
  9. 08-31 06:09:39.248: E/MyButton(879): onTouchEvent ACTION_UP

我有意点击的时候蹭了一下,不然不会触发MOVE,手抖可能会打印一堆MOVE的日志~~~

好了,可以看到,不管是DOWN,MOVE,UP都会按照下面的顺序执行:

1、dispatchTouchEvent

2、 setOnTouchListener的onTouch

3、onTouchEvent

下面就跟随日志的脚步开始源码的探索~

2、dispatchTouchEvent

首先进入View的dispatchTouchEvent

  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. }

直接看13行:首先判断mOnTouchListener不为null,并且view是enable的状态,然后 mOnTouchListener.onTouch(this, event)返回true,这三个条件如果都满足,直接return true ; 也就是下面的onTouchEvent(event)不会被执行了;

那么mOnTouchListener是和方神圣,我们来看看:

  1. /**
  2. * Register a callback to be invoked when a touch event is sent to this view.
  3. * @param l the touch listener to attach to this view
  4. */
  5. public void setOnTouchListener(OnTouchListener l) {
  6. mOnTouchListener = l;
  7. }

其实就是我们在Activity中设置的setOnTouchListener。

也就是说:如果我们设置了setOnTouchListener,并且return true,那么View自己的onTouchEvent就不会被执行了,当然了,本例我们return false,我们还得往下探索 ;

已经解决一个常见的问题:View的onTouchListener和onTouchEvent的调用关系,相信大家应该已经明白了~let's go;继续往下。

3、View的onTouchEvent:

接下来是View的onTouchEvent:

  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. }

代码还是比较长的,

10-15行,如果当前View是Disabled状态且是可点击则会消费掉事件(return true);可以忽略,不是我们的重点;

17-21行,如果设置了mTouchDelegate,则会将事件交给代理者处理,直接return true,如果大家希望自己的View增加它的touch范围,可以尝试使用TouchDelegate,这里也不是重点,可以忽略;

接下来到我们的重点了:

23行的判断:如果我们的View可以点击或者可以长按,则,注意IF的范围,最终一定return true ;

if (((viewFlags & CLICKABLE) == CLICKABLE ||

                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {

           //...

            return true;

        }

接下来就是   switch (event.getAction())了,判断事件类型,DOWN,MOVE,UP等;

我们按照例子执行的顺序,先看  case MotionEvent.ACTION_DOWN (71-78行):

1、MotionEvent.ACTION_DOWN

75行:给mPrivateFlags设置一个PREPRESSED的标识

76行:设置mHasPerformedLongPress=false;表示长按事件还未触发;

77行:发送一个延迟为ViewConfiguration.getTapTimeout()的延迟消息,到达延时时间后会执行CheckForTap()里面的run方法:

1、ViewConfiguration.getTapTimeout()为115毫秒;

2、CheckForTap

  1. private final class CheckForTap implements Runnable {
  2. public void run() {
  3. mPrivateFlags &= ~PREPRESSED;
  4. mPrivateFlags |= PRESSED;
  5. refreshDrawableState();
  6. if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
  7. postCheckForLongClick(ViewConfiguration.getTapTimeout());
  8. }
  9. }
  10. }

在run方法里面取消mPrivateFlags的PREPRESSED,然后设置PRESSED标识,刷新背景,如果View支持长按事件,则再发一个延时消息,检测长按;

  1. private void postCheckForLongClick(int delayOffset) {
  2. mHasPerformedLongPress = false;
  3. if (mPendingCheckForLongPress == null) {
  4. mPendingCheckForLongPress = new CheckForLongPress();
  5. }
  6. mPendingCheckForLongPress.rememberWindowAttachCount();
  7. postDelayed(mPendingCheckForLongPress,
  8. ViewConfiguration.getLongPressTimeout() - delayOffset);
  9. }
  1. class CheckForLongPress implements Runnable {
  2. private int mOriginalWindowAttachCount;
  3. public void run() {
  4. if (isPressed() && (mParent != null)
  5. && mOriginalWindowAttachCount == mWindowAttachCount) {
  6. if (performLongClick()) {
  7. mHasPerformedLongPress = true;
  8. }
  9. }
  10. }

可以看到,当用户按下,首先会设置标识为PREPRESSED,如果在115毫秒内抬起了,UP时会移除CheckForTap这个回调(UP时会分析);

如果115后,没有抬起,会将View的标识设置为PRESSED且去掉PREPRESSED标识,然后发出一个检测长按的延迟任务,延时为:ViewConfiguration.getLongPressTimeout() - delayOffset(500ms -115ms),这个115ms刚好时检测额PREPRESSED时间;也就是用户从DOWN触发开始算起,如果500ms内没有抬起则认为触发了长按事件:

1、如果此时设置了长按的回调,则执行长按时的回调,且如果长按的回调返回true;才把mHasPerformedLongPress置为ture;

2、否则,如果没有设置长按回调或者长按回调返回的是false;则mHasPerformedLongPress依然是false;

好了DOWN就分析完成了;大家回个神,下面回到VIEW的onTouchEvent中的ACTION_MOVE:

2、MotionEvent.ACTION_MOVE

86到105行:

87-88行:拿到当前触摸的x,y坐标;

91行判断当然触摸点有没有移出我们的View,如果移出了:

1、执行removeTapCallback(); 

2、然后判断是否包含PRESSED标识,如果包含,移除长按的检查:removeLongPressCallback();

3、最后把mPrivateFlags中PRESSED标识去除,刷新背景;

  1. private void removeTapCallback() {
  2. if (mPendingCheckForTap != null) {
  3. mPrivateFlags &= ~PREPRESSED;
  4. removeCallbacks(mPendingCheckForTap);
  5. }
  6. }

这个是移除,DOWN触发时设置的PREPRESSED的检测;即当前触发时机在DOWN触发不到115ms时,你就已经移出控件外了;

如果115ms后,你才移出控件外,则你的当前mPrivateFlags一定为PRESSED且发送了长按的检测;

就会走上面的2和3;首先移除removeLongPressCallback()

 private void removeLongPressCallback() {

        if (mPendingCheckForLongPress != null) {

          removeCallbacks(mPendingCheckForLongPress);

        }

    }

然后把mPrivateFlags中PRESSED标识去除,刷新背景;

好了,MOVE我们也分析完成了,总结一下:只要用户移出了我们的控件:则将mPrivateFlags取出PRESSED标识,且移除所有在DOWN中设置的检测,长按等;

下面再回个神,回到View的onTouchEvent的ACTION_UP:

3、MotionEvent.ACTION_UP

26到69行:

27行:判断mPrivateFlags是否包含PREPRESSED

28行:如果包含PRESSED或者PREPRESSED则进入执行体,也就是无论是115ms内或者之后抬起都会进入执行体。

36行:如果mHasPerformedLongPress没有被执行,进入IF

38行:removeLongPressCallback();移除长按的检测

45-50行:如果mPerformClick如果mPerformClick为null,初始化一个实例,然后立即通过handler添加到消息队列尾部,如果添加失败则直接执行 performClick();添加成功,在mPerformClick的run方法中就是执行performClick();

终于执行了我们的click事件了,下面看一下performClick()方法:

  1. public boolean performClick() {
  2. sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
  3. if (mOnClickListener != null) {
  4. playSoundEffect(SoundEffectConstants.CLICK);
  5. mOnClickListener.onClick(this);
  6. return true;
  7. }
  8. return false;
  9. }

if (mOnClickListener != null) {    

            mOnClickListener.onClick(this);

            return true;

        }

久违了~我们的mOnClickListener ;

别激动,还没结束,回到ACTION_UP,

58行:如果prepressed为true,进入IF体:

为mPrivateFlags设置表示为PRESSED,刷新背景,125毫秒后执行mUnsetPressedState

否则:mUnsetPressedState.run();立即执行;也就是不管咋样,最后mUnsetPressedState.run()都会执行;

看看这个UnsetPressedState主要干什么:

private final class UnsetPressedState implements Runnable {

        public void run() {

            setPressed(false);

        }

    }

public void setPressed(boolean pressed) {

        if (pressed) {

            mPrivateFlags |= PRESSED;

        } else {

            mPrivateFlags &= ~PRESSED;

        }

        refreshDrawableState();

        dispatchSetPressed(pressed);

    }

把我们的mPrivateFlags中的PRESSED取消,然后刷新背景,把setPress转发下去。

ACTION_UP的最后一行:removeTapCallback(),如果mPendingCheckForTap不为null,移除;

4、总结

好了,代码跨度还是相当大的,下面需要总结下:

1、整个View的事件转发流程是:

View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent

在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。

2、onTouchEvent中的DOWN,MOVE,UP

DOWN时:

a、首先设置标志为PREPRESSED,设置mHasPerformedLongPress=false ;然后发出一个115ms后的mPendingCheckForTap,如果115ms内抬起手指,触发了UP,则不会触发click事件,并且最终执行的是UnsetPressedState对象,setPressed(false)将setPress的传递下去;这种情况很少发生,可能只会在压力测试的时候会发现无法触发click事件;

b、如果115ms内没有触发UP,则将标志置为PRESSED,清除PREPRESSED标志,同时发出一个延时为500-115ms的,检测长按任务消息;

c、如果500ms内(从DOWN触发开始算),则会触发LongClickListener:

此时如果LongClickListener不为null,则会执行回调,同时如果LongClickListener.onClick返回true,才把mHasPerformedLongPress设置为true;否则mHasPerformedLongPress依然为false;

MOVE时:

主要就是检测用户是否划出控件,如果划出了:

115ms内,直接移除mPendingCheckForTap;

115ms后,则将标志中的PRESSED去除,同时移除长按的检查:removeLongPressCallback();

UP时:

a、如果115ms内,触发UP,此时标志为PREPRESSED,则执行UnsetPressedState,setPressed(false);会把setPress转发下去,可以在View中复写dispatchSetPressed方法接收;

b、如果是115ms-500ms间,即长按还未发生,则首先移除长按检测,执行onClick回调;

c、如果是500ms以后,那么有两种情况:

i.设置了onLongClickListener,且onLongClickListener.onClick返回true,则点击事件OnClick事件无法触发;

ii.没有设置onLongClickListener或者onLongClickListener.onClick返回false,则点击事件OnClick事件依然可以触发;

d、最后执行mUnsetPressedState.run(),将setPressed传递下去,然后将PRESSED标识去除;

最后问个问题,然后再运行个例子结束:

1、setOnLongClickListener和setOnClickListener是否只能执行一个

不是的,只要setOnLongClickListener中的onClick返回false,则两个都会执行;返回true则会屏幕setOnClickListener

最后我们给MyButton同时设置setOnClickListener和setOnLongClickListener,运行看看:

  1. package com.example.zhy_event03;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.MotionEvent;
  6. import android.view.View;
  7. import android.view.View.OnClickListener;
  8. import android.view.View.OnLongClickListener;
  9. import android.view.View.OnTouchListener;
  10. import android.widget.Button;
  11. import android.widget.Toast;
  12. public class MainActivity extends Activity
  13. {
  14. protected static final String TAG = "MyButton";
  15. private Button mButton ;
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState)
  18. {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.activity_main);
  21. mButton = (Button) findViewById(R.id.id_btn);
  22. mButton.setOnTouchListener(new OnTouchListener()
  23. {
  24. @Override
  25. public boolean onTouch(View v, MotionEvent event)
  26. {
  27. int action = event.getAction();
  28. switch (action)
  29. {
  30. case MotionEvent.ACTION_DOWN:
  31. Log.e(TAG, "onTouch ACTION_DOWN");
  32. break;
  33. case MotionEvent.ACTION_MOVE:
  34. Log.e(TAG, "onTouch ACTION_MOVE");
  35. break;
  36. case MotionEvent.ACTION_UP:
  37. Log.e(TAG, "onTouch ACTION_UP");
  38. break;
  39. default:
  40. break;
  41. }
  42. return false;
  43. }
  44. });
  45. mButton.setOnClickListener(new OnClickListener()
  46. {
  47. @Override
  48. public void onClick(View v)
  49. {
  50. Toast.makeText(getApplicationContext(), "onclick",Toast.LENGTH_SHORT).show();
  51. }
  52. });
  53. mButton.setOnLongClickListener(new OnLongClickListener()
  54. {
  55. @Override
  56. public boolean onLongClick(View v)
  57. {
  58. Toast.makeText(getApplicationContext(), "setOnLongClickListener",Toast.LENGTH_SHORT).show();
  59. return false;
  60. }
  61. });
  62. }
  63. }

效果图:

可以看到LongClickListener已经ClickListener都触发了~

最后,本篇博文完成了对View的事件分发机制的整个流程的说明,并且对源码进行了分析;

当然了,View结束,肯定到我们的ViewGroup了,请点击:Android ViewGroup事件分发机制

android view事件分发机制的更多相关文章

  1. Android View 事件分发机制 源码解析 (上)

    一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...

  2. Android View 事件分发机制 源代码解析 (上)

    一直想写事件分发机制的文章,无论咋样,也得自己研究下事件分发的源代码.写出心得~ 首先我们先写个简单的样例来測试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个M ...

  3. Android View 事件分发机制详解

    想必很多android开发者都遇到过手势冲突的情况,我们一般都是通过内部拦截和外部拦截法解决此类问题.要想搞明白原理就必须了解View的分发机制.在此之前我们先来了解一下以下三个非常重要的方法: di ...

  4. 浅谈Android View事件分发机制

    引言 前面的文章介绍了View的基础知识和View的滑动,今天我们来介绍View的另一个核心知识,View的事件分发机制. 点击事件的传递规则 所谓的点击事件的分发机制,其实就是对MotionEven ...

  5. android View事件分发机制结论

    原始博客有对源码的分析:http://blog.csdn.net/lmj623565791/article/details/39102591 结论:1.view事件的分发流程: dispatchTou ...

  6. Android之事件分发机制

    本文主要包括以下内容 view的事件分发 viewGroup的事件分发 首先来看两张图 在执行touch事件时 首先执行dispatchTouchEvent方法,执行事件分发. 再执行onInterc ...

  7. Android面试必问!View 事件分发机制,看这一篇就够了!

    在 Android 开发当中,View 的事件分发机制是一块很重要的知识.不仅在开发当中经常需要用到,面试的时候也经常被问到. 如果你在面试的时候,能把这块讲清楚,对于校招生或者实习生来说,算是一块不 ...

  8. Atitit View事件分发机制

    1. Atitit View事件分发机制 1. Atitit View事件分发机制1 1.1. 三个关键方法 dispatchTouchEvent onInterceptTouchEvent onTo ...

  9. android的事件分发机制理解

    android的事件分发机制理解 1.事件触发主要涉及到哪些层面的哪些函数(个人理解的顺序,可能在某一层会一次回调其它函数) activity中的dispatchTouchEvent .layout中 ...

随机推荐

  1. 扩展Lucas定理

    (1)Lucas定理:p为素数,则有: (2)证明: n=(ak...a2,a1,a0)p = (ak...a2,a1)p*p + a0 =  [n/p]*p+a0,m=[m/p]*p+b0其次,我们 ...

  2. 【NOIP2016TG】solution

    传送门:https://www.luogu.org/problem/lists?name=&orderitem=pid&tag=83%7C33 D1T1(toys) 题意:有n个小人, ...

  3. 【LSGDOJ 1408】邮局

    题目描述 一些村庄被建在一条笔直的高边公路边上.我们用一条坐标轴来描述这条高边公路,每一个村庄的坐标都是整数.没有两个村庄坐标相同.两个村庄问的距离,定义为它们坐标值差的绝对值. 我们需要在一些村庄建 ...

  4. WOJ 124. Football Coach 网络流

    Problem 1124 - Football Coach Description It is not an easy job to be a coach of a football team. Th ...

  5. linux内核中的链表

    1.内核中的链表 linux内核链表与众不同,他不是把将数据结构塞入链表,而是将链表节点塞入数据,在2.1内核中引入了官方链表,从此内核中所有的链表使用都采用此链表,千万不要在重复造车轮子了!链表实现 ...

  6. 【SDOI2009】学校食堂

    Description 小F的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭.学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴.当然,不同的人口味也不一定相同,但每个人的口味都可以用 ...

  7. Frame buffer分析 - fbmem.c【转】

    转自:http://www.cnblogs.com/armlinux/archive/2012/03/01/2396753.html 45 struct fb_info *registered_fb[ ...

  8. OCP 认证考试报名费技巧题库051052053解析合格线

    本人于2017年4月22日通过参加OCP考试,第一次参加,一天之内考了三门,三门一次性通过,052 - 95% ,053 - 86% ,051 - 100% 一.关于考试考试报名费: 052:158$ ...

  9. 面试题2:实现Singleton模式

    题目:设计一个类,我们只能生成该类的一个实例.

  10. Cisco banner 登陆消息提示设置命令详解

    从法律角度来看,登陆消息非常重要.当入侵者进入网络而没有受到适当的警告时,他们有可能赢得官司.在放置登陆消息之前应让律师检查下,永远不要使用"欢迎"等问候语,以免被误解为邀请大家使 ...