自定义view--轮盘(带触摸旋转事件)--kotlin代码-2
来源:互联网 发布:数据库1对多关系 编辑:程序博客网 时间:2024/06/07 13:02
这一篇在第一篇的基础上增加了惯性滑动和双点触控缩放指针的效果,效果如下:
首先是滚动惯性滑动效果,我使用的是属性动画进行实现的,使用了DecelerateInterpolator这个插值器,它的作用是由快到慢,符合我们的要求,代码如下:
/** * 开启惯性滚动动画 */ private fun startAni() { speed = if (scrollType === 1) speed else (-speed!!) var endPosition = if (nowAgree!!.toInt() > (speed!!).toInt()) nowAgree!!.toInt() + (speed!!).toInt() else (speed!!).toInt() animtor = ValueAnimator.ofInt(nowAgree!!.toInt(), endPosition) animtor?.interpolator = DecelerateInterpolator()//插值器,开始快后面慢// animtor.interpolator = AccelerateDecelerateInterpolator()//开始慢中间快后面慢 animtor?.duration = 1000 + Math.abs(speed!!.toLong()) animtor?.addUpdateListener { animation -> val updateValue = animation.animatedValue as Int nowAgree = (updateValue % 360 + 360) % 360.toFloat() ViewCompat.postInvalidateOnAnimation(this@MyView2) } animtor?.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) } }) animtor?.start() }
相关属性说明如下:
internal var speed: Double? = null//结束时手指滑动速度 internal var tracter: VelocityTracker? = null//速度获取对象 internal var animtor: ValueAnimator? = null//属性动画 internal var scrollType: Int? = 1//动画滚动方向,1正,0反
主要原理是通过获取滑动结束时滑动的像素速度作为动画旋转角度的结束位置值来进行相关的动画旋转操作,在这个过程中的主要难点在于处理滚动方向,所以我写了个算法用于指定滚动方向和滚动时间,以及滚动距离,代码如下:
/** * 获取滚动方向的算法 */ private fun getAgree2(xlength: Float?, ylength: Float?): Float { if (xlength!! < 0 && ylength!! < 0) {//A return Math.toDegrees((Math.atan((Math.abs(ylength) / Math.abs(xlength)).toDouble()))).toFloat() - 180 } else if (xlength > 0 && ylength!! < 0) {//B return -Math.toDegrees((Math.atan((Math.abs(ylength) / Math.abs(xlength)).toDouble()))).toFloat() } else if (xlength < 0 && ylength!! > 0) {//C return 180 - Math.toDegrees((Math.atan((Math.abs(ylength) / Math.abs(xlength)).toDouble()))).toFloat() } else if (xlength > 0 && ylength!! > 0) {//D return Math.toDegrees((Math.atan((Math.abs(ylength) / Math.abs(xlength)).toDouble()))).toFloat() } else return 0F }
算法原理说明如下:
通过触摸结束点获取到结束点与中心的相对于x轴的夹角,通过速度参数获取速度相对于x轴的夹角,然后将整个平面以中心点相对以x,y轴划分成ABCD四个区域,然后根据区域的不同和两个夹角的大小对比进行正负方向的区分,说明图如下:
其中q是触摸结束点,我们可以拿到它与中心点相对x轴线的夹角a,蓝色线条是以角a为基础的相交的垂直线,s是速度矢量,我们可以获取s相对于x轴的夹角角d,通过角a,我们可以得到角b和角c,角e为角d减去角c获得,根据角c知道,如果角c越小,s就与中心点越垂直,所以通过以蓝色线条的坐标为基础可以根据s的角度去区分滚动方向的正反。
相关角度计算代码如下:
/** * 获取速度和滚动方向的算法 */ private fun getSpeedTo(): Int { var speed_x = tracter!!.xVelocity var speed_y = tracter!!.yVelocity var xSpeed = Math.abs(speed_x).toDouble() var ySpeed = Math.abs(speed_y).toDouble() var speedDegrees = Math.toDegrees((Math.atan(Math.abs(speed_y / speed_x).toDouble()))) speed = Math.abs(Math.sqrt(Math.pow(xSpeed, 2.0) + Math.pow(ySpeed, 2.0)) * Math.cos(speedDegrees - 90 + Math.abs(endAgree!!))) var areaDegree = getAgree2(speed_x, speed_y) if (end_x!! <= rectF!!.centerX() && end_y!! <= rectF!!.centerY()) {//A if (areaDegree > endAgree!!) return 1 else return 0 } else if (end_x!! > rectF!!.centerX() && end_y!! <= rectF!!.centerY()) {//B if (areaDegree > endAgree!!) return 1 else return 0 } else if (end_x!! <= rectF!!.centerX() && end_y!! > rectF!!.centerY()) {//C if (((areaDegree in endAgree!!..90F) or (areaDegree in -180F..-180F + endAgree!!)) or (areaDegree > endAgree!!)) return 1 else return 0 } else if (end_x!! > rectF!!.centerX() && end_y!! > rectF!!.centerY()) {//D if (areaDegree > endAgree!!) return 1 else return 0 } else return 1 }
滚动动画说完了,我们现在来看下缩放效果的实现:
相关代码如下:需要注意的是判断相关action的时候要用event!!.action and MotionEvent.ACTION_MASK,不然无法触发多点触控的条件。
override fun onTouchEvent(event: MotionEvent?): Boolean { when (event!!.action and MotionEvent.ACTION_MASK) {//这个地方要加上and MotionEvent.ACTION_MASK这句,不然无法进行多点触控操作 MotionEvent.ACTION_DOWN -> { animtor?.cancel()//重置动画 start_x = event.x start_y = event.y startAgree = getAgree(start_x, start_y)//开始角度 initAgree = nowAgree //用于速度计算 if (tracter === null) tracter = VelocityTracker.obtain() else tracter!!.clear() tracter!!.addMovement(event) } MotionEvent.ACTION_MOVE -> { var count: Int = event.pointerCount if (count >= 2 && nowSpace !== null) {//如果触摸点超过2个就进行缩放触控 actSpace = getSpacing(event) actLength = (actSpace!! - nowSpace!!) / nowSpace!! * radius!! nowRadius = initRadius!! + actLength!! nowRadius = if (nowRadius!! >= 2 * radius!!) 2 * radius!! else nowRadius nowRadius = if (nowRadius!! <= radius!! / 5) radius!! / 5 else nowRadius } else {//如果是单点触控,则进行角度和速度计算 end_x = event.x end_y = event.y endAgree = getAgree(end_x, end_y) actAgree = endAgree!! - startAgree!! nowAgree = (initAgree!! + actAgree!!) % 360 tracter?.addMovement(event) tracter?.computeCurrentVelocity(500) } postInvalidate() } MotionEvent.ACTION_UP -> { initAgree = nowAgree var count: Int = event.pointerCount if (count <= 1) {//如果是单点触控,则进行速度计算 tracter?.computeCurrentVelocity(500) scrollType = getSpeedTo() startAni()//开始惯性动画播放 if (tracter !== null) { tracter!!.recycle() tracter = null } } } MotionEvent.ACTION_POINTER_DOWN -> { var count: Int = event.pointerCount if (count >= 2) { nowSpace = getSpacing(event) } } MotionEvent.ACTION_POINTER_UP -> { initRadius = nowRadius } } return true }
相关缩放位置我只针对了外面那个圆圈,所以代码我也只展示相关位置:
override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) ... canvas?.drawCircle(rectF!!.centerX(), rectF!!.centerY(), nowRadius!!, paint) ... }
相关属性说明如下:
internal var nowSpace: Float? = null//初始两点触控距离 internal var actSpace: Float? = null//滑动之后两点触控距离 internal var actLength: Float? = 0F//两点触控距离变化长度 internal var nowRadius: Float? = 0F//当前半径长度 internal var initRadius: Float? = 0F//初始半径长度
这一篇主要介绍相关原理,如果需要看整体代码,请点击:https://gitee.com/ccdy00/codes/xp0lc63d4jiy8m5nogstr83
- 自定义view--轮盘(带触摸旋转事件)--kotlin代码-2
- 自定义view--轮盘(带触摸旋转事件)--kotlin代码-1
- 自定义View交集触摸事件
- 自定义view(一)View坐标系与触摸事件
- 自定义view走势图(二、加入动画和触摸事件)
- 自定义视图触摸事件free view
- android自定义View(带旋转动画的饼状图)
- 自定义View 带箭头的圆旋转
- 自定义view 圆环带箭头旋转
- 自定义view重写触摸事件-imageview为例
- Android开发-自定义View-AndroidStudio(二十三)onTouchEvent触摸事件
- Cocos2d-x学习之---自定义图标(带触摸事件)
- android(八)、view触摸事件
- view的拖拽(触摸事件)
- ViewGroup View触摸事件
- view的触摸事件
- Android触摸事件--View
- 触摸事件分发-view
- CentOS6.5自带jdk卸载和安装
- test
- Choose and divide UVA
- 常见的几种最优化方法
- Linux下打包压缩war、解压war包和jar命令
- 自定义view--轮盘(带触摸旋转事件)--kotlin代码-2
- win7 访问不了局域网里Linux上的tomcat服务
- VirtualBox虚拟机配置CentOS7网络图文详解教程
- 故障表现:tomcat可以正常启动,但是浏览器显示找不到页面,404。
- 树莓派安装魔镜功能模块
- thinkphp模板如何转换时间格式
- vue.js 教程 思维导图 百度脑图分享 会持续更新
- 做个成功的嵌入式系统工程师
- KMP算法中的next数组的两种算法求解