Android事件:事件传递 基于监听器的事件处理 基于回调的事件处理

本文详细介绍了Android中的事件传递机制,包括dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent的用法,以及基于监听器和回调的两种事件处理方式,如匿名内部类、内部类实现和Activity直接实现接口。
摘要由CSDN通过智能技术生成

目录

一,事件传递机制

1.事件传递机制的三个方法 

(1)public boolean dispatchTouchEvent(MotionEvent event)

(2)public boolean onInterceptTouchEvent(MotionEvent event)

(3)public boolean onTouchEvent(MotionEvent event)

 2.事件传递的流程

二,基于监听器的事件处理

 1.基于监听器事件处理的三种实现方法

(1)匿名内部类实现

(2)内部类实现

(3)Activity直接实现接口

三,基于回调的事件处理


一,事件传递机制

        在Android中,每触摸或点击一次,就会生成一个MotionEvent对象,代表一次触摸事件。Android的事件传递主要由三个方法来完成:

  1. 事件分发:dispatchTouchEvent();
  2. 事件拦截:onInterceptTouchEvent();
  3. 事件响应:onTouchEvent();

         三种方法均返回boolean类型的值,表示是否解决事件。对于ViewGroup类,会进行事件分发,拦截,响应三种操作。但对于View来说,只有事件分发和响应,因为View没有子View,无法再向下传递,也就不需要事件拦截。

1.事件传递机制的三个方法 

(1)public boolean dispatchTouchEvent(MotionEvent event)

        事件分发方法,在Android中,事件首先到达Activity的顶层视图。在事件传递到Activity和View时,都会首先调用分发方法,如果子View的分发方法或当前View的响应方法能够成功解决事件,则返回true,表示当前事件已自我消化,否则返回false。

(2)public boolean onInterceptTouchEvent(MotionEvent event)

        事件拦截方法,View调用拦截方法后,会接着调用自己的响应方法去处理事件,并且如果某个View调用了拦截方法,那么它的父类View就不会再调用拦截方法,子类View成功处理了事件,父类View同样不会调用拦截方法。

(3)public boolean onTouchEvent(MotionEvent event)

        事件处理方法,成功处理返回true,否则返回false,子类View成功处理事件后,父类View不会再调用处理方法。

三种方法的关系用伪代码表示即为:

public boolean dispatchTouchEvent(MotionEvent event) {
	boolean isSolve = false;
	if(onInterceptTouchEvent(event)) {
		isSolve = onTouchEvent(event);
	} else {
		isSolve = child.dispatchTouchEvent(event);
	}
	
	return isSolve;
}

 2.事件传递的流程

        上图为事件传递的流程,由图可知事件传递的顺序为:Activity->ViewGroup->View。事件首先到达Activity的顶层视图,然后调用Activity的事件分发方法,随后事件被传递给最顶层ViewGroup,并调用ViewGroup的分发方法,若ViewGroup拦截事件,则交给ViewGroup的处理方法。若不拦截则继续调用子View的分发方法。如果最终事件没有被任何View处理,则返回给Activity处理。

二,基于监听器的事件处理

Android提供了两种方式的事件处理机制:基于监听器的事件处理基于回调的事件处理

基于监听器的事件处理,涉及到三个核心对象:

  1. 事件源:事件发生的场所(View或ViewGroup);
  2. 事件:用户界面上发生的具体事件;
  3. 事件监听器:负责监听事件源所发生的事件,并作出响应;

 基于监听器的原理就是把事件源事件处理器(监听器)分离,让监听器去处理事件。实现方法就是为视图控件绑定特定的事件监听器。

 1.基于监听器事件处理的三种实现方法

 实现监听器处理事件,或者说是给View绑定监听器的三种常用方法为:

(1)匿名内部类实现

private Button btn_login = findViewById(R.id.btn_login);
btn_login.setOnClickListener( v ->{
    //获取输入内容,用对话框提示出来
    String name = edt_name.getText().toString(); //获取用户名
    String passWard = edt_passward.getText().toString(); //获取密码
    //对话框提示
    Toast.makeText(
        LoginActivity.this,
        "登陆成功:" + name,
        Toast.LENGTH_LONG).show();
        //在哪显示 + 显示内容 + 显示时长
 
});

(2)内部类实现

使用匿名内部类需要一个控件实现一个匿名内部类,使用成员内部类,可实现代码复用

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_practice);
 
        Button btn_login = findViewById(R.id.btn_login);
 
        //2.内部类 便于代码复用 建议使用
        Listener listener = new Listener();
        btn_login.setOnClickListener(listener);  //绑定监听器
        btn_enroll.setOnClickListener(listener);
 
 
    }
class Listener implements View.OnClickListener{
        @Override
        public void onClick(View v) {
            //因为同一对象被多个方法调用,所以需要用id来区分
            int id = v.getId();
            if(id == R.id.btn_login){
                String userName = edt_name.getText().toString();
                String passward = edt_passward.getText().toString();
                Toast.makeText(PracticeActivity.this, "欢迎登录!" + userName, Toast.LENGTH_SHORT).show();
            }
            else{
                String userName = edt_name.getText().toString();
                Toast.makeText(PracticeActivity.this, "注册成功!" + userName, Toast.LENGTH_SHORT).show();
                //日志打印
                Log.i("LoginActivity", "用户注册");
            }
        }
    }

(3)Activity直接实现接口

直接让Activity实现监听器接口,实现监听方法,控件绑定监听器,通过this调用onClick方法。

public class PracticeActivity extends AppCompatActivity implements View.OnClickListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_practice);

        Button btn_login = findViewById(R.id.btn_login);
        Button btn_enroll = findViewById(R.id.btn_enroll);

        btn_enroll.setOnClickListener(this);
        btn_login.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if(id == R.id.btn_login){
            String userName = edt_name.getText().toString();
            String passward = edt_passward.getText().toString();
            Toast.makeText(PracticeActivity.this, "欢迎登录!" + userName, Toast.LENGTH_SHORT).show();
        }
        else{
            String userName = edt_name.getText().toString();
            Toast.makeText(PracticeActivity.this, "注册成功!" + userName, Toast.LENGTH_SHORT).show();
            //日志打印
            Log.i("LoginActivity", "用户注册");
        }
    }
}

三,基于回调的事件处理

        基于监听器的事件处理方法是让监听器去处理View组件产生的事件,在基于回调的事件处理机制中,事件源与事件监听器是统一的,或者说事件源本身就是事件监听器,事件源产生的事件由事件源自己去解决,解决的方法就是调用重写的回调方法。

        Android为视图组件提供了一些事件处理的回调方法,例如View类的包含:

boolean onKeyDown(int keyCode, KeyEvent event)按下按键时触发
boolean onKeyLongPress(int keyCode, KeyEvent event)长按按键时触发
boolean onKeyShortcut(int keyCode, KeyEvent event)键盘快捷键事件发生时触发
boolean onKeyShortcut(int keyCode, KeyEvent event)松开按键时触发
boolean onTouchEvent(MotionEvent event)触发触摸屏事件时触发
boolean onTrackballEvent(MotionEvent event)触发轨迹球事件时触发
void onFocusChanged(boolean gainFocus, int direction, Rect previously FocusedRect)组件焦点发生改变时触发,该方法只能在View中重写

        回调方法会接收一个MotionEvent对象,这个对象包含了触摸动作的具体信息,有以下几种常用类型:

ACTION_DOWN手指首次触摸到屏幕时触发,触发一次
ACTION_UP手指抬起时触发,触发一次
ACTION_MOVE手指在屏幕上滑动时触发,可以触发多次
ACTION_OUTSIDE触摸事件发生在视图边界时发生

        基于回调的事件处理步骤为:(1)自定义视图类继承需要的View类,(2)重写回调方法,(3)在XML文件中使用自定义的视图组件。

1.自定义视图类继承需要的View类,并重写回调方法(在实现构造方法时,必须使用两个参数的构造方法,否则app会奔溃,因为视图是在XML布局文件中定义的,系统会调用这个构造函数来创建和初始化视图,而AttributeSet参数包含了在XML中定义的属性和样式),回调方法返回boolean类型的值,表示是否成功解决事件,成功返回true,否则返回false。

public class MyButton extends androidx.appcompat.widget.AppCompatButton {

    //构造方法必须使用Context,AttributeSet两个参数的
    public MyButton(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    //重写回调方法,写入自己的逻辑
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("btn", "btn1 onTouch");
        switch (event.getAction()){
            case MotionEvent.ACTION_UP:
                Log.i("btn", "bnt1 Up");
                break;
            case MotionEvent.ACTION_DOWN:
                Log.i("btn", "bnt1 Down");
                break;
        }
        //返回是否成功解决事件
        return true;
    }
}

2.在XML文件中使用自定义的视图组件。

<com.example.practice.MyButton
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="按钮"
    tools:ignore="SpeakableTextPresentCheck">
</com.example.practice.MyButton>
  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值