向左滑动时, 屏幕中的 view 跟随手指滑动, 滑动到一定距离或者有超过指定的滑动速度, 松开时, 触发滑动动画, 清屏或者还原视图.

主要涉及到 onTouchEvent 的处理, 和 属性动画 的启动.

如果业务有有需要不跟随滑动的 view, 可以设置 白名单 -> whitelist, 还有一些具体的 业务 view 需要再滑动处理自己的事件时, 直接在自己的 onTouchEvent 拦截即可.

另外需要注意动画需要在 onDetachedFromWindow 时回收, 不会会造成内存泄漏.

具体代码如下

package com.pb.test.bilibili.widgetimport android.animation.Animator
import android.animation.ObjectAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.VelocityTracker
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.forEach
import androidx.core.view.forEachIndexed
import com.pb.test.bilibili.BiliLogger
import com.pb.test.bilibili.logDebug
import com.pb.test.utils.UITools
import kotlin.math.abs/**** author : YingYing Zhang* e-mail : 540108843@qq.com* time   : 2022-03-20* desc   :**/
class LiveRoomRootView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr), BiliLogger {private var startX = 0Fprivate var endX = 0Fprivate var delta = 0Fprivate var speedX = 0Fprivate val screenWidth = UITools.getScreenWidth(context)private val MIN_DISTANCE = screenWidth / 3private val MIN_SPEED = 400// 不移动名单val whitelist = mutableListOf<Int>()// 执行自己的 onTouchEvent 名单val interceptList = ArrayList<Int>()// 是否处于清屏状态private var isCleared = falseprivate val ANIMATOR_DURATION = 500Lprivate var leftDuration = 0Lprivate var leftDistance = 0Fprivate val animator = ObjectAnimator.ofFloat(1F, 0F).apply {duration = ANIMATOR_DURATIONaddUpdateListener { animation ->// 1 - 0val percent = animation?.animatedValue as FloatlogDebug { "animator - update, percent = $percent" }if (isCleared) {if (isValidAction()) {val ratio = leftDistance * percentlogDebug { "animator - update - recover - valid UP, radio = $ratio" }executeSlideWithFinger(ratio)} else {val ratio = screenWidth - abs(delta) * percentlogDebug { "animator - update  - recover - invalid UP, radio = $ratio" }executeSlideWithFinger(ratio)}} else {if (isValidAction()) {val ratio = screenWidth - leftDistance * percentlogDebug { "animator - update - clear - valid UP, radio = $ratio" }executeSlideWithFinger(ratio)} else {val ratio = abs(delta) * percentlogDebug { "animator - update  - clear - invalid UP, radio = $ratio" }executeSlideWithFinger(ratio)}}}addListener(object : Animator.AnimatorListener {override fun onAnimationStart(animation: Animator?) {}override fun onAnimationEnd(animation: Animator?) {if (isCleared) {if (isValidAction()) {logDebug { "animator - end - isClear false" }isCleared = false}} else {if (isValidAction()) {logDebug { "animator - end - isClear true" }isCleared = true}}resetResources()}override fun onAnimationCancel(animation: Animator?) {}override fun onAnimationRepeat(animation: Animator?) {}})}private val velocityTracker: VelocityTracker by lazy(LazyThreadSafetyMode.NONE) {VelocityTracker.obtain()}override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {return super.dispatchTouchEvent(ev)}override fun onTouchEvent(event: MotionEvent?): Boolean {if (animator.isRunning) return truelogDebug { "onTouchEvent, event action = ${event?.action}" }velocityTracker.addMovement(event)when (event?.action) {MotionEvent.ACTION_DOWN -> {logDebug { "onTouchEvent-ACTION_DOWN, action = ACTION_DOWN" }startX = event.rawX}MotionEvent.ACTION_MOVE -> {logDebug { "onTouchEvent-ACTION_MOVE, action = ACTION_MOVE" }// 滑动不能超过屏幕的宽度val delta = event.rawX - startXlogDebug { "onTouchEvent-ACTION_MOVE, isCleared = $isCleared" }if (isCleared) {// 处于清屏状态, 准备取消清屏if (delta < 0 && abs(delta) <= screenWidth) {val tempDelta = delta + screenWidthexecuteSlideWithFinger(tempDelta)} else {logDebug { "onTouchEvent-ACTION_MOVE, already in cleared, can't left" }}} else {// 准备去清屏if (delta > 0 && delta <= screenWidth) {executeSlideWithFinger(delta)} else {logDebug { "onTouchEvent-ACTION_MOVE, can't right" }}}}MotionEvent.ACTION_UP -> {logDebug { "onTouchEvent-ACTION_UP, action = ACTION_UP" }// 1秒被滑过多少个像素velocityTracker.computeCurrentVelocity(100)speedX = velocityTracker.xVelocitylogDebug { "onTouchEvent-ACTION_UP, 1秒内划过 x = $speedX, y = ${velocityTracker.yVelocity}" }// 设置了 maxVelocity 最大就显示成这个值// velocityTracker.computeCurrentVelocity(1000, 150F)endX = event.rawXdelta = endX - startXlogDebug { "onTouchEvent-ACTION_UP, startX = $startX, endX = $endX, delta = $delta" }// 处于清屏状态, 不能左滑if (isCleared && delta > 0) return super.onTouchEvent(event)// 非清屏状态, 不能右滑if (!isCleared && delta < 0) return super.onTouchEvent(event)/*** duration = left / width * duration** delta > 0 && isClear = false -> 当前是非清屏状态, 右滑*  1. delta >= MIN_DISTANCE, 滑动有效, 去清屏 --> 右滑, 距离: width - delta*  2. delta < MIN_DISTANCE, 滑动无效, 还原非清屏状态 --> 左滑, 距离: delta** delta < 0 && isClear = true -> 当前是清醒状态, 左滑*  1. abs(delta) >= MIN_DISTANCE, 滑动有效, 还原非清屏状态 --> 左滑, 距离: width - abs(delta)*  2. abs(delta) < MIN_DISTANCE, 滑动无效, 仍是清屏状态 --> 右滑, 距离: abs(delta)*/if (isValidAction()) {leftDistance = screenWidth - abs(delta)leftDuration = (leftDistance * ANIMATOR_DURATION / screenWidth).toLong()logDebug { "onTouchEvent-ACTION_UP - valid, leftDistance = $leftDistance, leftDuration = $leftDuration" }animator.duration = leftDuration} else {leftDistance = abs(delta)leftDuration = (leftDistance * ANIMATOR_DURATION / screenWidth).toLong()logDebug { "onTouchEvent-ACTION_UP - invalid UP, leftDistance = $leftDistance, leftDuration = $leftDuration" }animator.duration = leftDuration}animator.start()}MotionEvent.ACTION_CANCEL -> {logDebug { "onTouchEvent-ACTION_CANCEL" }velocityTracker.recycle()}}return true}// 跟随手指滑动private fun executeSlideWithFinger(delta: Float) {logDebug { "slideWithFinger, delta = $delta" }forEachIndexed { index, view ->if (whitelist.contains(view.id)) {logDebug { "slideWithFinger, whitelist contains ${view.id}" }return@forEachIndexed}view.translationX = delta}}private fun isValidAction() = speedX > MIN_SPEED || abs(delta) >= MIN_DISTANCEprivate fun resetResources() {logDebug { "resetResources" }delta = 0FspeedX = 0FstartX = 0FendX = 0FleftDistance = 0FleftDuration = 0L}override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {return super.onInterceptTouchEvent(ev)}override fun onDetachedFromWindow() {super.onDetachedFromWindow()velocityTracker.recycle()animator.cancel()animator.removeAllUpdateListeners()animator.removeAllListeners()}override val logTag = "LiveRoomRootView"
}

Android 清屏功能实现相关推荐

  1. java调用cmd清屏_Java | 使用JNA在Java中实现cls(cmd清屏)功能

    前言 本文将通过示例代码讲解如何在Java中通过使用JNA来调用cls命令行清屏功能,代码已同步到GitHub,此外本文不是专门讲解JNA的使用的,如需学习可到GitHub查看官网示例,下面我就开始通 ...

  2. python如何设置清屏功能_Python的IDEL增加清屏功能实例

    为idle增加一个清屏的扩展ClearWindow就可以了(在http://bugs.python.org/issue6143中可以看到这个扩展的说明). 下面我说安装使用的方法.首先下载clearw ...

  3. Android分屏功能,适配及相关问题

    Android Developers-开发-API指南-用户界面-多窗口支持: https://developer.android.google.cn/guide/topics/ui/multi-wi ...

  4. c语言菜单怎么清空,你好,请教个C语言菜单实现清屏功能。就是用switch里面输入一个数子,就实现部分清屏或者全清屏...

    在标准的C语言中,没有所谓的清屏函数的,特别是部分清屏 C语言它只是一个标准,只有标准的输入/输出函数 而你要的菜单/清屏功能,必须调用系统的函数才能实现(不同系统用户界面设计方式是不同的) 如在wi ...

  5. android录屏功能

    思路 android实现录屏功能有两种方案,一种是直接使用android自带的MediaProjectionManager实现录屏功能,第二种是是只录语音,用户的操作通过某种方式进行记录保存,最后通过 ...

  6. Java实现清屏功能

    一.cmd命令行下实现清贫 1.编写一个类ClearScreen.java,代码如下: public class ClearScreen{ //实现清屏方法的接口 public native stat ...

  7. java break 在if 中使用_Java | 使用JNA在Java中实现cls(cmd清屏)功能

    作者:庄周de蝴蝶 出自:掘金 原文:juejin.im/post/6876454367332368392 编写 dll文件 由于本文不是专门讲解如何编写dll文件的,而且我也不是一个专业的C家族语言 ...

  8. 类似直播App的向右滑动清屏功能

    声明:这博文涉及的代码块及lib是来自JieGuo这位大神的,本人只是做了很小的修改方便大家理解.感谢感谢!!! 下面是最终的效果图: 布局文件: <?xml version="1.0 ...

  9. Python tkinter库:简易画板(笔、直线、矩形、圆形和橡皮擦工具+导入、保存、撤销、清屏功能)

    Tkinter Tkinter是Python的一款功能强大且全面.操作简易的GUI编程模块.今天,我们将使用tkinter制作一款简易画图软件.加油吧! 最终效果如下: 下载模块 我们将使用Pytho ...

最新文章

  1. 微信公众平台开发,自定义菜单,群发消息,网页授权(3)
  2. Leetcode 回文数字判断
  3. android—获取网络数据
  4. mongdb安装配置
  5. 新骗子举报收录系统源码
  6. 【欢迎大家一起交流讨论】关于Word的自动检测修改--论文格式修正系统(毕业设计)...
  7. Android攻城狮—TextView实现跑马灯
  8. 学习自动驾驶的路径是什么?这份技能图谱告诉你
  9. cad填充图案乱理石_CAD教程:CAD填充图案管理技巧
  10. Java版推箱子(搬箱子)游戏开发入门示例及源码
  11. Leetcode 20有效的括号、33搜索旋转排序数组、88合并两个有序数组(nums1长度为m+n)、160相交链表、54螺旋矩阵、415字符相加(不能直接转Int)、reverse()函数
  12. 华为防火墙IPSec虚拟环境配置
  13. Redis中的TTL
  14. 新建文本文档出现错误怎么办
  15. Java 时间相关 获取某月的某一天
  16. Go学习笔记 -- 异常处理
  17. gitlab代码管理
  18. EMA(指数移动平均)及其深度学习应用
  19. 锐雅中国集团公司怎么样?
  20. 修改Wordpress固定链接导致页面无法无法访问的解决办法

热门文章

  1. matlab求能观测标准型,标准型和规范型.doc
  2. Qt通过setProperty来达到设置控件的不同样式表
  3. 【五六七人口普查】我国省市两级家庭户住房状况
  4. 深度解析自媒体淘客如何做到月入3万
  5. 次氯酸盐溶液增稠剂对稠度的执著已成癖,但似乎并没有坏处
  6. 下载ubuntu系统镜像的国内源地址,超快der!
  7. VOT 2017挑战赛——目标追踪相关分享
  8. K12777 零花钱
  9. 纯QML实现视频播放器
  10. python 字符串去除nbsp等等转义空格换行字符