当前位置: 首页 > article >正文

安卓触摸事件的传递

setOnTouchListener()返回值的副作用(触摸事件是否继续往下或往后传递)如下:

返回值效果是否往下层view传递是否往当前view的后续监听传递
true该pointer离开屏幕前的后续所有触摸事件都会传递给该TouchListener
false该pointer离开屏幕前的后续所有触摸事件都不会再传递给该TouchListener

注:

  1. 如果view设置了setOnClickListenersetOnLongClickListener,效果等同于在setOnTouchListener()执行完setOnClickListenersetOnLongClickListener的业务逻辑后返回true
  2. 并非所有view都允许触摸事件往下传递,如Button及其子类就不允许触摸事件向下传递,应该是默认实现了setOnClickListener
  3. 触摸事件执行先后顺序为setOnTouchListener -> setOnLongClickListener -> setOnClickListener

触摸事件的传递可以用以下代码理解:

package com.example.study.controller;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 安卓处理触摸事件示意(为方便理解,假设只有一个手指pointer触摸屏幕)
 */
public class TouchEventProcess {
    // 长按的时间
    private static final long LONG_CLICK_TIME_MILLIS = 500L;
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    private boolean touch = false;
    private boolean longClick = false;
    private Timer timer;
    private ViewListener viewListener;
    private List<View> list = new ArrayList<>();

    /**
     * 在这里设置触摸监听
     */
    public TouchEventProcess() {
        viewListener = new ViewListener();
        viewListener.setOnTouchListener((view, event) -> {
            System.out.println(DATE_FORMAT.format(new Date()) + " process touch event:" + MotionEvent.getEventName(event.actionMasked));
            return false;
        });
        viewListener.setOnLongClickListener(view -> {
            System.out.println(DATE_FORMAT.format(new Date()) + " process long click event");
            return false;
        });
        viewListener.setOnClickListener(view -> {
            System.out.println(DATE_FORMAT.format(new Date()) + " process click event");
        });
    }

    /**
     * 多个view
     *
     * @param event
     */
    public void processTouchEvent(MotionEvent event) {
        for (View view : list) {
            if (processTouchEventInView(view, event)) {
                return;
            }
            System.out.println("=====touc event trans to next view=====");
        }
    }

    public boolean processTouchEventInView(View view, MotionEvent event) {
        // 如果当前view最终返回的是false,不再响应当前pointer的触摸事件
        if (!viewListener.hasAnyListener()) {
            reset(event, "no process");
            return false;
        }
        if (viewListener.getOnTouchListener() != null) {
            touch = viewListener.getOnTouchListener().onTouch(view, event);
        }
        if (touch) {
            reset(event, "process touch");
            return true;
        }
        if (viewListener.getOnLongClickListener() != null) {
            if (event.actionMasked == MotionEvent.ACTION_DOWN) {
                timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        longClick = viewListener.getOnLongClickListener().onLongClick(view);
                        timer.cancel();
                    }
                }, LONG_CLICK_TIME_MILLIS);
            }
        }
        if (longClick) {
            reset(event, "process long click");
            return viewListener.hasClickListener();
        }
        if (viewListener.getOnClickListener() != null) {
            if (event.actionMasked == MotionEvent.ACTION_UP) {
                viewListener.getOnClickListener().onClick(view);
            }
        }
        reset(event, "process end");
        return viewListener.hasClickListener();
    }

    private void reset(MotionEvent event, String msg) {
        // 抬起手指pointer时重置
        if (event.actionMasked != MotionEvent.ACTION_UP) {
            return;
        }
        touch = false;
        longClick = false;
        if (timer != null) {
            timer.cancel();
        }
        System.out.println(DATE_FORMAT.format(new Date()) + " reset by " + msg);
    }

    public interface OnTouchListener extends Listener {
        boolean onTouch(View view, MotionEvent event);
    }

    public interface OnLongClickListener extends Listener {
        boolean onLongClick(View view);
    }

    public interface OnClickListener extends Listener {
        void onClick(View view);
    }

    public interface Listener {
    }

    static class ViewListener {
        private OnTouchListener onTouchListener;
        private OnLongClickListener onLongClickListener;
        private OnClickListener onClickListener;

        public OnTouchListener getOnTouchListener() {
            return onTouchListener;
        }

        public void setOnTouchListener(OnTouchListener onTouchListener) {
            this.onTouchListener = onTouchListener;
        }

        public OnLongClickListener getOnLongClickListener() {
            return onLongClickListener;
        }

        public void setOnLongClickListener(OnLongClickListener onLongClickListener) {
            this.onLongClickListener = onLongClickListener;
        }

        public OnClickListener getOnClickListener() {
            return onClickListener;
        }

        public void setOnClickListener(OnClickListener onClickListener) {
            this.onClickListener = onClickListener;
        }

        public boolean hasAnyListener() {
            return onTouchListener != null || onLongClickListener != null || onClickListener != null;
        }

        public boolean hasClickListener() {
            return onLongClickListener != null || onClickListener != null;
        }
    }

    static class View {
    }

    static class MotionEvent {
        public static int ACTION_DOWN = 0;
        public static int ACTION_MOVE = 1;
        public static int ACTION_UP = 2;
        int actionMasked;

        public MotionEvent(int actionMasked) {
            this.actionMasked = actionMasked;
        }

        public static String getEventName(int actionMasked) {
            for (Field field : MotionEvent.class.getFields()) {
                try {
                    if ((int) field.get(MotionEvent.class) == actionMasked) {
                        return field.getName();
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
            return "unknow";
        }
    }

    public static void main(String[] args) {
        TouchEventProcess touchEventProcess = new TouchEventProcess();
        touchEventProcess.list.add(new View());
        touchEventProcess.list.add(new View());
        touchEventProcess.list.add(new View());
        touchEventProcess.processTouchEvent(new MotionEvent(MotionEvent.ACTION_DOWN));
        touchEventProcess.processTouchEvent(new MotionEvent(MotionEvent.ACTION_MOVE));
        long click = 100L;
        long longClick = 1000L;
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                touchEventProcess.processTouchEvent(new MotionEvent(MotionEvent.ACTION_UP));
                timer.cancel();
            }
        }, longClick); // 调整delay即可切换短按长按
    }
}

验证代码

可用以下代码验证触摸事件的传递:

布局文件touch_event_test.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/touch_test_0"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F0F0F0"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:padding="5dp"
        android:text="return true"
        android:textSize="32sp"
        android:textStyle="bold" />

    <!--第一层-->
    <LinearLayout
        android:id="@+id/touch_test_1_3"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="#7A7374">
        <!--第二层-->
        <LinearLayout
            android:id="@+id/touch_test_1_2"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="#1BA784">
            <!--第三层-->
            <TextView
                android:id="@+id/touch_test_1_1"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:background="#EB507E"
                android:gravity="bottom|right"
                android:padding="5dp"
                android:text="1_1"
                android:textSize="16sp"
                android:textStyle="bold" />

            <TextView
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:layout_gravity="bottom|right"
                android:gravity="bottom|right"
                android:padding="5dp"
                android:text="1_2"
                android:textSize="16sp"
                android:textStyle="bold" />
        </LinearLayout>

        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="bottom|right"
            android:gravity="bottom|right"
            android:padding="5dp"
            android:text="1_3"
            android:textSize="16sp"
            android:textStyle="bold" />
    </LinearLayout>

    <!--分割线-->
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:background="#000000" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:padding="5dp"
        android:text="return false"
        android:textSize="32sp"
        android:textStyle="bold" />

    <!--第一层-->
    <LinearLayout
        android:id="@+id/touch_test_2_3"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="#7A7374">
        <!--第二层-->
        <LinearLayout
            android:id="@+id/touch_test_2_2"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="#1BA784">
            <!--第三层-->
            <TextView
                android:id="@+id/touch_test_2_1"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:background="#EB507E"
                android:gravity="bottom|right"
                android:padding="5dp"
                android:text="2_1"
                android:textSize="16sp"
                android:textStyle="bold" />

            <TextView
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:layout_gravity="bottom|right"
                android:gravity="bottom|right"
                android:padding="5dp"
                android:text="2_2"
                android:textSize="16sp"
                android:textStyle="bold" />
        </LinearLayout>

        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="bottom|right"
            android:gravity="bottom|right"
            android:padding="5dp"
            android:text="2_3"
            android:textSize="16sp"
            android:textStyle="bold" />
    </LinearLayout>
</LinearLayout>

TouchEventTestActivity.java

package org.tao.hetools.activities;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import androidx.activity.ComponentActivity;
import androidx.annotation.Nullable;

import org.tao.hetools.R;

public class TouchEventTestActivity extends ComponentActivity {
    private static final String TAG = "TouchEventTestActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.touch_event_test);
        initView();
    }

    @SuppressLint("ClickableViewAccessibility")
    private void initView() {
        View _0 = findViewById(R.id.touch_test_0);
        View _1_1 = findViewById(R.id.touch_test_1_1);
        View _1_2 = findViewById(R.id.touch_test_1_2);
        View _1_3 = findViewById(R.id.touch_test_1_3);
        View _2_1 = findViewById(R.id.touch_test_2_1);
        View _2_2 = findViewById(R.id.touch_test_2_2);
        View _2_3 = findViewById(R.id.touch_test_2_3);

        _0.setOnTouchListener((view, event) -> {
            showToast(event, "_0");
            return true;
        });
        // return true
        _1_1.setOnTouchListener((view, event) -> {
            showToast(event, "_1_1");
            return true;
        });
        // 设置click longClick事件会对触摸事件在view之间的传递有影响,下同
        // _1_1.setOnClickListener(view -> Toast.makeText(this, "_1_1 clicked", Toast.LENGTH_SHORT).show());
        // _1_1.setOnLongClickListener(view -> {
        //     Toast.makeText(this, "_1_1 long clicked", Toast.LENGTH_SHORT).show();
        //     return true;
        // });
        _1_2.setOnTouchListener((view, event) -> {
            showToast(event, "_1_2");
            return true;
        });
        _1_3.setOnTouchListener((view, event) -> {
            showToast(event, "_1_3");
            return true;
        });

        // return false
        _2_1.setOnTouchListener((view, event) -> {
            showToast(event, "_2_1");
            return false;
        });
        // _2_1.setOnClickListener(view -> Toast.makeText(this, "_2_1 clicked", Toast.LENGTH_SHORT).show());
        // _2_1.setOnLongClickListener(view -> {
        //     Toast.makeText(this, "_2_1 long clicked", Toast.LENGTH_SHORT).show();
        //     return true;
        // });
        _2_2.setOnTouchListener((view, event) -> {
            showToast(event, "_2_2");
            return false;
        });
        _2_3.setOnTouchListener((view, event) -> {
            showToast(event, "_2_3");
            return false;
        });
    }

    private void showToast(MotionEvent event, String msg) {
        Log.i(TAG, msg + " " + event.getActionMasked());
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN -> Toast.makeText(this, msg + " press down", Toast.LENGTH_SHORT).show();
            case MotionEvent.ACTION_UP -> Toast.makeText(this, msg + " press up", Toast.LENGTH_SHORT).show();
        }
    }
}

http://www.kler.cn/a/463016.html

相关文章:

  • VBA 64位API声明语句第005讲
  • C++ 并发专题 - std::promise 和 std::future 介绍
  • el-table 实现纵向多级表头
  • PP模块部分BAPI函数
  • Linux实验报告12-Apache服务器的配置
  • leetcode 149. 直线上最多的点数
  • 电脑有杂音滋滋滋响怎么处理?电脑有杂音解决指南
  • 【信息系统项目管理师】第14章:项目沟通管理过程详解
  • 【vim】vim常用操作总结
  • 深入解析JVM调优工具及其实战应用
  • 软件测试面试八股文,查漏补缺(附文档)
  • latex与word优缺点对比
  • Python基于卷积神经网络的车牌识别系统开发与实现
  • MAC环境安装(卸载)软件
  • C++算法练习day73——45.跳跃游戏2
  • 基于单片机的电梯模拟控制系统
  • 青少年编程与数学 02-005 移动Web编程基础 14课题、性能优化
  • MySQL UNION
  • node-sass安装报错,换成sass
  • 时间敏感网络中遗留端站同步的TSN解决方案
  • 利用Python爬虫获取1688商品详情的探索之旅
  • CentOS Stream 9 搭建三节点Clickhouse集群
  • 芊芊测字,免费测字,ai测字(1.0)
  • springboot+全局异常处理
  • linux制作bin包
  • RabbitMQ - 4 ( 22000 字 RabbitMQ 入门级教程 )