Android 事件机制 - 回调

在上一章节中我们讲过 Android 由两种事件处理机制 基于监听基于回调基于监听 我们在上一章节中已经学习了,这一章节我们来学习 基于回调的事件处理机制

回调

回调就是组件自己会监听自己,如果自己的某些属性改变了,就会调用自己的某些方法,而这些方法又会调用其它组件的某些方法

打个比方,比如我最爱喝 金桔柠檬,我们点好后只要留下 ,然后坐等服务员叫我们就好了,服务员会时时监听我的 金桔柠檬 做好了没,如果做好了,就会说 X 先生,你的金桔柠檬做好了

服务员主动告诉我们 x 先生,金桔柠檬做好了 就是一种回调

Android 回调的事件处理机制

Android 有两个场景可以使用基于回调的事件处理机制

  1. 自定义 View
  2. 基于回调的事件传播

自定义 View

当用户在 UI 控件上触发某个事件时,控件有自己特定的方法会负责处理该事件

自定义 View 通用的做法是: 继承基础的 UI 控件,重写该控件的事件处理方法

在 xml 布局中使用自定义的 View 时,需要使用 "全限定类名"

常见 View 组件的回调方法

Android 中很多 UI 控件都提供了一些是事件回调方法,比如 View,有以下几个方法

回调方法 说明
boolean onTouchEvent(MotionEvent event) 触摸 UI 控件时触发
boolean onKeyDown(int keyCode,KeyEvent event) 在 UI 控件上按下手指时触发
boolean onKeyUp(int keyCode,KeyEvent event) 在 UI 控件上松开手指时触发
boolean onKeyLongPress(int keyCode,KeyEvent event) 长按组件某个按钮时
boolean onKeyShortcut(int keyCode,KeyEvent event) 键盘快捷键事件发生
boolean onTrackballEvent(MotionEvent event) 在组件上触发轨迹球屏事件
void onFocusChanged (boolean gainFocus, int direction, Rect previously FocusedRect) 当 UI 控件的焦点发生改变

范例

我们接下来写一个范例来熟悉下基于回调的事件处理方式

我们会自定义一个 EditText,然后重写上表中的一系列方法

  1. 创建一个 空的 Android 项目 cn.twle.android.EventCallback

  2. MainActivity.java 目录下新建一个文件 MsEditText.java 实现自定义 EditText

    package cn.twle.android.eventcallback;
    
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.KeyEvent;
    import android.view.MotionEvent;
    import android.widget.EditText;
    import android.content.Context;
    
    public class MsEditText extends EditText  {
    
        private static String TAG = "MsEditText";  
        public MsEditText(Context context, AttributeSet attrs) {  
            super(context, attrs);  
        }
    
        //重写键盘按下触发的事件  
        @Override  
        public boolean onKeyDown(int keyCode, KeyEvent event) {  
            super.onKeyDown(keyCode,event);  
            Log.d(TAG, "onKeyDown() 方法被调用");  
            return true;  
        }
    
        //重写弹起键盘触发的事件  
        @Override  
        public boolean onKeyUp(int keyCode, KeyEvent event) {  
            super.onKeyUp(keyCode,event);  
            Log.d(TAG,"onKeyUp() 方法被调用");  
            return true;  
        }
    
        //组件被触摸了  
        @Override  
        public boolean onTouchEvent(MotionEvent event) {  
            super.onTouchEvent(event);  
            Log.d(TAG,"onTouchEvent() 方法被调用");  
            return true;  
        }  
    }
    
  3. 修改 activity_main.xml 添加刚刚自定义的按钮

    <?xml version="1.0" encoding="utf-8" ?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal" 
        android:orientation="vertical" >
    
        <cn.twle.android.eventcallback.MsEditText
            android:layout_marginTop="16dp" 
            android:id="@+id/et_ev"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="自定义 MsEditText"/>
    
    </LinearLayout>
    

运行后显示日志如下

我们可以看到,因为我们直接重写了 Button 的三个回调方法,当发生点击事件后就不需要添加 事件监听器就可以完成回调

组件会处理对应的事件,即事件由事件源(组件)自身处理

基于回调的事件传播

细心的看一下上表列出的几个事件回调方法,为什么它们的返回值总是 boolean 类型,它有什么用呢?

要回答这个问题,我们就要先搞清楚 Android 中事件处理的流程

  1. 触发该 UI 的事件监听器
  2. 触发该 UI 控件提供的回调方法
  3. 传播到该 UI 组件所在的 Activity

如果三个流程中任意一个返回了 true 就不会继续向外传播,后面的就不会执行了

所以 返回值 boolean 是用来标示这个方法是否已经被完全处理,如果为 false 的话就是没处理完,就会触发该组件所在 Activity 中相关的回调方法

综上,一个事件是否向外传播取决于方法的返回值是时 true 还是 false

范例

  1. 复用上面的范例

  2. 修改 MsEditText.javaonKeyDown() 方法返回 false

    package cn.twle.android.eventcallback;
    
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.KeyEvent;
    import android.view.MotionEvent;
    import android.widget.EditText;
    import android.content.Context;
    
    public class MsEditText extends EditText  {
    
        private static String TAG = "MsEditText";  
        public MsEditText(Context context, AttributeSet attrs) {  
            super(context, attrs);  
        }
    
        //重写键盘按下触发的事件  
        @Override  
        public boolean onKeyDown(int keyCode, KeyEvent event) {  
            super.onKeyDown(keyCode,event);  
            Log.d(TAG, "onKeyDown() 方法被调用");  
            return false;  
        }
    
        //重写弹起键盘触发的事件  
        @Override  
        public boolean onKeyUp(int keyCode, KeyEvent event) {  
            super.onKeyUp(keyCode,event);  
            Log.d(TAG,"onKeyUp() 方法被调用");  
            return true;  
        }
    
        //组件被触摸了  
        @Override  
        public boolean onTouchEvent(MotionEvent event) {  
            super.onTouchEvent(event);  
            Log.d(TAG,"onTouchEvent() 方法被调用");  
            return true;  
        }  
    }
    
  3. 然后修改 MainActivity.java

    package cn.twle.android.eventcallback;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    
    import android.util.Log;
    
    import android.view.View;
    import android.view.KeyEvent;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            MsEditText et_ev = (MsEditText)findViewById(R.id.et_ev);
    
            et_ev.setOnKeyListener(new View.OnKeyListener() {  
                @Override  
                public boolean onKey(View v, int keyCode, KeyEvent event) {  
                    if(event.getAction() == KeyEvent.ACTION_DOWN)  
                    {  
                        Log.d("MsEditText","监听器的 onKeyDown() 方法被调用");  
                    }  
                    return false;  
                }  
            });  
        }
    
        @Override  
        public boolean onKeyDown(int keyCode, KeyEvent event) {  
            super.onKeyDown(keyCode, event);  
            Log.i("MsEditText","Activity 的 onKeyDown() 方法被调用");  
            return false;  
        }  
    }
    

从运行结果可以看出,我们上面说的三个流程是正确的,传播机制为:

监听器 ---> view 组件的回调方法 ---> Activity的回调方法

可以说 Android 事件处理机制中的基于回调的事件处理机制的核心就是 事件传播的顺序

监听器优先,然后到 View 组件自身,最后再到 Activity 任意一个流程返回值 false 继续传播,true 终止传播

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.