fix: 下拉手势改用Activity.dispatchTouchEvent检测
前两种方案(SwipeDownLayout.onInterceptTouchEvent、 RecyclerView.OnItemTouchListener)均无法检测到下拉。 改用Activity.dispatchTouchEvent——触摸事件链最顶层, 在任何View处理之前就能看到所有触摸事件,不可能被拦截。 HomeFragment注册回调,onDestroyView时清理避免泄漏。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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 打卡模式(后续模块实现)
|
||||
|
||||
@@ -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<FragmentHomeBinding>() {
|
||||
observePunchState()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
// 清理 Activity 下拉回调,避免泄漏
|
||||
(activity as? com.xiaoqu.watch.app.MainActivity)?.onSwipeDown = null
|
||||
}
|
||||
|
||||
// ===== 打卡面板 =====
|
||||
|
||||
/** 初始化打卡面板 */
|
||||
@@ -215,52 +219,17 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
||||
|
||||
// ===== 下拉手势 =====
|
||||
|
||||
/** 触摸起始 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
|
||||
(activity as? com.xiaoqu.watch.app.MainActivity)?.onSwipeDown = {
|
||||
// 只在主页(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
|
||||
if (binding.viewPager.currentItem == 1 && !punchPanel.isShowing) {
|
||||
showPunchPanel()
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
swipeTriggered = false
|
||||
}
|
||||
}
|
||||
return false // 不拦截,ViewPager2 正常工作
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 主页 =====
|
||||
|
||||
Reference in New Issue
Block a user