今天带大家实现简书的滑动返回效果.
先看看效果图:
最终效果图.gif因为没有具体内容,也没有简书的图片资源,所以稍微简陋了点.但是依然不妨碍我们的效果展示~
OK,接下来惯例,通过阅读本文你能学习到:
- ViewDragHelper的使用(如果你想学习自定义View,那么ViewDragHelper你绝对不能错过)
- 好像也没有什么了....
这个效果,难度不大,会ViewDragHelper的同学应该10分钟就能写出来了吧~如果不会也没关系~
1. 我们自定义一个SwipeBackFrameLayout继承自FrameLayout1.1 因为看到左边黄色的View是被遮住的,而另外一个View的宽度是MatchParent的,所以FrameLayout是不错的选择.顺便增加一个回调,通知activity去finish
1 2 3 4 5 6 7 | public void setCallback(Callback mCallback){ this .mCallback = mCallback; } private Callback mCallback; public interface Callback{ void onShouldFinish(); } |
1.2 Xml布局,非常简单:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | < yifeiyuan.practice.practicedemos.drager.SwipeBackFrameLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:id = "@+id/swipe_back" android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = "yifeiyuan.practice.practicedemos.drager.SwipeBackActivity" > < TextView android:layout_width = "40dp" android:layout_height = "match_parent" android:text = "@string/hello_world" android:gravity = "center" android:background = "#ffff00" /> < View android:layout_width = "match_parent" android:layout_height = "match_parent" android:background = "#ff00ff" /> </ yifeiyuan.practice.practicedemos.drager.SwipeBackFrameLayout > |
1.3 实例化一个ViewDragHelper
1 2 3 4 5 6 7 8 9 | //1f代表灵敏度 mDragHelper = ViewDragHelper.create( this , 1f, new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) { return false ; } } //因为我们是从左向右滑动 所以设置EDGE_LEFT mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); |
1.4 在SwipeBackFrameLayout里实例化xml里的子View
1 2 3 4 5 6 7 8 9 | private View mDividerView; private View mContentView; @Override protected void onFinishInflate() { super .onFinishInflate(); mDividerView = getChildAt( 0 ); mDividerView.setAlpha(0f); mContentView = getChildAt( 1 ); } |
1.5 让ViewDragHelper处理touch事件
1 2 3 4 5 6 7 8 9 | @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { mDragHelper.processTouchEvent(event); return true ; } |
1.6重写ViewDragHelper的一些处理方法
已附上详细注释
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | @Override public void onEdgeTouched( int edgeFlags, int pointerId) { super .onEdgeTouched(edgeFlags, pointerId); //触摸到左边界的时候 我们capture住mContentView mDragHelper.captureChildView(mContentView, pointerId); } @Override public int getViewHorizontalDragRange(View child) { return 1 ; } [url=home.php?mod=space&uid= 389554 ] @Override [/url] public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super .onViewPositionChanged(changedView, left, top, dx, dy); Log.d(TAG, "onViewPositionChanged() called with left = [" + left + "], top = [" + top + "], dx = [" + dx + "], dy = [" + dy + "]" ); //0.0 - 1.0 //Notice 这边可以给个接口回调出去,就可以做各种炫酷的效果了 float alpha = ( float ) (left* 1.0 /mDividerWidth); mDividerView.setAlpha(alpha); } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { // Log.d(TAG, "clampViewPositionHorizontal() called with dx = [" + dx + "]"); // 计算left 我们的目标范围是0-dividerwidth的宽度 mLastdx = dx; int newLeft = Math.min(mDividerWidth, Math.max(left, 0 )); return newLeft; } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { //>0代表用户想关闭 if (mLastdx> 0 ){ // 还不到关闭条件,我们让view滑动过去,再关闭 if (mDividerWidth != releasedChild.getLeft()) { mDragHelper.settleCapturedViewAt(mDividerWidth,releasedChild.getTop(); invalidate(); } else { if (mCallback != null ) { mCallback.onShouldFinish(); } } } else { //用户不想关闭 ,则滑动到最左边 if (mDividerWidth != 0 ) { mDragHelper.settleCapturedViewAt( 0 , releasedChild.getTop()); invalidate(); } } } @Override public void onViewDragStateChanged( int state) { super .onViewDragStateChanged(state); //滑动停止,并且到达了滑动的判断条件 则回调关闭 if (mDragHelper.getViewDragState()==ViewDragHelper.STATE_IDLE&&mCallback != null &&mDividerWidth==mContentView.getLeft()&&mLastdx> 0 ) { mCallback.onShouldFinish(); } } }); |
1.7 增加对view滑动事件处理,对于以上mDividerWidth我们在onLayout里获取
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | private int mDividerWidth; @Override protected void onLayout( boolean changed, int left, int top, int right, int bottom) { super .onLayout(changed, left, top, right, bottom); mDividerWidth = mDividerView.getWidth(); } //Notice view 刚初始化的时候就会被调用一次 @Override public void computeScroll() { super .computeScroll(); // Log.d(TAG, "computeScroll() called with " + ""); if (mDragHelper.continueSettling( true )) { invalidate(); } } |
我们写完自定义view后还需要自定义一下activity的退出动画~
2.定义activity的finish动画2.1 在anim目录下,创建两个动画xml:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | //no_anim < alpha android:duration = "300" xmlns:android = "http://schemas.android.com/apk/res/android" android:fromAlpha = "1.0" android:toAlpha = "1.0" ></ alpha > //out_to_right < translate xmlns:android = "http://schemas.android.com/apk/res/android" android:duration = "300" android:fromXDelta = "0%" android:toXDelta = "100%" ></ translate > |
2.2 在activity里设置callback监听,并运用动画
1 2 3 4 5 6 7 | mSwipeBack.setCallback( new SwipeBackFrameLayout.Callback() { @Override public void onShouldFinish() { finish(); overridePendingTransition(R.anim.no_anim, R.anim.out_to_right); } }); |
好了!!~代码量非常少!~就是这么简单~
吐槽一下,简书对代码块的支持太差了,代码复制过来全是乱的!!
同学们还是去看源码吧:
源码在我的 Github (https://github.com/AlanCheen/PracticeDemo)上
如果你觉得喜欢,举手之劳,给我点个赞吧!~
如果有什么不好的,不对的欢迎指出!~
如果有什么更好的方式,也欢迎指导!!!!
下次见!~