Android16 自定义全局手势 任意界面依次点击屏幕四个角返回Home界面

需求任意app中依次点击屏幕四个角返回Launcher。

主要目标是要捕获全局Touch事件,思路是可以在View.java中dispatchTouchEvent或者参考系统手势实现。鉴于在View中操作容易误触进入其他界面,最后参考系统手势实现。

framework中有一个专门用来监听系统手势的监听器SystemGesturesPointerEventListener.java

直接在这里实现,主要业务在onPointerEvent方法中实现。

先写检测手势触摸的逻辑

java 复制代码
    private void checkFourCornersClick(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        if (screenWidth == 0 || screenHeight == 0) {
            return;
        }
	    //四角位置定位,这部分可以根据自己的需要定义,触摸点的位置,顺序,大小等
        int corner = getCornerPosition(x, y);

        if (corner != -1) {
            if (DEBUG) Slog.d(TAG_CORNERS, "Corner clicked: " + corner + " at (" + x + ", " + y + ")");
			//四角touch顺序判断
            if (isExpectedNextCorner(corner)) {
                mClickedCorners.add(corner);//手势符合后添加到集合
                Slog.i(TAG_CORNERS, "Correct corner order, progress: " + mClickedCorners.size() + "/4");

                resetCornerTimeout();//这里需要判断触摸超时业务
				//四角全部touch后执行下一步任务
                if (mClickedCorners.size() == 4) {
                    Slog.i(TAG_CORNERS, "All four corners clicked! Triggering action.");
                    performFourCornersAction();
                }
            } else {
                Slog.w(TAG_CORNERS, "Wrong corner order. Expected: " + CORRECT_ORDER[mClickedCorners.size()]
                        + ", got: " + corner + ". Reset.");
                resetCornerClick();//未按流程触摸,重置手势集合
            }
        }
    }

四角定位和顺序判断,mCornerSize随意,这里直接写的50

java 复制代码
    private int getCornerPosition(float x, float y) {
        if (x <= mCornerSize && y <= mCornerSize) {
            return 1;//返回值方便顺序判断
        }
        if (x <= mCornerSize && y >= screenHeight - mCornerSize) {
            return 2;
        }
        if (x >= screenWidth - mCornerSize && y <= mCornerSize) {
            return 3;
        }
        if (x >= screenWidth - mCornerSize && y >= screenHeight - mCornerSize) {
            return 4;
        }
        return -1;
    }

	private boolean isExpectedNextCorner(int corner) {
        if (mClickedCorners.size() >= CORRECT_ORDER.length) {
            return false;
        }
        int nextExpected = CORRECT_ORDER[mClickedCorners.size()];
        return corner == nextExpected;
    }

执行手势完成后的任务

java 复制代码
    private void performFourCornersAction() {
        resetCornerClick();

        try {
            Intent intent = new Intent(Intent.ACTION_MAIN);
            intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP);
            intent.addCategory(Intent.CATEGORY_HOME);
            mContext.startActivity(intent);
        } catch (Exception e) {
            Slog.e(TAG_CORNERS, "Failed to start activity", e);
        }

        Slog.i(TAG_CORNERS, "Four corners gesture action completed");
    }

onPointerEvent检测手势

java 复制代码
    @Override
    public void onPointerEvent(MotionEvent event) {
        if (mGestureDetector != null && event.isTouchEvent()) {
            mGestureDetector.onTouchEvent(event);
        }
        //mh@预留手势开关{
        if (mEnableFourCornersGestures && event.getAction() == MotionEvent.ACTION_DOWN) {
            checkFourCornersClick(event);
        }
        //mh@}
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mSwipeFireable = true;
                mDebugFireable = true;
                mDownPointers = 0;
                captureDown(event, 0);
                if (mMouseHoveringAtLeft) {
                    mMouseHoveringAtLeft = false;
       ...

写一个init方法,主要初始化开关和触摸点的大小

java 复制代码
    private void initFourCornersGesture() {
        //触摸超时handler
        mCornerTimeoutRunnable = new Runnable() {
            @Override
            public void run() {
                resetCornerClick();
                if (DEBUG) Slog.d(TAG_CORNERS, "Four corners gesture timeout, reset");
            }
        };
		//自定义ContentObserver,监听开关变化
        mSettingsObserver = new SettingsObserver(mHandler);
        mSettingsObserver.observe();
        updateFourCornersEnabled();
        
        updateCornerSize();

        Slog.i(TAG_CORNERS, "Four corners gesture initialized, enabled: " + mEnableFourCornersGestures
                + ", corner size: " + mCornerSize + "px");
    }

    private void updateFourCornersEnabled() {
        try {
            mEnableFourCornersGestures = Settings.Global.getInt(mContext.getContentResolver(),
                    SETTINGS_KEY_FOUR_CORNERS, 0) == 1;
        } catch (Exception e) {
            Slog.e(TAG_CORNERS, "Failed to read settings, using default", e);
            mEnableFourCornersGestures = true;
        }
    }
	//触摸超时,重置计时器
    private void resetCornerTimeout() {
        mHandler.removeCallbacks(mCornerTimeoutRunnable);
        mHandler.postDelayed(mCornerTimeoutRunnable, CORNER_CLICK_TIMEOUT_MS);
    }
	//重置手势集合
    private void resetCornerClick() {
        mClickedCorners.clear();
        mHandler.removeCallbacks(mCornerTimeoutRunnable);
        if (DEBUG) Slog.d(TAG_CORNERS, "Corner click state reset");
    }

	private class SettingsObserver extends ContentObserver {
        SettingsObserver(Handler handler) {
            super(handler);
        }

        void observe() {
            Uri uri = Settings.Global.getUriFor(SETTINGS_KEY_FOUR_CORNERS);
            mContext.getContentResolver().registerContentObserver(uri, false, this);
            if (DEBUG) Slog.d(TAG_CORNERS, "Settings observer registered for: " + uri);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);
            if (uri == null || SETTINGS_KEY_FOUR_CORNERS.equals(uri.getLastPathSegment())) {
                updateFourCornersEnabled();
                Slog.i(TAG_CORNERS, "Four corners gesture setting changed to: " + mEnableFourCornersGestures);
                
                if (!mEnableFourCornersGestures) {
                    resetCornerClick();
                }
            }
        }
    }

在构造方法中初始化

java 复制代码
	SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) {
        mContext = checkNull("context", context);
        mHandler = handler;
        mCallbacks = checkNull("callbacks", callbacks);
        onConfigurationChanged();
        
        initFourCornersGesture();//mh@
    }

最后在设置--开发者选项中实现一个开关控制

参考:

Android R PointerEventDispatcher 触摸事件监听帮助类

Android获取整个屏幕的Touch事件

相关推荐
三少爷的鞋20 分钟前
换个思路理解:非阻塞挂起
android
好的佩奇1 小时前
蓝牙之经典蓝牙
android·flutter·蓝牙
传奇开心果编程4 小时前
【Flutter踩坑排雷】第19课:路由硬编码坑|字符串路由书写错误,页面跳转失败排查
android·flutter·ios·移动开发·跨平台·ui框架·声明式ui
微小的鱼6 小时前
Linux的渲染与显示系统链路
android·java·linux
道尔柯南18 小时前
C++ string 类详解:从常用接口到深拷贝,彻底掌握字符串处理
android·java·c++
唐诺19 小时前
5、Android JNI 动态注册中的“签名“详解
android·jni
zhaoshuzhaoshu1 天前
Android 系统启动与调试中的日志抓取方式全解析
android
雨声不在1 天前
android 动画掉帧
android
在多学一点1 天前
MYSQL第二次作业
android·数据库·mysql
骑着蜗牛撵大象3271 天前
MySQL 8.0 迁移后 SQL 变慢:执行计划里 3 类被忽略的成本差异
android·数据库·sql·mysql·优化器·执行计划