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

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

(0)

相关推荐

  • Android仿微信右滑返回功能的实例代码

    先上效果图,如下: 先分析一下功能的主要技术点,右滑即手势判断,当滑到一直距离时才执行返回,并且手指按下的位置是在屏幕的最左边(这个也是有一定范围的),  这些可以实现onTouchEvent来实现. 接着就是返回时,有滑动效果,很显然这个是Acitivty切换动画实现的.好啦,分析完了就开干.下面上代码: @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case Mot

  • Android右滑返回上一个界面的实现方法

    Android右滑返回上一个界面的实现方法 public class BaseActivity extends Activity implements OnTouchListener { public ProgressDialog progressDialog; public String states; public RequestQueue mQueue; /** 触摸时按下的点 **/ PointF downP = new PointF(); /** 触摸时当前的点 **/ PointF

  • Android实现类似IOS右滑返回的效果(原因分析及解决办法)

    使用类库SwipeBackLayout https://github.com/Issacw0ng/SwipeBackLayout 出现的问题: 1. 主Activity返回时黑屏或者返回只是看到桌面背景而没有看到上一个Activity界面 原因: 使用滑动返回需要在Activity的额主题中声明android:windowIsTranslucent=true,而该属性是设置Activity为是否为透明主题,当主Activity采用透明主题时,由于是app Activity栈中的第一个,所以滑动返

  • android 右滑返回的示例代码

    类似于微信的右滑返回,在BaseActivity里利用dispatchTouchEvent()拦截右滑动作,利用setTranslationX()实现动画,在DecorView里添加View作为滑动时的左侧阴影. 渐进步骤: 设置activity背景透明 重写finish()等方法设置activity的跳转动画 重写dispatchTouchEvent()拦截 所需要 右滑动作 重写onTouchEvent()给根布局设置偏移量 添加滑动时上层activity的左侧阴影 滑动时关联下层activ

  • Android中SwipeBack实现右滑返回效果

    现在有很多App支持右滑返回,比如知乎,效果比较赞. 于是自己对Activity和Fragment进行了继承,派生出SwipeBackActivity和SwipeBackFragment,用于对这种效果的实现,也就是只要继承这两个类就可以了. 效果如下 Activity Fragment Frgament的效果实现比Activity稍微简单,因为Activity要考虑到dectorView. 支持滑动的控件SwipeLayout,核心思路就是把原有的控件添加到支持滑动的控件中,SwipeLayo

  • iOS禁用右滑返回的两种方法

    本文实例为大家分享了iOS禁用右滑返回的具体代码,供大家参考,具体内容如下 方式一: 前提:如果使用的自定义UINavigationController基类,请不要在此基类里写相关的手势操作方法. 代码如下: -(void)viewDidAppear:(BOOL)animated{ if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.naviga

  • Android 实现右滑返回功能

    前言 右滑返回这个功能好像在iOS上蛮实用的,因为它的返回键在左上角,右手握持手机就得穿越整个屏幕去按它,不过对于大屏Android手机也是蛮实用的,右下角的返回键随着屏占比的增大按起来的难度也不小,就算有mBack这样的交互,多一个右滑返回也是极好的是吧? 上代码 SwipBackDemo 上效果图 SlidingPaneLayout 这个东西是Support V4中早就存在的,同样是侧滑,远没有侧滑菜单android.support.v4.widget.DrawerLayout被熟知,这玩意

  • Android实现全局右滑返回

    目前Android手机的全面屏越来越盛行,很多应用都已经支持了右滑返回上一级页面的功能,那么这个功能如何实现呢? 首先来说下思路吧,主要是通过 MotionEvent 这个事件通过对这个事件的不同处理,在通过 PointF 来监听按下去的点,处于什么位置. 接下来,通过代码给大家讲解一下 显示新建一个Gesture的这个样一个类,用来处理,滑动的逻辑. public class GestureHandler {} 接下来是定义相关的一些屏幕宽高.滑动的区间的一些表示 //屏幕宽高 int sWi

  • Android仿抖音右滑清屏左滑列表功能的实现代码

    概述 ​ 项目中要实现仿抖音直播间滑动清屏,侧滑列表的功能,在此记录下实现过程和踩坑记录希望避免大家走些弯路,也当作自己的一个总结 ​ 首先看下Demo中的效果 ​ 阅读文章需要提前熟悉些事件分发的内容,相信大家都已经了解过了,网上也有很多优秀的文章,这里推荐两篇自己读过印象较深的文章 https://www.jb51.net/article/124249.htm https://www.jb51.net/article/124861.htm 关于这方面的知识,在Android中是再重要不过的了

  • Swift NavigationBar隐藏后的右滑手势效果

    需求 我们在开发中经常遇见这样的需求,就是A视图没有导航,pushB视图后导航栏.然后要求可以使用iOS的系统侧滑返回功能.类似如下的功能: 问题 在处理这个需求的时候,我们一般会遇到两个问题: 右滑返回手势 ios开发中,使用push视图,系统是有默认的侧滑返回上个视图的功能.但是当我们自定义导航栏时,这个手势的事件就没有再触发,此时只要我们重新将代理设置为controller即可. 代码示例: 在BaseViewController中添加如下的代码 //开启 push视图 右滑手势() fi

随机推荐