最近刷抖音看视频时,对一个视频某个位置比较感兴趣,采用双指放大查看细节,然后还可以随意滑动到任何位置,比较感兴趣,决定自己来实现此效果;
分析效果:ViewPager左右滑动,视频列表上下滑动+下拉刷新,双指进行缩放操作计算移动坐标来平移view,双指到单指也可以进行平移
问题评估:viewpager左右滑动和列表左右滑动冲突问题,单指滑动滑出边界和下拉刷新控件手势冲突;
首先双指缩放和单指平移我想到的是,使用ScaleGestureDetector和GestureDetector,这两个一个是监听双指,一个是单指;看下了ScaleGestureDetector.OnScaleGestureListener的三个重写方法:
onScale
onScaleBegin
onScaleEnd

双指放下,走的依次是onScaleBegin->onScale->onScaleEnd,只有ScaleBegin返回true时才会调用onScale和onScaleEnd,发现只要双指抬起来走的是onScaleEnd,因为我需要监听up事件才要还原View,发现不太适用;
于是决定换一种思路,是不是可以在双指事件拿到之后对一个弹窗处理呢?如果只有单指Action_down事件,就把事件交给下拉刷新控件处理,双指Action_pointer_down时,弹出弹窗,对弹窗进行缩放,平移也对弹窗进行处理,简单点说就是一个dialog遮罩在页面上,根据双指单指控制展示还是隐藏;

单指:ACTION_DOWN->ACTION_MOVE->ACTION_UP;
多指:ACTION_DOWN->ACTION_POINTER_DOWN->ACTION_MOVE->ACTION_POINTER_UP->ACTION_UP

一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。当触摸事件被拦截时,Up可能是0个。View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。

自定义控件首先要了解,一种是继承ViewGroup,一种是继承View;
dispatchTouchEvent

onInterceptTouchEvent
onTouchEvent

这是我自定义缩放View,获取到手势之后,根据双指、单指事件计算坐标
/*** 可缩放的Layout*/
class TouchToScaleLayout(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {// 缩放view的初始left/topprivate var originalXY: IntArray? = null// 触摸时 双指中间的点 / 双指距离private var originalTwoPointerCenter: Point? = nullprivate var originalDistance: Int = 0// 移动时 双指距离 缩放比例private var moveDistance: Int = 0private var moveScale: Float = 0.0f;// 双指移动距离的增量比(用于计算缩放比、背景颜色)private var moveDistanceIncrement: Float = 0.0f// 缩放的Viewprivate var scaleableView: View? = null// 缩放的View原LayoutParamsprivate var viewLayoutParams: ViewGroup.LayoutParams? = null// 缩放的View,在dialog中的LayoutParamsprivate var dialogFrameLayoutParams: FrameLayout.LayoutParams? = null// 用于缩放的dialogprivate var dialog: ScaleDialog? = null// 缩放的动画状态private var isDismissAnimating: Boolean = false//监听回调private var mListener: OnVideoZoomListener? = nullprivate var mOneOrTwoListener: OnVideoZoomOneOrTwoPointListener? = nullfun setZoomListener(listener: OnVideoZoomListener) {mListener = listener}fun setOneOrTwoPointListener(listener:OnVideoZoomOneOrTwoPointListener){mOneOrTwoListener=listener}override fun onTouchEvent(ev: MotionEvent?): Boolean {//后续事件将可以继续传递给该view的onTouchEvent()处理//不想上传递return true}override fun dispatchTouchEvent(ev: MotionEvent): Boolean {if (childCount == 0 && scaleableView == null) return super.dispatchTouchEvent(ev)when (ev.actionMasked) {MotionEvent.ACTION_DOWN -> {Log.e("--------", "ACTION_DOWN")if (mOneOrTwoListener!=null){mOneOrTwoListener!!.onOnePoint()}//交与系统处理return super.dispatchTouchEvent(ev)}MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {//抬起直接复原下拉上拉if (mOneOrTwoListener!=null){mOneOrTwoListener!!.onOnePoint()}if (ev.action == MotionEvent.ACTION_UP) {Log.e("--------", "ACTION_UP")}if (ev.action == MotionEvent.ACTION_CANCEL) {Log.e("--------", "ACTION_CANCEL")}//自己处理requestDisallowInterceptTouchEvent(true)if (scaleableView != null) {if (!isDismissAnimating) {dismissWithAnimator()}return true}}MotionEvent.ACTION_POINTER_DOWN -> {if (mOneOrTwoListener!=null){mOneOrTwoListener!!.onTwoPoint()}Log.e("--------", "ACTION_POINTER_DOWN")//自己处理requestDisallowInterceptTouchEvent(true)if (scaleableView == null && childCount > 0) {scaleableView = getChildAt(0)originalXY = IntArray(2)scaleableView?.getLocationOnScreen(originalXY)dialog = ScaleDialog(context)dialog?.show()viewLayoutParams = scaleableView!!.layoutParamsdialogFrameLayoutParams =LayoutParams(scaleableView!!.width, scaleableView!!.height).apply {leftMargin = originalXY!![0]topMargin = originalXY!![1]}postDelayed({if (scaleableView != null && scaleableView?.parent == this && !isDismissAnimating) {removeView(scaleableView)dialog?.addView(scaleableView!!, dialogFrameLayoutParams)}}, 80)}originalDistance = getDistance(ev)if (originalTwoPointerCenter == null) {originalTwoPointerCenter = Point()}originalTwoPointerCenter?.x = getTwoPointerCenterX(ev)originalTwoPointerCenter?.y = getTwoPointerCenterY(ev)return true}MotionEvent.ACTION_MOVE -> {Log.e("--------", "ACTION_MOVE")if (scaleableView != null && scaleableView?.parent != this) {if (ev.pointerCount == 2) {if (mOneOrTwoListener!=null){mOneOrTwoListener!!.onTwoPoint()}// 双指距离和距离比例moveDistance = getDistance(ev)moveDistanceIncrement =(moveDistance.toFloat() - originalDistance.toFloat()) / originalDistance.toFloat()// 关键点:// 1.设置pivotX和pivotY为view左上角,相比View中心点更容易计算缩放后的位移// 2.位移计算公式 (触摸屏幕时的坐标 * 缩放比 = 缩放后的坐标,当前两指中心点 - 缩放后的坐标 + 触摸屏幕时的leftMargin和topMargin = left和top最终需要的位移)//   leftMargin = 当前两指中心点的x坐标 - 首次触摸屏幕时两指中心点的x坐标 乘以 缩放比 + 首次触摸时的原始leftMargin//   topMargin同上,将x换成y// 缩放moveScale = 1 + moveDistanceIncrementmoveScale = max(0.5f, moveScale)moveScale = min(3.0f, moveScale)if (moveScale < 1) {if (mListener != null) {mListener!!.onScaleEnd(false)}} else if (moveScale > 1) {//手指按下直接设置展示if (mListener != null) {mListener!!.onScaleEnd(true)}}scaleableView?.run {pivotX = 0fpivotY = 0fscaleX = moveScalescaleY = moveScale}// 位移if (originalTwoPointerCenter != null && originalXY != null) {updateOffset((getTwoPointerCenterX(ev) - originalTwoPointerCenter!!.x * moveScale) + originalXY!![0].toFloat(),(getTwoPointerCenterY(ev) - originalTwoPointerCenter!!.y * moveScale) + originalXY!![1].toFloat())}// 透明背景dialog?.setShadowAlpha(max(min(0.8f, moveDistanceIncrement / 1.5f), 0f))return true} else if (ev.pointerCount == 1) { //单指移动updateOffset(getOnePointerCenterX(ev) - getOnePointerCenterX(ev) * moveScale,getOnePointerCenterY(ev) - getOnePointerCenterY(ev) * moveScale)// 透明背景dialog?.setShadowAlpha(max(min(0.8f, moveDistanceIncrement / 1.5f), 0f))return true}}}}//事件继续向下分发return super.dispatchTouchEvent(ev)}...
}

如果你使用的有下拉刷新,注意把下拉、上拉进行动态控制,否则在滑动中会系统通知ACTION_CANCEL;

仿抖音视频双指缩放和单指滑动效果相关推荐

  1. html5仿抖音全屏播放,仿抖音视频全屏播放滑动切换

    1 前言 随着移动技术的快速迭代,数据流量费用的快速下降,视频.直播正成为全民的媒体盛宴,我司必然也不会缺席此次盛宴,这里讲述的是通过h5实现仿抖音视频全屏播放&滑动切换的效果,供我司直播鉴定 ...

  2. Android直播头像动画,iOS 仿抖音直播头像缩放动画

    效果图 仿抖音直播头像缩放效果, 简单写了demo, 思路简单, 直接用的递归重复调用, 呈上所有代码. @interface YCXHeaderZoomViewController () @prop ...

  3. Android 使用ViewPager2+ExoPlayer+VideoCache 实现仿抖音视频翻页播放

    1. 实现效果 效果图中,视频没有铺满 是因为使用了ExoPlayer的RESIZE_MODE_FIT模式, 虽然使用RESIZE_MODE_FILL模式可以填充整个父布局,但是本Demo中使用的视频 ...

  4. html5仿抖音切换效果,仿抖音视频滑动效果

    更新记录 1.6.2(2020-06-04) 优化css3动画效果 1.6.1(2020-05-23) 1.修复串音 2.新增进度条 3.新增弹幕 查看更多 scroll-video uniapp仿抖 ...

  5. 仿抖音视频自动播放html,vue 仿抖音视频列表(兼容微信内置X5浏览器)

    制作 仿抖音视频列表遇到很多坑,特别是安卓微信内置浏览器,让人脑壳疼,核心代码不多 便于理解 组件用到了vant 中的swiper滑动组件 h5 原生 video 属性 webkit-playsinl ...

  6. vue仿抖音视频列表(兼容微信内置X5浏览器)

    vue 仿抖音视频列表(兼容微信内置X5浏览器)https://blog.csdn.net/superKM/article/details/87603255制作 仿抖音视频列表遇到很多坑,特别是安卓微 ...

  7. 微信小程序仿抖音视频

    微信小程序仿抖音视频 使用轮播图实现视频滑动效果. wxml 部分 <view class="video-contain"><!-- 自定义头部 -->&l ...

  8. android图片双指缩放,Android图片双指缩放,单指移动实现

    Android 实现,图片双指缩放,单指移动.通过自定义ImageView控件实现. ZoomDragImageView.java代码如下: public class SwZoomDragImageV ...

  9. Android 仿抖音视频播放列表和评论列表

    Android 汇集CSDN.GitHub等最实用的良心之作-KING Android最实用的各种技能点的网址链接(每天都会更新,希望大家用的上) Android 仿抖音系列之视频播放列表和评论列表 ...

最新文章

  1. 数据结构_顺序栈的代码实践
  2. mysql教程联合索引_MySQL中的联合索引学习教程
  3. 混合云如何落地?光环有云携手AWS一触即发
  4. VTK:PolyData之RibbonFilter
  5. ORACLE TEXT DATASTORE PREFERENCE(二)
  6. csrf spring_无状态Spring安全性第1部分:无状态CSRF保护
  7. hive 集成sentry
  8. 大数据之Hive教程
  9. 高质量程序设计指南c++/c语言(14)--函数指针
  10. ORACLE 制定时间 加N月
  11. sql语句有没有怎么优化的空间,这条语句在我这里执行是死机
  12. SPSS遇到缺失值怎么办?删除还是替换?【SPSS 067期】
  13. 编辑实测:迅捷PDF转换器怎么将PDF转换成JPG
  14. python下载大文件mp4_Python 实现视频爬取下载及断点续传优化、异步下载
  15. linux开机禁用vga设备,用vga_switcheroo在Linux下(开启KMS)彻底关闭某一可切换显卡的简单教程...
  16. 《电脑商情报》国内信号最强,有效距离最远的四种无线网卡
  17. 电路分析 极简复习指导、公式推导、常用结论归纳 第十章 含有耦合电感的电路
  18. 【VS开发】error C2220: 警告被视为错误 - 没有生成“object”文件
  19. auto.ja 部落冲突01 找图并点击
  20. jquery--拖拽效果

热门文章

  1. 根据字符串最后一次出现的位置将这个字符串拆分字符串为数组
  2. java直播毕业设计,使用red5,obs,video.js仿bilibili实现一个视频直播网站
  3. Vaadin Web应用开发教程(37):可视化界面编辑插件
  4. swep在C语言中的头文件,C ++ STL中的queue :: swap()
  5. Makefile中的ifeq 多条件使用
  6. android 服务 结束,android – onDestroy被调用但服务没有结束
  7. [可视化]时间线的7种设计方式
  8. React Native真机运行apk安装失败: com.android.ddmlib.InstallException: INSTALL_FAILED_USER_RESTRICTED
  9. python中有哪些颜色_8种颜色中哪一种最准确?
  10. Scrum是敏捷开发中的一种形式,它提供了一系列流程、方法、工具,旨在帮助项目团队保持高效、可持续地交付价值