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事件

相关推荐
Lesile3 小时前
Interview#1 历史演进:MVC · MVP · MVVM · MVI架构详解
android·android jetpack
杉氧3 小时前
Flutter 像素级还原实战:用 CustomPaint 与 Bezier 曲线手绘精致图针
android·前端·flutter
我命由我123456 小时前
Android 在构建过程中,发现有两个依赖库都包含了相同路径的资源文件
android·java·开发语言·java-ee·kotlin·android-studio·android runtime
Coffeeee6 小时前
谷歌的一个优化建议,让我重新学了一遍Android里面如何正确处理位图
android·google·kotlin
冰暮流星6 小时前
mysql之新建表及对表的查询
android·数据库·mysql
用户2018792831677 小时前
彻底搞懂LayoutInflater、ViewInflater与textViewStyle优先级:XML属性为何能覆盖全局样式?
android
达达尼昂7 小时前
AI Native 工程实践:如何为 Claude 5 设计更有效的上下文
android·人工智能·后端
Cry丶7 小时前
【已解决】adb devices 能识别手机,但 HBuilderX 搜索不到设备的避坑指南
android·adb·uni-app·hbuilderx·真机调试
墨神谕8 小时前
认识一下Unicode(统一码)
android·java·数据库
Sirens.19 小时前
MySQL表设计进阶-约束范式连接索引与事务
android·数据库·mysql