欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android左滑返回功能的实现示例代码

程序员文章站 2022-04-13 22:49:53
前几天用了个app发现左滑可以返回首页,发现这个功能很炫酷,就想着自己能不能做出来,于是研究了一下 原理 将activity的背景设置为透明同时设置切换动画...

前几天用了个app发现左滑可以返回首页,发现这个功能很炫酷,就想着自己能不能做出来,于是研究了一下

原理

  1. 将activity的背景设置为透明同时设置切换动画
  2. 手指滑动的时候,根view跟着滑动,滑倒一定的距离就finish掉。

原理很简单,但实现起来可能有些坑。这里记录一下。源码参考

处理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?.computecurrentvelocity(1000 , mmaximumvelocity)
          val velocity = velocitytracker?.xvelocity?.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背景要透明的

  <style name="apptheme.transparent" parent="theme.appcompat.light.noactionbar">
    <item name="android:windowbackground">@color/transparent</item>
    <item name="android:windowistranslucent">true</item>
  </style>

设置activity进场和出场动画

r.anim.enter

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

  <translate
    android:fromxdelta="100%p"
    android:toxdelta="0"
    android:duration="200"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"/>
</set>

r.anim.exit

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
  android:shareinterpolator="true"
  >

  <translate
    android:fromxdelta="0"
    android:toxdelta="100%p"
    android:duration="200"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"/>
</set>

activity代码如下

class twoactivity : baseactivity() {

  override fun oncreate(savedinstancestate: bundle?) {
    super.oncreate(savedinstancestate)
    setcontentview(r.layout.activity_two)

    val index = intent?.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,但是打开不保留活动来测试是没有问题的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。