From b274012019155fd06c09256c1b890bd59f8edba2 Mon Sep 17 00:00:00 2001 From: dongliang Date: Tue, 28 Apr 2026 21:13:46 +0930 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=B8=8B=E6=8B=89=E6=89=8B=E5=8A=BF?= =?UTF-8?q?=E6=94=B9=E7=94=A8Activity.dispatchTouchEvent=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 前两种方案(SwipeDownLayout.onInterceptTouchEvent、 RecyclerView.OnItemTouchListener)均无法检测到下拉。 改用Activity.dispatchTouchEvent——触摸事件链最顶层, 在任何View处理之前就能看到所有触摸事件,不可能被拦截。 HomeFragment注册回调,onDestroyView时清理避免泄漏。 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../java/com/xiaoqu/watch/app/MainActivity.kt | 39 +++++++++++++ .../com/xiaoqu/watch/ui/home/HomeFragment.kt | 55 ++++--------------- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/xiaoqu/watch/app/MainActivity.kt b/app/src/main/java/com/xiaoqu/watch/app/MainActivity.kt index 2275be3..796194f 100644 --- a/app/src/main/java/com/xiaoqu/watch/app/MainActivity.kt +++ b/app/src/main/java/com/xiaoqu/watch/app/MainActivity.kt @@ -2,6 +2,7 @@ package com.xiaoqu.watch.app import android.content.pm.ActivityInfo import android.os.Bundle +import android.view.MotionEvent import android.view.View import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity @@ -10,6 +11,7 @@ import com.xiaoqu.watch.service.manager.SystemStateMonitor import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber import javax.inject.Inject +import kotlin.math.abs /** * 主 Activity(Launcher 模式,单 Activity + 多 Fragment 架构) @@ -54,6 +56,43 @@ class MainActivity : AppCompatActivity() { systemStateMonitor.unregister() } + // ===== 下拉手势检测(Activity 级别,不可能被子 View 拦截) ===== + + /** 下拉回调(由 HomeFragment 注册) */ + var onSwipeDown: (() -> Unit)? = null + + private var touchStartY = 0f + private var touchStartX = 0f + private var swipeTriggered = false + + override fun dispatchTouchEvent(ev: MotionEvent): Boolean { + // 在事件分发给任何 View 之前,先检测下拉手势 + if (onSwipeDown != null) { + when (ev.action) { + MotionEvent.ACTION_DOWN -> { + touchStartY = ev.rawY + touchStartX = ev.rawX + swipeTriggered = false + } + MotionEvent.ACTION_MOVE -> { + if (!swipeTriggered) { + val dy = ev.rawY - touchStartY + val dx = ev.rawX - touchStartX + // 下滑超过 50px 且垂直方向为主 + if (dy > 50 && abs(dy) > abs(dx) * 1.5f) { + swipeTriggered = true + onSwipeDown?.invoke() + } + } + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + swipeTriggered = false + } + } + } + return super.dispatchTouchEvent(ev) + } + /** * 物理返回键拦截: * - 已绑定用户 → 开启 NFC 打卡模式(后续模块实现) diff --git a/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt b/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt index ab40ee4..2a9d722 100644 --- a/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/xiaoqu/watch/ui/home/HomeFragment.kt @@ -2,12 +2,10 @@ package com.xiaoqu.watch.ui.home import android.os.Bundle import android.view.LayoutInflater -import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.widget.TextView import android.widget.Toast -import androidx.recyclerview.widget.RecyclerView import androidx.core.os.bundleOf import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -124,6 +122,12 @@ class HomeFragment : BaseFragment() { observePunchState() } + override fun onDestroyView() { + super.onDestroyView() + // 清理 Activity 下拉回调,避免泄漏 + (activity as? com.xiaoqu.watch.app.MainActivity)?.onSwipeDown = null + } + // ===== 打卡面板 ===== /** 初始化打卡面板 */ @@ -215,51 +219,16 @@ class HomeFragment : BaseFragment() { // ===== 下拉手势 ===== - /** 触摸起始 Y */ - private var touchStartY = 0f - /** 触摸起始 X */ - private var touchStartX = 0f - /** 本次手势是否已触发过下拉 */ - private var swipeTriggered = false - /** * 初始化下拉手势检测 - * 通过 RecyclerView.OnItemTouchListener 监听 ViewPager2 内部触摸 - * 这是最可靠的方式——RecyclerView 在处理事件前会先通知所有 listener + * 在 Activity.dispatchTouchEvent 中检测——触摸事件链最顶层,不会被任何子 View 拦截 */ private fun initSwipeDownDetector() { - // ViewPager2 内部 RecyclerView 需等布局完成后获取 - binding.viewPager.post { - val recyclerView = binding.viewPager.getChildAt(0) as? RecyclerView - recyclerView?.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() { - override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { - when (e.action) { - MotionEvent.ACTION_DOWN -> { - touchStartY = e.rawY - touchStartX = e.rawX - swipeTriggered = false - } - MotionEvent.ACTION_MOVE -> { - if (swipeTriggered) return false - // 只在主页(page=1)且面板未展开时响应 - if (binding.viewPager.currentItem != 1) return false - if (punchPanel.isShowing) return false - - val dy = e.rawY - touchStartY - val dx = e.rawX - touchStartX - // 下滑超过 60px 且方向以垂直为主 - if (dy > 60 && Math.abs(dy) > Math.abs(dx) * 1.5f) { - swipeTriggered = true - showPunchPanel() - } - } - MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { - swipeTriggered = false - } - } - return false // 不拦截,ViewPager2 正常工作 - } - }) + (activity as? com.xiaoqu.watch.app.MainActivity)?.onSwipeDown = { + // 只在主页(page=1)且面板未展开时响应 + if (binding.viewPager.currentItem == 1 && !punchPanel.isShowing) { + showPunchPanel() + } } }