前几天用了个app发现左滑可以返回首页,发现这个功能很炫酷,就想着自己能不能做出来,于是研究了一下
原理
原理很简单,但实现起来可能有些坑。这里记录一下。源码参考
处理onInterceptTouchEvent
事件拦截要处理一件事情:确定这次触摸事件是不是应该交给SlideFinishLayout的onTouchEvent处理。
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { val action = ev.action when (action){ MotionEvent.ACTION_DOWN -> { mLastX = ev.x.toInt() mIsDrag = false } MotionEvent.ACTION_MOVE -> { mScroller.computeScrollOffset() mIsDrag = !mScroller.isFinished val deltaY:Int = ev.x.toInt() - mLastX if (deltaY >= mTouchSlop){ mIsDrag = true } } } return mIsDrag }
onTouchEvent
这个是核心的实现方法.
这里会用到OverScroller的startScroll()方法来处理手指离开后的动画。OverScroller使用起来非常的简单,如果想让View滚动就调用startScroll()传入相应参数,会把计算的结果回调给View的computeScroll()方法,下面是主要的实现思路:
override fun onTouchEvent(event: MotionEvent): Boolean { val action = event.action //1.初始化轨迹 initVelocityTrackerIfNotExists(event) when(action){ MotionEvent.ACTION_DOWN -> { //2.down 事件 mScroller放弃动画 记录触摸的位置 if (!mScroller.isFinished){ mScroller.abortAnimation() } mLastX = event.rawX.toInt() mIsDrag = true dispatchScroll(0 , 0 , slideState) } MotionEvent.ACTION_MOVE -> { //3.move事件调用performDrag()方法 会调用offsetLeftAndRight() 从而移动View val currentX = event.rawX.toInt() val deltaX = currentX - mLastX log("x = ${event.rawX} y = ${event.rawX}") performDrag(deltaX) log("deltaX = $deltaX") mLastX = currentX } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL ->{ //4.up事件 主要处理手指离开后的View的滚动,以及是否要达到销毁的条件 //endDrag()方法会处理手指离开后的动画以及是否达到销毁条件 if (mIsDrag){ mLastX = 0 mIsDrag = false velocityTracker&#63;.computeCurrentVelocity(1000 , mMaximumVelocity) val velocity = velocityTracker&#63;.xVelocity&#63;.toInt()!! isReachFinish = endDrag(velocity) postInvalidateOnAnimation() recycleVelocityTracker() } } } return true } //开始拖动 private fun performDrag(x:Int){ var canOffset = false isDragLeft = x > 0 slideState = SlideState.DRAGGING //计算 滚动的 距离 if (slideModel == DirectionModel.ONLY_LEFT ){ if (x > 0){ offsetLeftAndRight(x) canOffset = true } }else if (slideModel == DirectionModel.ONLY_RIGHT){ if (x<0){ offsetLeftAndRight(x) canOffset = true } }else{ offsetLeftAndRight(x) canOffset = true } if (canOffset){ dispatchScroll(x , left , slideState) } } //手指离开后的动作 fun endDrag(xVelocity:Int):Boolean{ slideState = SlideState.SETTLING val left = this.left log( "left = $left screenWidth * FACTOR= ${screenWidth * FACTOR}") log( "xVelocity = $xVelocity mMinimumVelocity= $mMinimumVelocity") if (Math.abs(left) > screenWidth * FACTOR || xVelocity > mMinimumVelocity){ if (left>0){ //左滑动 mScroller.startScroll(left , 0 , screenWidth - left , 0) }else{ //右滑动 mScroller.startScroll(left , 0 , left - screenWidth , 0) } return true }else{ mScroller.startScroll(left , 0 , -left , 0) return false } }
怎么使用
activity背景要透明的
设置activity进场和出场动画
R.anim.enter
<&#63;xml version="1.0" encoding="utf-8"&#63;>
R.anim.exit
<&#63;xml version="1.0" encoding="utf-8"&#63;>
activity代码如下
class TwoActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle&#63;) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_two) val index = intent&#63;.getStringExtra("index") nextBtn.setOnClickListener { startActivity(Intent(this@TwoActivity , TwoActivity::class.java)) overridePendingTransition(R.anim.enter, 0) } //滑动达到finish的监听事件,slideFinishLayout没有做任何处理 如果这里不掉用finish也不会退出activity slideFinishLayout.finishListener = { finish() overridePendingTransition(0, 0) } } override fun finish() { super.finish() overridePendingTransition(0, R.anim.exit) } }
存在的问题
堆栈之前的activity被意外销毁了,此时的当前的activity虽然为透明的,但是背景是黑色的,可能是因为被意外销毁还没有来的及调用onCreate,但是打开不保留活动来测试是没有问题的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。