最近刷抖音看视频时,对一个视频某个位置比较感兴趣,采用双指放大查看细节,然后还可以随意滑动到任何位置,比较感兴趣,决定自己来实现此效果;
分析效果: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. Ubuntu 14.04下 Redis MongoDB安装及使用
  2. Linux下的截图工具 KSnapshot
  3. Redis附加功能之键过期功能
  4. 电子计算机工程学,计算机工程
  5. 仿Mathematica中的函数ProductLog
  6. Python线程join和setDaemon
  7. [Android学习笔记]LinearLayout布局,剩余空间的使用
  8. Android内存分析工具:Memory Profiler
  9. java linkedhashset_java之LinkedHashSet
  10. org.eclipse.birt.report.exception.ViewerException: 没有可用的报表设计对象.
  11. debian下erlang新版本安装
  12. CheckBoxList控件绑定数据和设置选定项
  13. dxf制作kml_如何借助GIS工具箱实现dxf转换shp格式
  14. sqlmap安装与使用
  15. Hive建表语句的中文注释乱码问题
  16. [团队项目]英语学习助手之 NABC 模型
  17. 3分钟阿里云无影云桌面全解析(云桌面/传统电脑/VDI/服务器区别对比)
  18. 服务器2012系统登录密码忘记6,Server2012忘记管理员密码的处理方法
  19. 威洛特:你的猫有以下6种情形 ,便知把你当父母还是孩子
  20. 达内python第一次月考题目_第一次月考总结

热门文章

  1. 社会困境问题(social dilemma)
  2. 使用element ui-upload组件上传头像
  3. eclipse alt+←方向 快捷键失效-ikbc F108机械键盘的大坑
  4. STM32编程中,float转u32和u32转float的方法
  5. 01配置tigase使其支持http通讯
  6. 达观OCR文字识别赋能公积金中心实现业务办理再提速
  7. 【独立版】智创云享3107、易搜资料1.4.3、变现宝1.2.4最新版
  8. 文科生报志愿可以学计算机吗,文科生可以报哪些专业 2021高考志愿如何选择专业...
  9. 【杂项】笔记本键盘 “Win+字母“快捷组合键失效 解决过程记录
  10. vue如何创建vnode_【Vue原理】Component - 源码版 之 创建组件VNode