如何实现仅双击触发波纹动画、单击不触发?(ExoPlayer场景)
问题背景
我正在尝试为ExoPlayer实现和YouTube完全一致的视频快进功能,参考相关内容后已成功添加双击监听器。
问题描述
当前遇到的问题:单击视图时也会触发波纹动画,不符合需求——仅希望双击触发快进时显示波纹动画。尝试通过统计点击次数控制波纹可见性,但未达预期,反而出现异常:单击时显示波纹,双击时虽正常触发一次波纹,还会莫名多出现一次(见下方GIF):
{:width="400"}
GIF画质不佳,敬请谅解
代码示例
- Kotlin代码
var doubleClickLastTime = 0L var clicksDone = 0 binding.forward.setOnClickListener { if((System.currentTimeMillis() - doubleClickLastTimeF) < 350){ doubleClickLastTime = 0 clicksDone = 0 exoPlayer.seekTo(exoPlayer.contentPosition + 10000) }else{ if (clicksDoneF == 0){ binding.forward.foreground = null }else{ binding.forward.foreground = with(TypedValue()) { this@VideoPlayer.theme.resolveAttribute( android.R.attr.selectableItemBackground, this, true) ContextCompat.getDrawable(this@VideoPlayerFromUrl, resourceId) } } clicksDone++ doubleClickLastTime = System.currentTimeMillis() } }
- XML代码
<androidx.cardview.widget.CardView android:id="@+id/forward" android:layout_width="550dp" android:layout_height="match_parent" app:layout_constraintStart_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent" android:layout_marginVertical="50dp" android:foreground="?selectableItemBackgroundBorderless" app:cardCornerRadius="16px" app:cardElevation="0dp" android:backgroundTint="@android:color/transparent"/>
解决方案
核心思路
放弃动态修改foreground控制波纹的方式,改用手动触发波纹动画,仅在确认是双击时执行快进并显示波纹,同时屏蔽默认点击波纹。
具体实现
- 修改XML布局
移除android:foreground属性,避免默认点击触发波纹:
<androidx.cardview.widget.CardView android:id="@+id/forward" android:layout_width="550dp" android:layout_height="match_parent" app:layout_constraintStart_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent" android:layout_marginVertical="50dp" app:cardCornerRadius="16px" app:cardElevation="0dp" android:backgroundTint="@android:color/transparent"/>
- 用GestureDetector区分单击/双击
通过手势检测器准确识别双击动作,仅在双击时执行快进并触发波纹:
val gestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() { override fun onDoubleTap(e: MotionEvent): Boolean { // 执行快进逻辑 exoPlayer.seekTo(exoPlayer.contentPosition + 10000) // 手动触发波纹动画 val rippleDrawable = ContextCompat.getDrawable(this@VideoPlayer, R.drawable.custom_ripple) rippleDrawable?.let { it.setBounds(0, 0, binding.forward.width, binding.forward.height) binding.forward.foreground = it it.start() // 动画结束后移除foreground,避免影响后续操作 it.callback = object : Drawable.Callback { override fun invalidateDrawable(who: Drawable) {} override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {} override fun unscheduleDrawable(who: Drawable, what: Runnable) {} override fun onDrawableStateChanged(who: Drawable) { if (!who.isRunning) { binding.forward.foreground = null } } } } return true } // 屏蔽单击的默认响应 override fun onSingleTapConfirmed(e: MotionEvent): Boolean { return true } }) binding.forward.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) true }
- 自定义波纹Drawable(可选)
若需要和YouTube一致的波纹效果,创建res/drawable/custom_ripple.xml:
<?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/your_ripple_color"> <!-- 替换为目标波纹颜色 --> <item android:id="@android:id/mask"> <shape android:shape="rectangle"> <corners android:radius="16dp"/> <!-- 和CardView圆角保持一致 --> </shape> </item> </ripple>
原方案问题原因
原方案通过动态修改foreground的时机逻辑有缺陷,单击时的foreground修改会延迟生效,双击时两次点击事件会分别触发foreground的设置与清除,导致出现两次波纹。GestureDetector能准确区分单击和双击,从根源避免该问题。
内容的提问来源于stack exchange,提问作者Sambhav Khandelwal
相关产品推荐
相关产品推荐

