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 6412603..1b911c4 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 @@ -1,9 +1,7 @@ package com.xiaoqu.watch.ui.home import android.os.Bundle -import android.view.GestureDetector import android.view.LayoutInflater -import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.widget.TextView @@ -77,9 +75,6 @@ class HomeFragment : BaseFragment() { private var debugTapCount = 0 private var lastTapTime = 0L - // ===== 下拉手势检测 ===== - private lateinit var gestureDetector: GestureDetector - override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentHomeBinding { return FragmentHomeBinding.inflate(inflater, container, false) } @@ -112,7 +107,7 @@ class HomeFragment : BaseFragment() { initConfigPage() // 初始化下拉手势 - initGestureDetector() + initSwipeDownDetector() // 启动时钟定时器 startClockUpdater() @@ -150,9 +145,10 @@ class HomeFragment : BaseFragment() { showRevokeConfirm() } - // 面板关闭时恢复 ViewPager2 滑动 + // 面板关闭时恢复 ViewPager2 滑动和下拉检测 punchPanel.onDismiss = { binding.viewPager.isUserInputEnabled = true + binding.swipeDownLayout.swipeEnabled = true } } @@ -208,8 +204,9 @@ class HomeFragment : BaseFragment() { /** 展开打卡面板 */ private fun showPunchPanel() { if (punchPanel.isShowing) return - // 禁用 ViewPager2 滑动 + // 禁用 ViewPager2 滑动和下拉检测 binding.viewPager.isUserInputEnabled = false + binding.swipeDownLayout.swipeEnabled = false // 查询考勤状态 punchViewModel.fetchAttendance() // 展开面板 @@ -218,33 +215,17 @@ class HomeFragment : BaseFragment() { // ===== 下拉手势 ===== - /** 初始化下拉手势检测器 */ - private fun initGestureDetector() { - gestureDetector = GestureDetector(requireContext(), - object : GestureDetector.SimpleOnGestureListener() { - override fun onFling( - e1: MotionEvent?, - e2: MotionEvent, - velocityX: Float, - velocityY: Float - ): Boolean { - // 只在主页(page=1)且面板未展开时响应下拉 - if (binding.viewPager.currentItem != 1) return false - if (punchPanel.isShowing) return false - - // 下拉:velocityY > 0 且垂直速度大于水平速度 - if (velocityY > 500 && Math.abs(velocityY) > Math.abs(velocityX)) { - showPunchPanel() - return true - } - return false - } - }) - - // 在 ViewPager2 上设置触摸监听 - binding.viewPager.getChildAt(0)?.setOnTouchListener { _, event -> - gestureDetector.onTouchEvent(event) - false // 不消费事件,让 ViewPager2 正常处理 + /** + * 初始化下拉手势检测 + * 使用 SwipeDownLayout 在 onInterceptTouchEvent 中检测下拉 + * 比 setOnTouchListener 更可靠——能在事件被子 View 消费前看到 + */ + private fun initSwipeDownDetector() { + binding.swipeDownLayout.onSwipeDown = { + // 只在主页(page=1)且面板未展开时响应 + if (binding.viewPager.currentItem == 1 && !punchPanel.isShowing) { + showPunchPanel() + } } } diff --git a/app/src/main/java/com/xiaoqu/watch/ui/widget/SwipeDownLayout.kt b/app/src/main/java/com/xiaoqu/watch/ui/widget/SwipeDownLayout.kt new file mode 100644 index 0000000..4549e05 --- /dev/null +++ b/app/src/main/java/com/xiaoqu/watch/ui/widget/SwipeDownLayout.kt @@ -0,0 +1,70 @@ +package com.xiaoqu.watch.ui.widget + +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import android.widget.FrameLayout +import kotlin.math.abs + +/** + * 支持下拉手势检测的 FrameLayout + * 在 onInterceptTouchEvent 中检测向下滑动,触发回调 + * 不消费事件,子 View 的水平滑动(如 ViewPager2)不受影响 + * + * 原理同 SwipeRefreshLayout:在事件分发给子 View 之前先观察, + * 只有明确的垂直下拉才拦截 + */ +class SwipeDownLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + companion object { + /** 最小下拉距离(像素) */ + private const val SWIPE_THRESHOLD_PX = 80 + /** 触摸容差:垂直距离必须大于水平距离的倍数 */ + private const val DIRECTION_RATIO = 1.5f + } + + /** 下拉触发回调 */ + var onSwipeDown: (() -> Unit)? = null + + /** 是否启用下拉检测(面板打开时禁用) */ + var swipeEnabled = true + + private var startX = 0f + private var startY = 0f + private var swiped = false + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + if (!swipeEnabled) return false + + when (ev.action) { + MotionEvent.ACTION_DOWN -> { + startX = ev.x + startY = ev.y + swiped = false + } + MotionEvent.ACTION_MOVE -> { + if (swiped) return false + + val dy = ev.y - startY + val dx = ev.x - startX + + // 向下滑动超过阈值,且垂直方向为主 + if (dy > SWIPE_THRESHOLD_PX && abs(dy) > abs(dx) * DIRECTION_RATIO) { + swiped = true + onSwipeDown?.invoke() + // 不拦截事件(return false),让子 View 继续处理 + // 这样 ViewPager2 的左右滑动不会被打断 + } + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + swiped = false + } + } + // 始终返回 false:不拦截事件,只观察 + return false + } +} diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 1d3feb9..5ae8e91 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,6 +1,7 @@ - @@ -43,4 +44,4 @@ android:layout_height="match_parent" android:visibility="gone" /> - +