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

Android中SwipeBack实现右滑返回效果

这篇文章主要介绍了Android中SwipeBack实现右滑返回效果的相关资料,需要的朋友可以参考下

现在有很多App支持右滑返回,比如知乎,效果比较赞。

于是自己对Activity和Fragment进行了继承,派生出SwipeBackActivity和SwipeBackFragment,用于对这种效果的实现,也就是只要继承这两个类就可以了。

效果如下

  • Activity

Fragment

Frgament的效果实现比Activity稍微简单,因为Activity要考虑到dectorView。

支持滑动的控件SwipeLayout,核心思路就是把原有的控件添加到支持滑动的控件中,SwipeLayout要注意计算手势速度,源码如下:

package com.ui.jerry.swipebackdemo;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Scroller;
import android.widget.Toast;


public class SwipeLayout extends LinearLayout {
  public static final String TAG = "SwipeLayout";

  private View mEmptyView;
  private View mContentView;

  private int mLeftEdge;
  private int mWidth;
  private int mMaxScrollX;

  private Scroller mScroller;
  private VelocityTracker mVelocityTracker = null;
  private int mMaxFlingVelocity;
  private int mLastX;

  ViewGroup.LayoutParams childParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

  private Context mContext;
  public static final int DURATION = 1500;  //满屏滑动时间
  public static final int OPEN_ANIM_DURATION = 1000;
  public static int SNAP_VELOCITY = 600; //最小的滑动速率

  private OnFinishListener mOnFinishListener;

  public SwipeLayout(Context context) {
    this(context, null);
  }

  public SwipeLayout(Context context, AttributeSet attrs) {
    super(context, attrs);

    mCOntext= context;
    init();
  }

  public void setOnFinishListener(OnFinishListener mOnFinishListener) {
    this.mOnFinishListener= mOnFinishListener;
  }

  void init() {
    mScroller = new Scroller(mContext);
    mMaxFlingVelocity = ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity();

    mWidth = DisplayUtils.getScreenWidth(mContext) * 2;
    mMaxScrollX = mWidth / 2;
    mLeftEdge = mMaxScrollX - mMaxScrollX / 3;

    setOrientation(LinearLayout.HORIZONTAL);

    childParams.width = DisplayUtils.getScreenWidth(mContext);

    mEmptyView = LayoutInflater.from(mContext).inflate(R.layout.view_translate, null);

    addView(mEmptyView, childParams);
  }

  public void setContentView(View contentView) {
    if (mContentView != null) {
      removeView(mContentView);
    }
    mCOntentView= contentView;
    addView(contentView, childParams);

    postDelayed(new Runnable() {
      @Override
      public void run() {
        openActivityAnimation();
      }
    }, 200);
  }

  /**
   * 获取速度追踪器
   *
   * @return
   */
  private VelocityTracker getVelocityTracker() {
    if (mVelocityTracker == null) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    return mVelocityTracker;
  }

  /**
   * 回收速度追踪器
   */
  private void recycleVelocityTracker() {
    if (mVelocityTracker != null) {
      mVelocityTracker.clear();
      mVelocityTracker.recycle();
      mVelocityTracker = null;
    }
  }


  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    //1.获取速度追踪器
    getVelocityTracker();
    //2.将当前事件纳入到追踪器中
    mVelocityTracker.addMovement(ev);

    int pointId = -1;

    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        //如果屏幕的动画还没结束,你就按下了,我们就结束上一次动画,即开始这次新ACTION_DOWN的动画
//        clearScrollHis();
        mLastX = (int) ev.getX();
        pointId = ev.getPointerId(0);
        break;
      case MotionEvent.ACTION_MOVE:
        int nextScrollX = (int) (mLastX - ev.getX() + getScrollX());

        if (scrollTo(nextScrollX)) {
          mLastX = (int) ev.getX();
        }
        break;
      case MotionEvent.ACTION_CANCEL:
      case MotionEvent.ACTION_UP:
        //3.计算当前速度
        mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
        //获取x y方向上的速度
        float vX = mVelocityTracker.getXVelocity(pointId);

        Log.i(TAG, "mVelocityX:" + vX);

        //大于某个速率 直接滑动
        if (vX > SNAP_VELOCITY) {
          scrollToLeft();
        } else if (vX <-SNAP_VELOCITY) {
          scrollToRight();
        } else {
          snapToDestation();
        }


        //4.回收速度追踪器
        recycleVelocityTracker();
        break;
    }
    return true;
  }

  private void openActivityAnimation() {
    clearScrollHis();
    mScroller.startScroll(getScrollX(), 0, mMaxScrollX - getScrollX(), 0, OPEN_ANIM_DURATION);
    invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
  }

  public void closeActivityAnimation() {
    clearScrollHis();
    mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0, OPEN_ANIM_DURATION);
    invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
  }

  private void clearScrollHis() {
    if (mScroller != null) {
      if (!mScroller.isFinished()) {
        mScroller.abortAnimation();
      }
    }
  }

  /**
   * 根据现在的滚动位置判断
   */
  private void snapToDestation() {
    int scrollX = getScrollX();
    if (scrollX > 0 && scrollX <= mLeftEdge) {
      smoothScrollTo(0);
    } else if (scrollX > mLeftEdge) {
      smoothScrollTo(mMaxScrollX);
    }
  }

  /**
   * 直接滚动
   *
   * @param x
   * @return
   */
  public boolean scrollTo(int x) {
    if (x <0) {
      scrollTo(0, 0);
    } else if (x > mMaxScrollX) {
      scrollTo(mMaxScrollX, 0);
    } else {
      scrollTo(x, 0);
    }
    return true;
  }

  public void scrollToRight() {
    smoothScrollTo(mMaxScrollX);
  }

  public void scrollToLeft() {
    smoothScrollTo(0);
  }

  @Override
  protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);

    Log.d(TAG, "left:" + l);

    if (l == 0) {
      Log.d(TAG, "OnFinish");

      Toast.makeText(mContext, "Finished", Toast.LENGTH_SHORT).show();

      if(mOnFinishListener!=null){
        mOnFinishListener.onFinish();
      }
    }
  }

  public void smoothScrollTo(int fx) {
    if (fx <0) {
      smoothScrollTo(0, 0);
    } else if (fx > mMaxScrollX) {
      smoothScrollTo(mMaxScrollX, 0);
    } else {
      smoothScrollTo(fx, 0);
    }
  }

  //  //调用此方法滚动到目标位置
  public void smoothScrollTo(int fx, int fy) {
    int dx = fx - getScrollX();
    int dy = fy - getScrollY();
    smoothScrollBy(dx, dy);
  }

  //调用此方法设置滚动的相对偏移
  public void smoothScrollBy(int dx, int dy) {

    //设置mScroller的滚动偏移量
    mScroller.startScroll(getScrollX(), 0, dx, dy, Math.abs(dx * DURATION / mMaxScrollX));
    invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
  }

  @Override
  public void computeScroll() {

    //先判断mScroller滚动是否完成
    if (mScroller.computeScrollOffset()) {

      //这里调用View的scrollTo()完成实际的滚动
      scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

      //必须调用该方法,否则不一定能看到滚动效果
      postInvalidate();
    }
    super.computeScroll();
  }

  /**
   * fragment或者activity 结束的接口
   */
  public interface OnFinishListener{
    void onFinish();
  }
}

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


推荐阅读
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文将详细介绍如何使用剪映应用中的镜像功能,帮助用户轻松实现视频的镜像效果。通过简单的步骤,您可以快速掌握这一实用技巧。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何在 Xcode 中使用快捷键和菜单命令对多行代码进行缩进,包括右缩进和左缩进的具体操作方法。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 本文总结了在使用Ionic 5进行Android平台APK打包时遇到的问题,特别是针对QRScanner插件的改造。通过详细分析和提供具体的解决方法,帮助开发者顺利打包并优化应用性能。 ... [详细]
  • 理解存储器的层次结构有助于程序员优化程序性能,通过合理安排数据在不同层级的存储位置,提升CPU的数据访问速度。本文详细探讨了静态随机访问存储器(SRAM)和动态随机访问存储器(DRAM)的工作原理及其应用场景,并介绍了存储器模块中的数据存取过程及局部性原理。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • 几何画板展示电场线与等势面的交互关系
    几何画板是一款功能强大的物理教学软件,具备丰富的绘图和度量工具。它不仅能够模拟物理实验过程,还能通过定量分析揭示物理现象背后的规律,尤其适用于难以在实际实验中展示的内容。本文将介绍如何使用几何画板演示电场线与等势面之间的关系。 ... [详细]
author-avatar
水水2502919973
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有