首页 > 代码库 > 右滑返回功能 几种实现方式总结
右滑返回功能 几种实现方式总结
方式一:利用onTouchListener简单实现
基本思想:让Activity中的根布局实现onTouchListener当手指在屏幕上向右滑动时,我们记下ACTION_DOWN的X轴的位置在手指滑动时(即ACTION_MOVE),获取滑动时的X轴的位置当滑动的位置大于某个临界值且在这个方向上的速度大于某个临界值时,我们就认为用户滑动手指的意图是返回上一个页面。重大缺点:页面完全没有跟着手指滑动,只是当手指滑动到一定条件时将Activity干掉,所以会感觉退出的非常生硬。public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Button btn = new Button(this);btn.setText("点击进入第二个页面");setContentView(btn);btn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(MainActivity.this, SecondActivity.class));overridePendingTransition(R.anim.in_from_right, R.anim.out_to_left);//设置切换动画,从右边进入,左边退出}});}}public class SecondActivity extends Activity implements OnTouchListener {/**记录手指按下时的横坐标*/private float xDown;/**记录手指移动时的横坐标*/private float xMove;/**用于计算手指滑动的速度*/private VelocityTracker mVelocityTracker;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);TextView tv_info;tv_info = new TextView(this);tv_info.setTextColor(Color.RED);tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);tv_info.setGravity(Gravity.CENTER);tv_info.setText("从左侧边缘向右滑动返回");setContentView(tv_info);tv_info.setOnTouchListener(this);}@Overridepublic boolean onTouch(View v, MotionEvent event) {//创建VelocityTracker对象,并将触摸content界面的滑动事件加入到VelocityTracker当中。if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();mVelocityTracker.addMovement(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN:xDown = event.getRawX();break;case MotionEvent.ACTION_MOVE:if (xDown < MyApplication.XDISTANCE_MAXFROM) {//当滑动的起始位置小于我们设定的值xMove = event.getRawX();int distanceX = (int) (xMove - xDown);//活动的距离int xSpeed = getScrollVelocity();//获取顺时速度//当滑动的距离大于我们设定的最小距离且滑动的瞬间速度大于我们设定的速度if (distanceX > MyApplication.XDISTANCE_MIN && xSpeed > MyApplication.XSPEED_MIN) {finish();overridePendingTransition(R.anim.in_from_left, R.anim.out_to_right);//设置切换动画,从右边进入,左边退出}}break;case MotionEvent.ACTION_UP:mVelocityTracker.recycle();mVelocityTracker = null;break;default:break;}return true;}/*** 获取手指在界面滑动的速度。* @return 滑动速度,以每秒钟移动了多少像素值为单位。*/private int getScrollVelocity() {mVelocityTracker.computeCurrentVelocity(1000);int velocity = (int) mVelocityTracker.getXVelocity();return Math.abs(velocity);}}public class MyApplication extends Application {private static MyApplication mApplication = null;/**手指向右滑动时的最大起始距离(只有从左边缘滑动才有效)*/public static int XDISTANCE_MAXFROM;/**手指向右滑动时的最小距离(防止误操作)*/public static int XDISTANCE_MIN;/**手指向右滑动时的最小速度(防止误操作)*/public static int XSPEED_MIN;@Overridepublic void onCreate() {super.onCreate();mApplication = this;XDISTANCE_MAXFROM = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics());XDISTANCE_MIN = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());XSPEED_MIN = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());}public static MyApplication getApplication() {return mApplication;}}
方式二:利用View的滚动原理,自定义根ViewGroup
基本思想:我们的滑动逻辑主要是利用View的scrollBy() 方法, scrollTo()方法和Scroller类来实现的当手指拖动视图的时候,我们监听手指在屏幕上滑动的距离利用View的scrollBy() 方法使得View随着手指的滑动而滑动而当手指离开屏幕,我们在根据逻辑使用Scroller类startScroll()方法设置滑动的参数,然后再根据View的scrollTo进行滚动。对于View的滑动,存在一些Touch事件消费的处理等问题,最主要的就是Activity里面有一些ListView、 GridView、ScrollView等控件假如我们Activity里面存在ListView、GridView等控件的话,我们对Activity的最外层布局进行滚动根本就无效果,因为Touch事件被ListView、GridView等控件消费了,所以Activity的最外层布局根本得不到Touch事件,也就实现不了Touch逻辑了为了解决此Touch事件问题,我们将OnTouchListener直接设置到ListView、GridView上面,这样子就避免了Activity的最外层接受不到Touch事件的问题了public class SwipeBackLayout extends FrameLayout {public static final int XDISTANCE_MAXFROM = 30;/**手指向右滑动时的最大【起始】距离(防止误操作,只有从左边缘滑动才有效)*/private int xdistance_maxfrom;public static final int XDISTANCE_MIN = 90;/**手指向右滑动时的最小【滑动】距离(防止误操作,只有滑动超过1/4才关闭)*/private int xdistance_min;/**手指向右滑动时的最小【滑动】距离(只有滑动超过此距离才滚动view)*/private int xdistance_mintouch;//******************************************************************************************/**是否要finish掉Activity*/private boolean isFinish;/**Activity最外层的布局,系统会对我们的布局文件的最外层套一个FrameLayout,所以我们其实就是对FrameLayout进行滚动就行了*/private View mContentView;private int viewWidth;/**记录按下点的X坐标*/private int downX;/**记录手指在屏幕上的X坐标*/private int touchX;/**Scroller只是个计算器,提供插值计算,让滚动过程具有动画属性,但它并不是UI,也不是辅助UI滑动,反而是单纯地为滑动提供计算*/private Scroller mScroller;private Activity mActivity;private List<ViewPager> mViewPagers = new LinkedList<ViewPager>();//构造方法******************************************************************************************public SwipeBackLayout(Context context) {this(context, null);}public SwipeBackLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SwipeBackLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);xdistance_min = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, XDISTANCE_MIN, getResources().getDisplayMetrics());xdistance_maxfrom = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, XDISTANCE_MAXFROM, getResources().getDisplayMetrics());//getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件,如ViewPage就是用这个距离来判断用户是否翻页的xdistance_mintouch = ViewConfiguration.get(context).getScaledTouchSlop();//注意单位是pxmScroller = new Scroller(context);mShadowDrawable = getResources().getDrawable(R.drawable.shadow_left);}//必须(只需)调用的方法******************************************************************************************public void attachToActivity(Activity activity) {mActivity = activity;TypedArray a = activity.getTheme().obtainStyledAttributes(new int[] { android.R.attr.windowBackground });int background = a.getResourceId(0, 0);a.recycle();ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);decorChild.setBackgroundResource(background);decor.removeView(decorChild);addView(decorChild);mContentView = (View) decorChild.getParent();decor.addView(this);}//事件拦截与处理******************************************************************************************@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {ViewPager mViewPager = getMyTouchViewPager(mViewPagers, ev);//如果存在ViewPager并且ViewPager不是处在第一个Item,我们才拦截Touch事件,否则不拦截(Touch事件由ViewPager处理)if (mViewPager != null && mViewPager.getCurrentItem() != 0) return super.onInterceptTouchEvent(ev);switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:downX = (int) ev.getRawX();//getX() 获得相对view的位置坐标(不会超过view的长度和宽度),getRawX获得相对屏幕的位置坐标touchX = (int) ev.getRawX();break;case MotionEvent.ACTION_MOVE:int moveX = (int) ev.getRawX();if (downX <= xdistance_maxfrom && moveX - downX > xdistance_mintouch) return true;//当滑动的起始位置小于我们设定的值,break;}return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_MOVE:int moveX = (int) event.getRawX();if (moveX - downX > xdistance_mintouch) mContentView.scrollBy(touchX - moveX, 0);//核心代码:将View中的内容滚动指定距离,注意:不是滚动到指定位置//Move the scrolled position of your view. This will cause a call to onScrollChanged(int, int, int, int) and the view will be invalidated.//要知道:我们调用View的scrollBy()方法,不是对该View进行滚动,而是对该View里面的内容(例如Button上面的文字)进行滚动touchX = moveX;break;case MotionEvent.ACTION_UP:if (Math.abs(mContentView.getScrollX()) >= xdistance_min) {//当滑动的距离大于我们设定的最小距离时isFinish = true;//关闭scrollRight();//滑到右侧} else {//当滑动的距离小于我们设定的最小距离时isFinish = false;//不关闭scrollOrigin();//回到起始位置}break;}return true;}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);if (changed) {//获取该布局的父布局和获取其控件的宽度viewWidth = this.getWidth();getAlLViewPager(mViewPagers, this);}}@Overridepublic void computeScroll() {//Called by a parent to request that a child update its values for mScrollX and mScrollY if necessary.//computeScroll方法不是来让ViewGroup滑动的,真正让ViewGroup滑动的是scrollTo,scrollBy方法。computeScroll的作用是计算ViewGroup如何滑动if (mScroller.computeScrollOffset()) {//Call this when you want to know the new location.调用startScroll的时候scroller.computeScrollOffset()返回truemContentView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());postInvalidate();if (mScroller.isFinished() && isFinish) mActivity.finish();//如果Scroller已经停止了滑动}}//阴影******************************************************************************************/**滑动时左边缘添加阴影*/private Drawable mShadowDrawable;/**滑动时左边缘是否显示阴影*/private boolean isShowShadow = true;/**设置滑动时左边缘是否显示阴影*/public void setShowShadow(boolean isShowShadow) {this.isShowShadow = isShowShadow;}@Overrideprotected void dispatchDraw(Canvas canvas) {//绘制VIew本身,通过调用View.onDraw实现;绘制自己的孩子,通过调用dispatchDraw实现(ViewGroup往往重写的就是dispatchDraw方法)//Called by draw to draw the child views. This may be overridden by derived classes to gain control just before its children are drawn (but after its own view has been drawn).super.dispatchDraw(canvas);if (isShowShadow && mShadowDrawable != null && mContentView != null) {int left = mContentView.getLeft() - mShadowDrawable.getIntrinsicWidth();int right = left + mShadowDrawable.getIntrinsicWidth();int top = mContentView.getTop();int bottom = mContentView.getBottom();mShadowDrawable.setBounds(left, top, right, bottom);mShadowDrawable.draw(canvas);}}//******************************************************************************************/*** 获取SwipeBackLayout里面的ViewPager的集合*/private void getAlLViewPager(List<ViewPager> mViewPagers, ViewGroup parent) {int childCount = parent.getChildCount();for (int i = 0; i < childCount; i++) {View child = parent.getChildAt(i);if (child instanceof ViewPager) mViewPagers.add((ViewPager) child);else if (child instanceof ViewGroup) getAlLViewPager(mViewPagers, (ViewGroup) child);}}/*** 返回我们touch的ViewPager*/private ViewPager getMyTouchViewPager(List<ViewPager> mViewPagers, MotionEvent ev) {if (mViewPagers == null || mViewPagers.size() == 0) return null;Rect mRect = new Rect();for (ViewPager v : mViewPagers) {v.getHitRect(mRect);if (mRect.contains((int) ev.getX(), (int) ev.getY())) return v;}return null;}//******************************************************************************************/*** 滚动出界面*/private void scrollRight() {int delta = viewWidth + mContentView.getScrollX();//调用startScroll()是不会有滚动效果的,只有在computeScroll()获取滚动情况,做出滚动的响应,computeScroll在父控件执行drawChild时,会调用这个方法mScroller.startScroll(mContentView.getScrollX(), 0, -delta, 0, Math.abs(delta));//设置一些滚动的参数:int startX, int startY, int dx, int dy, int durationpostInvalidate();//刷新界面}/*** 滚动到起始位置*/private void scrollOrigin() {int delta = mContentView.getScrollX();mScroller.startScroll(mContentView.getScrollX(), 0, -delta, 0, Math.abs(delta));postInvalidate();}}/*** 想要实现向右滑动删除Activity效果只需要继承SwipeBackActivity即可* 如果当前页面含有ViewPager,只需要调用SwipeBackLayout的setViewPager()方法即可*/public class SwipeBackActivity extends Activity {protected SwipeBackLayout layout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);layout = new SwipeBackLayout(this);layout.attachToActivity(this);}@Overridepublic void startActivity(Intent intent) {super.startActivity(intent);overridePendingTransition(R.anim.base_slide_right_in, R.anim.base_slide_remain);}@Overridepublic void onBackPressed() {super.onBackPressed();overridePendingTransition(0, R.anim.base_slide_right_out);}}public class NormalActivity extends SwipeBackActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {requestWindowFeature(Window.FEATURE_NO_TITLE);//这里不但要放在setContentView前,还必须放在super.onCreate之前,否则报错!super.onCreate(savedInstanceState);TextView tv_info = new TextView(this);tv_info.setTextColor(Color.RED);tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);tv_info.setGravity(Gravity.CENTER);tv_info.setText("向右滑动返回,点击也能退出");//tv_info.setBackgroundColor(Color.GREEN);//因为本Activity的样式为【@android:style/Theme.Translucent】,如果根布局不设置背景色,则整个界面是透明的tv_info.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {finish();}});layout.setShowShadow(false);//不显示阴影setContentView(tv_info);}}<activityandroid:name=".NormalActivity"android:theme="@android:style/Theme.Translucent" />
方式三:利用开源框架实现
https://github.com/ikew0ng/SwipeBackLayout但是用起来有点问题其Activity的样式不能自定义,只能是透明的所以当前一个Activity滑动时看到的是桌面,而当其结束时却回到了之前的界面,这不是扯淡吗!
来自为知笔记(Wiz)
附件列表
右滑返回功能 几种实现方式总结
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。