热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android实现微信侧滑关闭页面效果

这篇文章主要为大家详细介绍了Android实现微信侧滑关闭页面效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

最近手机升级了5.0系统后,突然间发现微信竟然有IOS一样的侧滑关闭当前页面的效果,就想把这种效果也加进自己的项目里面。本着不重复造轮子的原则,就在网上百度了很久,发现大多数人都是采用自定义View来实现,但是对于我这种已经基本完成的项目来说,如果全部的Activity再重新使用自定义的View无疑是一种可怕的噩梦。

因此,我这里实现了另外一种不需要自定义View也能实现的方法,其子类只要继承于它,便能拥有其侧滑滑动的功能。
随便说一句,此方法仅对5.0以上的手机有效(反正微信也是5.0上才能用),5.0以下的请无视!!!

原理

在每个Activity里面都有一个底层的View,也就是所谓的rootView,当我们加载一个xml布局时,系统就会自动给你生成这个rootView,由于它是一个View,那么也就意味着你可以通过一定的代码随意移动这个根布局。
如下代码所示,只要简单的几行代码便能实现布局的移动。

public class SlideActivity extends AppCompatActivity {
  View mRootView;
  private GestureDetector mDetector;
  private int mWindowWidth;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_demo);
    mRootView = getWindow().getDecorView();
    mRootView.setBackgroundColor(Color.BLUE);
    mDetector = new GestureDetector(this, new GestureListener());
    mWindowWidth = getWindow().getWindowManager().getDefaultDisplay().getWidth();
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    return mDetector.onTouchEvent(event);
  }

  /**
   * 手势监听
   */
  private class GestureListener extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      if (e1 != null) {
        handlerCurrentActivityScroll(e2);
      }
      return super.onScroll(e1, e2, distanceX, distanceY);
    }

    /**
     * 处理当前页面滑动
     */
    private void handlerCurrentActivityScroll(MotionEvent e2) {
      mRootView.setTranslationX(e2.getX());
      if (e2.getX() > mWindowWidth - 20) {
        finish();
      }
    }
  }
}

这是我们的效果

Activity联动

这差距还是很大的,最明显的地方是,我们移动 的时候,上一层Activity竟然没有跟着联动。

解决这个问题的方法也简单,如图所示,每当启动一个Activity时,系统都会把Activity放到一个栈里面,由于栈的工作原理可知,APP里面的Activity是一层覆盖一层的,就如上图所示。为此,每当启动一个Activity时,就可以把当前的Actiivty存储到一个List里面,这样,我们就可以在当前的Activity里面取出上一个Activity进行操作。

因此,在进入一个新的Activity的时候,在其onCreate方法里面把当前的Activity加载到列表里,当退出时,在finish的重载方法里面,将当前Activity从列表里面移除。

注意!!!在滑动的时候必须需要考虑到Activity里面有可能会有类似于ListView一类的滑动控件,因此,我们必须对事件进行分发控制。

代码如下

/**
 * Created by yuyu on 2015/10/29.
 */
public class TestActivity extends AppCompatActivity {
  View mRootView;
  private GestureDetector mGestureDetector;
  private static List mActivitys = new ArrayList<>();
  /**
   * 移动距离
   */
  private float mWindowWidth;
  private TestActivity mBeforeActivity;
  /**
   * 上一个Activity偏移量
   */
  private float mOffsetX;
  /**
   * 上一个页面移出的位置
   */
  private float mOutsideWidth;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_demo);
    /**
     * 把当前Activity加到列表里面
     */
    mActivitys.add(this);
    initScrollBack();
  }

  /**
   * 初始化左滑退出功能
   */
  private void initScrollBack() {
    mWindowWidth = getWindowManager().getDefaultDisplay().getWidth();
    mOutsideWidth = -mWindowWidth / 4;
    mOffsetX = mOutsideWidth;
    mGestureDetector = new GestureDetector(this, new GestureListener());
    mRootView = getWindow().getDecorView();
    mRootView.setBackgroundColor(Color.BLUE);
  }

  /**
   * 控制分发事件,在这里控制能能触发拖动的范围
   */
  @Override
  public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
    if (ev.getX()  1) {
        mBeforeActivity = mActivitys.get(mActivitys.size() - 2);
        beforeActivityTranslationX(mOutsideWidth);
      }
      return onTouchEvent(ev);
    }
    return super.dispatchTouchEvent(ev);
  }

  @Override
  public void finish() {
    mActivitys.remove(this);
    if (mOffsetX <0.0001 || mOffsetX > 0.0001) {
      beforeActivityTranslationX(0);
    }
    super.finish();
  }

  public void onClick(View view) {
    Intent intent = new Intent(this, Activity5.class);
    startActivity(intent);
  }

  public View getRootView() {
    return mRootView;
  }

  /**
   * 控制上一个Activity移动
   */
  private void beforeActivityTranslationX(float translationX) {
    if (mBeforeActivity != null) {
      mBeforeActivity.getRootView().setTranslationX(translationX);
    }
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    return mGestureDetector.onTouchEvent(event);
  }

  /**
   * 手势监听
   */
  private class GestureListener extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      if (e1 != null) {
        handlerCurrentActivityScroll(e2);
        handleBeforeActivityScroll(e2, distanceX);
      }
      return super.onScroll(e1, e2, distanceX, distanceY);
    }

    /**
     * 处理当前页面滑动
     */
    private void handlerCurrentActivityScroll(MotionEvent e2) {
      mRootView.setTranslationX(e2.getX());
      if (e2.getX() > mWindowWidth - 20) {
        finish();
      }
    }

    /**
     * 处理上一个页面滑动
     */
    private void handleBeforeActivityScroll(MotionEvent e2, float distanceX) {
      if (mBeforeActivity != null) {
        mOffsetX = distanceX <0 &#63; mOffsetX + Math.abs(distanceX) / 4 : mOffsetX - Math.abs(distanceX) / 4;
        if (mOffsetX > 0.0001) {
          mOffsetX = 0f;
        }
        mBeforeActivity.getRootView().setTranslationX(mOffsetX);
      }
    }
  }
}

这是联动后的效果图

现在算是有点效果了,但是和微信的差距还是很大,接下来我们便需要开始处理自动滑动了

自动滑动

这个就不需要多说了,这个主要就是利用属性动画进行移动

以下是完整的代码

public class SlideActivity extends AppCompatActivity {
  private static final String TAG = "SlideActivity";
  private static List mActivitys = new ArrayList<>();
  /**
   * 手势监听
   */
  private GestureDetector mGestureDetector;
  private View mRootView;
  private boolean isScroll = false;
  /**
   * 移动距离
   */
  private float mWindowWidth;
  private SlideActivity mBeforeActivity;
  /**
   * 上一个Activity偏移量
   */
  private float mOffsetX;
  /**
   * 上一个页面移出的位置
   */
  private float mOutsideWidth;
  private boolean canScrollBack = true;
  private boolean canScroll = false;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
    getWindow().setEnterTransition(new Slide(Gravity.RIGHT));
    super.onCreate(savedInstanceState);
    /**
     * 把当前Activity加到列表里面
     */
    mActivitys.add(this);
    initScrollBack();
  }

  @Override
  public void startActivity(Intent intent) {
    startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
  }

  /**
   * 初始化左滑退出功能
   */
  private void initScrollBack() {
    mWindowWidth = getWindowManager().getDefaultDisplay().getWidth();
    mOutsideWidth = -mWindowWidth / 4;
    mOffsetX = mOutsideWidth;
    mGestureDetector = new GestureDetector(this, new GestureListener());
    mRootView = getWindow().getDecorView();

  }

  /**
   * 控制上一个Activity移动
   */
  private void beforeActivityTranslationX(float translationX) {
    if (mBeforeActivity != null) {
      mBeforeActivity.getRootView().setTranslationX(translationX);
    }
  }

  /**
   * 设置是否能滑动
   *
   * @param canScrollBack true 可以滑动
   */
  protected void setCanScrollBack(boolean canScrollBack) {
    this.canScrollBack = canScrollBack;
  }

  public View getRootView() {
    return mRootView;
  }

  @Override
  public void finish() {
    mActivitys.remove(this);
    if (mOffsetX <0.0001 || mOffsetX > 0.0001) {
      beforeActivityTranslationX(0);
    }
    super.finish();
  }

  /**
   * 控制分发事件
   */
  @Override
  public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
    if (canScrollBack && ev.getX()  1) {
        mBeforeActivity = mActivitys.get(mActivitys.size() - 2);
        beforeActivityTranslationX(mOutsideWidth);
      }
      canScroll = true;
      return onTouchEvent(ev);
    }
    return super.dispatchTouchEvent(ev);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (canScrollBack && canScroll) {
      if (event.getAction() == MotionEvent.ACTION_UP && isScroll) {
        isScroll = false;
        canScroll = false;
        //退出当前Activity
        if (event.getX() > mWindowWidth / 2) {
          if (mBeforeActivity != null) {
            ObjectAnimator.ofFloat(mBeforeActivity.getRootView(), "translationX", mOffsetX, 0).setDuration(500).start();
          }
          ObjectAnimator moveIn = ObjectAnimator.ofFloat(mRootView, "translationX", event.getX(), mWindowWidth);
          moveIn.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
              super.onAnimationEnd(animation);
              finish();
            }
          });
          moveIn.setDuration(500).start();
          //反弹回来
        } else if (event.getX()  mWindowWidth - 20) {
        finish();
      }
    }

    /**
     * 处理上一个页面滑动
     */
    private void handleBeforeActivityScroll(MotionEvent e2, float distanceX) {
      if (mBeforeActivity != null) {
        mOffsetX = distanceX <0 &#63; mOffsetX + Math.abs(distanceX) / 4 : mOffsetX - Math.abs(distanceX) / 4;
        if (mOffsetX > 0.0001) {
          mOffsetX = 0f;
        }
        mBeforeActivity.getRootView().setTranslationX(mOffsetX);
      }
    }
  }
}

这是我们最终的效果图

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


推荐阅读
  • 本文讨论了同事工资打听的话题,包括同工不同酬现象、打探工资的途径、为什么打听别人的工资、职业的本质、商业价值与工资的关系,以及如何面对同事工资比自己高的情况和凸显自己的商业价值。故事中的阿巧发现同事的工资比自己高后感到不满,通过与老公、闺蜜交流和搜索相关关键词来寻求解决办法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 2022年的风口:你看不起的行业,真的很挣钱!
    本文介绍了2022年的风口,探讨了一份稳定的副业收入对于普通人增加收入的重要性,以及如何抓住风口来实现赚钱的目标。文章指出,拼命工作并不一定能让人有钱,而是需要顺应时代的方向。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • 手把手教你使用GraphPad Prism和Excel绘制回归分析结果的森林图
    本文介绍了使用GraphPad Prism和Excel绘制回归分析结果的森林图的方法。通过展示森林图,可以更加直观地将回归分析结果可视化。GraphPad Prism是一款专门为医学专业人士设计的绘图软件,同时也兼顾统计分析的功能,操作便捷,可以帮助科研人员轻松绘制出高质量的专业图形。文章以一篇发表在JACC杂志上的研究为例,利用其中的多因素回归分析结果来绘制森林图。通过本文的指导,读者可以学会如何使用GraphPad Prism和Excel绘制回归分析结果的森林图。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 嵌入式处理器的架构与内核发展历程
    本文主要介绍了嵌入式处理器的架构与内核发展历程,包括不同架构的指令集的变化,以及内核的流水线和结构。通过对ARM架构的分析,可以更好地理解嵌入式处理器的架构与内核的关系。 ... [详细]
  • HTML5网页模板怎么加百度统计?
    本文介绍了如何在HTML5网页模板中加入百度统计,并对模板文件、css样式表、js插件库等内容进行了说明。同时还解答了关于HTML5网页模板的使用方法、表单提交、域名和空间的问题,并介绍了如何使用Visual Studio 2010创建HTML5模板。此外,还提到了使用Jquery编写美好的HTML5前端框架模板的方法,以及制作企业HTML5网站模板和支持HTML5的CMS。 ... [详细]
author-avatar
圈圈
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有