结合源码,重温 Android View 的事件处理知多少 ?

Android View事件处理在我们的编程中,可谓是无处不在了。但对于大多数人而言,一直都是简单的使用,对其原理缺乏深入地认识。

Android 有一段时间了,最近发现,很多基础知识开始有些遗忘了,所以从新复习了 View事件分发。特地整理成了这篇文章分享给大家。

本文不难,可以作为大家茶余饭后的休闲。

祝大家阅读愉快!

Android View 的事件处理

方便大家学习,我在 GitHub 上建立个 仓库

仓库内容与博客同步更新。由于我在 稀土掘金 简书 CSDN 博客园 等站点,都有新内容发布。所以大家可以直接关注该仓库,即使获得精彩内容

仓库地址:
超级干货!精心归纳 Android 、JVM 、算法等,各位帅气的老铁支持一下!给个 Star !

一、View 的事件回调

我们结合源码看看 View 的事件分发是个怎样的过程,首先我们建立一个类 MyButton 类继承 AppCompatButton 用于测试:

public class MyButton extends AppCompatButton { private final String TAG = "DeBugMyButton"; public MyButton(Context context) { super(context); } public MyButton(Context context, AttributeSet attrs) { super(context, attrs); } public MyButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } } 1.1 事件分发流程

我们都知道有一个方法叫做 public boolean dispatchTouchEvent(MotionEvent event) 。首先我们要知道,对于我们这个自定义控件,他的触摸事件都是从我们 dispatchTouchEvent 这个方法开始往下去分发的。所以可以说:这个方法是一个入口方法。

1.1.1 onTouchEvent 作用

现在我们重写该方法和另一个方法:onTouchEvent ,并且打印一行日志:

@Override public boolean dispatchTouchEvent(MotionEvent event) { Log.d(TAG, "----on dispatch Touch Event----"); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(TAG, "----on touch event----"); } return super.onTouchEvent(event); }

然后我们在 MainActivity 中,设置一个实例化一个 MyButton 控件对象用于测试,并且给他添加一个 onClickListenter 和 setOnTouchListener

public class MainActivity extends AppCompatActivity { private final String TAG = "DeBugMainActivity"; /** * 自定义控件 MyButton */ private MyButton mMyButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iniView(); } /** * 实例化控件 */ private void iniView() { mMyButton = findViewById(R.id.my_button); mMyButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d(TAG, "----on touch----"); break; default: break; } return false; } }); mMyButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "----on click----"); } }); } }

然后我们运行这个 Demo ,点击 MyButton 按钮,会的到如下日志:

结合源码,重温 Android View 的事件处理知多少 ?

我们可以看到首先回调了这个 dispatchTouchEvent ,然后是它的监听器 OnTouch ,接着是它的 onTouchEvent,最后又执行了 dispatchTouchEvent ,那么这是为什么呢?

这是因为我们这儿只监听了 ACTION_DOWN 而当手指抬起时它同样还回去回调 dispatchTouchEvent ,最后我们打印 OnClick 的回调。

总结一下就是:
dispatchTouchEvent -> setOnTouchListener -> onTouchEvent -> setOnClickListener

说明我们 setOnClickListener 是通过 onTouchEvent 处理,产生了 OnClick 。一会我们再来看看其中的原理。

既然说 dispatchTouchEvent 像一个入口,就先让我们来看下它是怎么处理和操作的: 首先,既然我们调用了 super.dispatchTouchEvent(event) ,那么我们就来看看它父类中是怎么实现该方法的。不信的是,它的父类 AppCompatButton 也没有实现该方法 ,最后经过层层搜寻,我们发现这个方法是属于 View 的方法。

1.1.2 dispatchTouchEvent 的实现

那么现在我们来看看 View 的 dispatchTouchEvent 怎么实现的:

public boolean dispatchTouchEvent(MotionEvent event) { ...... //noinspection SimplifiableIfStatement 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; } } if (!result && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } // Clean up after nested scrolls if this is the end of a gesture; // also cancel it if we tried an ACTION_DOWN but we didn't want the rest // of the gesture. if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && !result)) { stopNestedScroll(); } return result; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwxgg.html