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

android 右滑返回的示例代码

程序员文章站 2023-12-19 15:28:40
类似于微信的右滑返回,在baseactivity里利用dispatchtouchevent()拦截右滑动作,利用settranslationx()实现动画,在decorvi...

类似于微信的右滑返回,在baseactivity里利用dispatchtouchevent()拦截右滑动作,利用settranslationx()实现动画,在decorview里添加view作为滑动时的左侧阴影。

android 右滑返回的示例代码

渐进步骤:

  • 设置activity背景透明
  • 重写finish()等方法设置activity的跳转动画
  • 重写dispatchtouchevent()拦截 所需要 右滑动作
  • 重写ontouchevent()给根布局设置偏移量
  • 添加滑动时上层activity的左侧阴影
  • 滑动时关联下层activity滑动

注意:步骤中的代码为了不关联到后面的步骤,会与最终的有点不同

背景透明

  <item name="android:windowbackground">@android:color/transparent</item>
  <item name="android:windowistranslucent">true</item>

activity的跳转动画

根据项目需要,重写用到的startactivity(intent intent),startactivityforresult(intent intent, int requestcode),finish()等activity跳转和销毁方法

@override
public void startactivity(intent intent) {
  super.startactivity(intent);
  overridependingtransition(r.anim.slide_right_in, 0);
}

@override
public void startactivityforresult(intent intent, int requestcode) {
  super.startactivityforresult(intent, requestcode);
  overridependingtransition(r.anim.slide_right_in, 0);
}

@override
public void finish() {
  super.finish();
  overridependingtransition(0, r.anim.slide_right_out);
}

//r.anim.slide_right_in
<set xmlns:android="http://schemas.android.com/apk/res/android">
 <translate
   android:duration="300"
   android:fromxdelta="100%"
   android:toxdelta="0"
   android:fromydelta="0"
   android:toydelta="0"/>
</set>

//r.anim.slide_right_out
<set xmlns:android="http://schemas.android.com/apk/res/android">
 <translate
   android:duration="300"
   android:fromxdelta="0"
   android:toxdelta="100%"
   android:fromydelta="0"
   android:toydelta="0" />
</set>

拦截右滑动作

所有的触摸事件通过activity.dispatchtouchevent(motionevent ev)向view分发。
手指在x轴方向右滑动50~100px时,判断是否为(产品经理要)右滑动作

  • 手指落点为全屏幕,x方向滑动距离要比y方向的大一些;
  • 手指落点为左侧边,x方向滑动距离有一些就行
private float downx = 0;
private float downy = 0;
private boolean shouldintercept = false;
private boolean hadjudge = false;

@override
public boolean dispatchtouchevent(motionevent ev) {
  if (shouldintercept) {
    return ontouchevent(ev);
  }
  switch (ev.getaction()) {
    case motionevent.action_down: {
      downx = ev.getrawx();
      downy = ev.getrawy();
      hadjudge = false;
      break;
    }
    case motionevent.action_move: {
      if (hadjudge) break;
      if (ev.getrawx() == downx) break;
      if (ev.getrawx() < downx) {
        //左滑
        hadjudge = true;
        break;
      }
      if (ev.getrawx() - downx >=100){
        //超出判断距离
        hadjudge = true;
        break;
      }
      if (ev.getrawx() - downx > 50) {
        //x轴右滑50~100px
        float rate = (ev.getrawx() - downx) / (math.abs(ev.getrawy() - downy));
        if ((downx < 50 && rate > 0.5f) || rate > 2) {
          shouldintercept = true;
        }
      }
      break;
    }
    case motionevent.action_up: {
      downx =0;
      downy = 0;
      shouldintercept = false;
      hadjudge=false;
      break;
    }
  }
  //activity的默认分发
  if (ev.getaction() == motionevent.action_down) {
    onuserinteraction();
  }
  if (getwindow().superdispatchtouchevent(ev)) {
    return true;
  }
  return true;
}

根布局位移动画

根据手指滑动距离设置根布局偏移距离,用滑动距离和手指抬起时的速度判断是否返回

private view rootview = null;
private float lastx = -1;  
private velocitytracker velocitytracker = null;
private int maxflingvelocity;


@override
public boolean ontouchevent(motionevent event) {
  if (rootview == null) {
    viewgroup rootgroup = (viewgroup) (getwindow().getdecorview());
    rootview = rootgroup.getchildat(0);
  }
  //测量手指抬起时的速度
  if (velocitytracker == null) {
    velocitytracker = velocitytracker.obtain();
    maxflingvelocity = viewconfiguration.get(this).getscaledmaximumflingvelocity();
  }
  velocitytracker.addmovement(event);


  switch (event.getaction()) {
    case motionevent.action_down: {
      lastx = event.getrawx();
      break;
    }
    case motionevent.action_move: {
      if (lastx == -1) {
        lastx = event.getrawx();
        break;
      }
      //根据手指滑动距离设置根布局偏移距离
      rootview.settranslationx(rootview.gettranslationx() + event.getrawx() - lastx);
      if (rootview.gettranslationx() < 0) rootview.settranslationx(0);
      lastx = event.getrawx();
      break;
    }
    case motionevent.action_up: {
      //测量手指抬起时的速度
      velocitytracker.computecurrentvelocity(1000, maxflingvelocity);
      float velocityx = velocitytracker.getxvelocity();
      if (velocitytracker != null) {
        velocitytracker.recycle();
        velocitytracker = null;
      }

      //判断是否返回
      if (downx < 50 && velocityx > 1000) {
        //手指在左侧边落下,返回
        onback();
      } else if (velocityx > 3600) {
        //手指快速滑动,返回
        onback();
      } else if (rootview.gettranslationx() > convertutil.getwidthinpx() * 0.3) {
        //滑动距离超过30%屏幕宽度,返回
        onback();
      } else {
        //不返回,根布局偏移归零
        rootview.animate().translationx(0).setduration(200).start();
      }
      
      lastx = -1;
      shouldintercept = false;
      hadjudge=false;
      downx = 0;
      downy = 0;
      break;
    }
  }
  return super.ontouchevent(event);
}

添加左侧阴影

activity的最顶层view为decorview,decorview是一个framelayout,里面只有一个linearlayout,linearlayout包含着标题栏和自定义布局(setcontentview)。
上一步跟随手指滑动进行偏移的就是linearlayout,现在要在decorview里添加一个view,设置背景作为阴影,并跟随linearlayout进行移动

private view shadowview = null;
@override
public boolean ontouchevent(motionevent event) {
  if (rootview == null) {
    //添加阴影
    
    viewgroup rootgroup = (viewgroup) (getwindow().getdecorview());

    shadowview = new view(this);
    rootgroup.addview(shadowview, 0);
    viewgroup.layoutparams params = shadowview.getlayoutparams();
    //阴影宽度
    params.width = (int) ((float) convertutil.getwidthinpx() * 0.05f);
    params.height = convertutil.getheightinpx();
    shadowview.setlayoutparams(params);
    shadowview.setbackgroundresource(r.drawable.shadow_grey_h);
    shadowview.settranslationx(params.width);

    rootview = rootgroup.getchildat(1);
  }

  ...

  switch (event.getaction()) {
    ...
    case motionevent.action_move: {
      if (lastx == -1) {
        lastx = event.getrawx();
        break;
      }
      //根据手指滑动距离设置根布局偏移距离
      rootview.settranslationx(rootview.gettranslationx() + event.getrawx() - lastx);
      if (rootview.gettranslationx() < 0) rootview.settranslationx(0);
      //阴影跟随根布局移动
      shadowview.settranslationx(-shadowview.getwidth()+rootview.gettranslationx());
      lastx = event.getrawx();
      break;
    }
    case motionevent.action_up: {
      ...
      } else {
        //不返回,根布局偏移归零
        rootview.animate().translationx(0).setduration(200).start();
        //阴影偏移归零
        shadowview.animate().translationx(-shadowview.getwidth()).setduration(200).start();
      }
      ...
    }
  }
  ...
}

关联下层activity滑动

  • 保存所有的activity以获取下层activity
  • 给下层activity添加退出和进入的动画
  • 在上层activity滑动时调用下层滑动

获取下层activity

private static arraylist<activity> activity_stack = new arraylist<>();
private baseswipebackactivity lastactivity = null;

@override
protected void oncreate(@nullable bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  maxflingvelocity = viewconfiguration.get(this).getscaledmaximumflingvelocity();

  if (!activity_stack.contains(this)) activity_stack.add(this);
  if (activity_stack.size() >= 2) {
    activity last = activity_stack.get(activity_stack.size() - 2);
    if (last instanceof baseswipebackactivity) {
      lastactivity = (baseswipebackactivity) last;
    }
  }
}

@override
protected void ondestroy() {
  super.ondestroy();
  activity_stack.remove(this);
}

下层activity的退出、进入动画

private void loweractivityexitanim() {
  if (rootview == null) return;
  //只移动30%
  rootview.animate().translationx(-convertutil.getwidthinpx() * 0.3f).setduration(300).start();
}

private void loweractivityenteranim(float uppertranslationx) {
  if (rootview == null) return;
  //保证滑动退出时,上下层时间同步
  float r = 1-uppertranslationx/ (float) convertutil.getwidthinpx();
  rootview.animate().translationx(0).setduration((long) (300f * r)).start();
}

在跳转时,调用下层activity的退出、进入动画

@override
public void startactivity(intent intent) {
  super.startactivity(intent);
  overridependingtransition(r.anim.slide_right_in, 0);
  loweractivityexitanim();
}

@override
public void startactivityforresult(intent intent, int requestcode) {
  super.startactivityforresult(intent, requestcode);
  overridependingtransition(r.anim.slide_right_in, 0);
  loweractivityexitanim();
}

@override
public void finish() {
  super.finish();
  overridependingtransition(0, r.anim.slide_right_out);
  if (lastactivity != null) lastactivity.loweractivityenteranim(rootview.gettranslationx());
  activity_stack.remove(this);
}

上层activity滑动时关联下层滑动

@override
public boolean ontouchevent(motionevent event) {
  ...
  switch (event.getaction()) {
    ...
    case motionevent.action_move: {
      ...
      //根据手指滑动距离设置根布局偏移距离
      rootview.settranslationx(rootview.gettranslationx() + event.getrawx() - lastx);
      if (rootview.gettranslationx() < 0) rootview.settranslationx(0);
      //阴影跟随根布局移动
      shadowview.settranslationx(-shadowview.getwidth() + rootview.gettranslationx());
      //下层activity跟随移动
      if (lastactivity != null && lastactivity.rootview != null)
        //-convertutil.getwidthinpx() * 0.3f初始的偏移
        lastactivity.rootview.settranslationx(-convertutil.getwidthinpx() * 0.3f + rootview.gettranslationx() * 0.3f);
      ...
    }
    case motionevent.action_up: {
      ...
      } else {
        //不返回,根布局偏移归零
        rootview.animate().translationx(0).setduration(200).start();
        //阴影偏移归零
        shadowview.animate().translationx(-shadowview.getwidth()).setduration(200).start();
        //下层activity偏移复原
        if (lastactivity != null)
          lastactivity.loweractivityexitanim();
      }
      ...
    }
  }

  return super.ontouchevent(event);
}

完整的

public abstract class baseswipebackactivity extends appcompatactivity {

  private static arraylist<activity> activity_stack = new arraylist<>();
  private baseswipebackactivity lastactivity = null;

  private view rootview = null;
  private view shadowview = null;

  private float downx = 0;
  private float downy = 0;
  private boolean shouldintercept = false;
  private boolean hadjudge = false;

  private float lastx = -1;
  private velocitytracker velocitytracker = null;
  private int maxflingvelocity;

  @override
  protected void oncreate(@nullable bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    maxflingvelocity = viewconfiguration.get(this).getscaledmaximumflingvelocity();

    if (!activity_stack.contains(this)) activity_stack.add(this);
    if (activity_stack.size() >= 2) {
      activity last = activity_stack.get(activity_stack.size() - 2);
      if (last instanceof baseswipebackactivity) {
        lastactivity = (baseswipebackactivity) last;
      }
    }
  }

  @override
  protected void onresume() {
    initshadow();
    super.onresume();
  }

  @override
  protected void ondestroy() {
    super.ondestroy();
    activity_stack.remove(this);
  }

  @override
  public void startactivity(intent intent) {
    super.startactivity(intent);
    overridependingtransition(r.anim.slide_right_in, 0);
    loweractivityexitanim();
  }

  @override
  public void startactivityforresult(intent intent, int requestcode) {
    super.startactivityforresult(intent, requestcode);
    overridependingtransition(r.anim.slide_right_in, 0);
    loweractivityexitanim();
  }

  @override
  public void finish() {
    super.finish();
    overridependingtransition(0, r.anim.slide_right_out);
    if (lastactivity != null) lastactivity.loweractivityenteranim(rootview.gettranslationx());
    activity_stack.remove(this);
  }

  private void initshadow() {
    if (shadowview == null) {
      viewgroup rootgroup = (viewgroup) (getwindow().getdecorview());

      shadowview = new view(this);
      rootgroup.addview(shadowview, 0);
      viewgroup.layoutparams params = shadowview.getlayoutparams();
      //阴影宽度
      params.width = (int) ((float) convertutil.getwidthinpx() * 0.05f);
      params.height = convertutil.getheightinpx();
      shadowview.setlayoutparams(params);
      //渐变背景作为阴影
      shadowview.setbackgroundresource(r.drawable.shadow_grey_h);
      shadowview.settranslationx(-params.width);

      rootview = rootgroup.getchildat(1);
    }
  }

  @override
  public boolean dispatchtouchevent(motionevent ev) {
    if (shouldintercept) {
      return ontouchevent(ev);
    }
    switch (ev.getaction()) {
      case motionevent.action_down: {
        downx = ev.getrawx();
        downy = ev.getrawy();
        hadjudge = false;
        break;
      }
      case motionevent.action_move: {
        if (hadjudge) break;
        if (ev.getrawx() == downx) break;
        if (ev.getrawx() < downx) {
          //左滑
          hadjudge = true;
          break;
        }
        if (ev.getrawx() - downx >= 100) {
          //超出判断距离
          hadjudge = true;
          break;
        }
        if (ev.getrawx() - downx > 50) {
          //x轴右滑50~100px
          float rate = (ev.getrawx() - downx) / (math.abs(ev.getrawy() - downy));
          if ((downx < 50 && rate > 0.5f) || rate > 2) {
            shouldintercept = true;
          }
        }
        break;
      }
      case motionevent.action_up: {
        downx = 0;
        downy = 0;
        shouldintercept = false;
        hadjudge = false;
        break;
      }
    }
    //activity的默认分发
    if (ev.getaction() == motionevent.action_down) {
      onuserinteraction();
    }
    if (getwindow().superdispatchtouchevent(ev)) {
      return true;
    }
    return true;
  }

  @override
  public boolean ontouchevent(motionevent event) {
    initshadow();

    //测量手指抬起时的速度
    if (velocitytracker == null) {
      velocitytracker = velocitytracker.obtain();
      maxflingvelocity = viewconfiguration.get(this).getscaledmaximumflingvelocity();
    }
    velocitytracker.addmovement(event);


    switch (event.getaction()) {
      case motionevent.action_down: {
        lastx = event.getrawx();
        break;
      }
      case motionevent.action_move: {
        if (lastx == -1) {
          lastx = event.getrawx();
          break;
        }
        //根据手指滑动距离设置根布局偏移距离
        rootview.settranslationx(rootview.gettranslationx() + event.getrawx() - lastx);
        if (rootview.gettranslationx() < 0) rootview.settranslationx(0);
        //阴影跟随根布局移动
        shadowview.settranslationx(-shadowview.getwidth() + rootview.gettranslationx());
        //下层activity跟随移动
        if (lastactivity != null && lastactivity.rootview != null)
          lastactivity.rootview.settranslationx(-convertutil.getwidthinpx() * 0.3f + rootview.gettranslationx() * 0.3f);

        lastx = event.getrawx();
        break;
      }
      case motionevent.action_up: {
        //测量手指抬起时的速度
        velocitytracker.computecurrentvelocity(1000, maxflingvelocity);
        float velocityx = velocitytracker.getxvelocity();
        if (velocitytracker != null) {
          velocitytracker.recycle();
          velocitytracker = null;
        }

        //判断是否返回
        if (downx < 50 && velocityx > 1000) {
          //手指在左侧边落下,返回
          onback();
        } else if (velocityx > 3600) {
          //手指快速滑动,返回
          onback();
        } else if (rootview.gettranslationx() > convertutil.getwidthinpx() * 0.3) {
          //滑动距离超过30%屏幕宽度,返回
          onback();
        } else {
          //不返回,根布局偏移归零
          rootview.animate().translationx(0).setduration(200).start();
          //阴影偏移归零
          shadowview.animate().translationx(-shadowview.getwidth()).setduration(200).start();
          //下层activity偏移复原
          if (lastactivity != null) lastactivity.loweractivityexitanim();
        }

        lastx = -1;
        shouldintercept = false;
        hadjudge = false;
        downx = 0;
        downy = 0;
        break;
      }
    }

    return super.ontouchevent(event);
  }
  

  private void loweractivityexitanim() {
    if (rootview == null) return;
    rootview.animate().translationx(-convertutil.getwidthinpx() * 0.3f).setduration(300).start();
  }

  private void loweractivityenteranim(float uppertranslationx) {
    if (rootview == null) return;
    float r = 1-uppertranslationx/ (float) convertutil.getwidthinpx();
    rootview.animate().translationx(0).setduration(r == 0.0f ? 10 : (long) (300f * r)).start();
  }

  //退出
  abstract public void onback();
}

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

上一篇:

下一篇: