当前位置: 移动技术网 > 移动技术>移动开发>Android > android 右滑返回的示例代码

android 右滑返回的示例代码

2019年07月24日  | 移动技术网移动技术  | 我要评论

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

渐进步骤:

  • 设置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();
}

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网