Android View滑动相关的基础知识点
2017-06-13 10:17
375 查看
本文涉及到的知识点:MotionEvent、ViewConfiguration、VelocityTracker 、GestureDetector、scrollTo、scrollBy、Scroller、OverScroller
ACTION_MOVE :手指在屏幕上移动
ACTION_UP :手指在屏幕上松开的一刹那
ACTION_CANCEL:当前滑动手势被打断
1、如果点击屏幕立即松开,事件顺序 ACTION_DOWN->ACTION_UP
2、如果点击屏幕滑动然后松开,事件顺序 ACTION_DOWN->ACTION_MOVE->ACTION_UP
3、ACTION_CANCEL是当前滑动手势被打断时调用,比如在某个控件保持按下操作,然后手势从控件内部转移到外部,此时控件手势事件被打断,会触发ACTION_CANCEL
MotionEvent类中有两组方法 getX()/getY() 以及getRawX()/getRawY(),由此我们可以得到点击事件的x坐标和y坐标,他们之间不同的是getX()/getY() 返回的是相当于当前View左上角的x和y坐标,getRawX()/getRawY()返回的是相当于手机屏幕左上角的x和y坐标。
1、getScaledTouchSlop():
ViewConfiguration.get(getContext()).getScaledTouchSlop()返回一个int类型的值,表示被系统认为的滑动的最小距离,小于这个值系统不认为是一次滑动 。
2、getScaledMaximumFlingVelocity()
ViewConfiguration.get(getContext()).getScaledMaximumFlingVelocity()获得一个fling手势动作的最小速度值。
3、getScaledMinimumFlingVelocity()
ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity()获得一个fling手势动作的最大速度值。
*1、通过VelocityTracker.obtain()来获得VelocityTracker 实例,
2、通过velocityTracker.addMovement(event)将用户的滑动事件传给velocityTracker
3、通过velocityTracker.computeCurrentVelocity(int units, float maxVelocity)来开始计算速度,然后调用getXVelocity(int)和getYVelocity(int)得到每个指针id检索速度。*
官网给的一个例子 Tracking Movement:
OnGestureListener有下面的几个动作:
按下(onDown): 刚刚手指接触到触摸屏的那一刹那,就是触的那一下。
抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作。
长按(onLongPress): 手指按在持续一段时间,并且没有松开。
滚动(onScroll): 手指在触摸屏上滑动。
按住(onShowPress): 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。
抬起(onSingleTapUp):手指离开触摸屏的那一刹那。
官网给的例子:Detecting Common Gestures :
更详细可以看下这两篇博客:
http://blog.csdn.net/xyz_lmn/article/details/16826669
http://blog.csdn.net/hpk1994/article/details/51224228
ScrollBy默认也是调用的ScrollTo方法:
mScrollX 记录的是View的左边缘和View内容的左边缘之间的距离,mScrollY 记录的是View的上边缘和View内容的上边缘之间的距离
看源码scrollTo(int x, int y)中将x值赋给了mScrollX ,y赋给了mScrollY ,所以scrollTo是相对于View左边缘的绝对滑动,scrollBy是相对滑动,另外要注意的是:scrollTo和scrollBy是对View内容的滑动而不是对View的滑动。
调用Scroller的startScroll()方法并不会有任何滑动行为,这里的滑动指的是View内容的滑动而不是View的滑动,来看startScroll方法的源码:
可以看到只是一些赋值操作,没有任何滑动操作,所以如果想让View内容滑动,还需要invalidate()的参与,如:
看下效果:
![](http://upload-images.jianshu.io/upload_images/587163-0167141bdd549d8f.gif?imageMogr2/auto-orient/strip)
流程是这样的:首先在startScroll中设置各种滑动信息,这时并没有进行滑动,当调用下面的invalidate时,View会进行重绘,重绘时又会调用View中的computeScroll()方法,View中的computeScroll()是个空实现,需要我们自己实现,在computeScroll()中,我们先通过computeScrollOffset判断是否滑动完成,如果没完成,通过mScroller.getCurrX()、mScroller.getCurrY()得到滑动的位置,然后通过scrollTo(currX, currY)来实现滑动,此时完成了一次滑动,然后调用invalidate()方法继续重绘,继续滑动到新位置,直到滑动完成。
OverScroller因为和Scroller非常类似,而且增加了回弹支持,所以大部分情况下我们都可以使用OverScroller。
下一篇通过模仿一下QQ侧滑菜单例子来实践一下上述知识点:Android仿QQ侧滑菜单
MotionEvent
ACTION_DOWN :手指刚接触到屏幕ACTION_MOVE :手指在屏幕上移动
ACTION_UP :手指在屏幕上松开的一刹那
ACTION_CANCEL:当前滑动手势被打断
1、如果点击屏幕立即松开,事件顺序 ACTION_DOWN->ACTION_UP
2、如果点击屏幕滑动然后松开,事件顺序 ACTION_DOWN->ACTION_MOVE->ACTION_UP
3、ACTION_CANCEL是当前滑动手势被打断时调用,比如在某个控件保持按下操作,然后手势从控件内部转移到外部,此时控件手势事件被打断,会触发ACTION_CANCEL
MotionEvent类中有两组方法 getX()/getY() 以及getRawX()/getRawY(),由此我们可以得到点击事件的x坐标和y坐标,他们之间不同的是getX()/getY() 返回的是相当于当前View左上角的x和y坐标,getRawX()/getRawY()返回的是相当于手机屏幕左上角的x和y坐标。
ViewConfiguration
主要用到下面三个:1、getScaledTouchSlop():
ViewConfiguration.get(getContext()).getScaledTouchSlop()返回一个int类型的值,表示被系统认为的滑动的最小距离,小于这个值系统不认为是一次滑动 。
2、getScaledMaximumFlingVelocity()
ViewConfiguration.get(getContext()).getScaledMaximumFlingVelocity()获得一个fling手势动作的最小速度值。
3、getScaledMinimumFlingVelocity()
ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity()获得一个fling手势动作的最大速度值。
VelocityTracker
VelocityTracker 是一个跟踪触摸事件滑动速度的帮助类,用于实现flinging 及其他类似的手势,它的使用流程:*1、通过VelocityTracker.obtain()来获得VelocityTracker 实例,
2、通过velocityTracker.addMovement(event)将用户的滑动事件传给velocityTracker
3、通过velocityTracker.computeCurrentVelocity(int units, float maxVelocity)来开始计算速度,然后调用getXVelocity(int)和getYVelocity(int)得到每个指针id检索速度。*
方法 | 备注 |
---|---|
obtain() | 获得VelocityTracker 实例 |
addMovement(MotionEvent event) | 将滑动事件传给VelocityTracker |
computeCurrentVelocity(int units, float maxVelocity) | 计算速度 参数units是时间,单位是毫秒 maxVelocity是最大滑动速度 |
getXVelocity(int id) | 获得X轴的速度,在使用此方法之前必须先调用computeCurrentVelocity()计算速度 |
getYVelocity(int id) | 获得Y轴的速度,在使用此方法之前必须先调用computeCurrentVelocity()计算速度 |
public class MainActivity extends Activity { private static final String DEBUG_TAG = "Velocity"; ... private VelocityTracker mVelocityTracker = null; @Override public boolean onTouchEvent(MotionEvent event) { int index = event.getActionIndex(); int action = event.getActionMasked(); int pointerId = event.getPointerId(index); switch(action) { case MotionEvent.ACTION_DOWN: if(mVelocityTracker == null) { // Retrieve a new VelocityTracker object to watch the velocity of a motion. mVelocityTracker = VelocityTracker.obtain(); } else { // Reset the velocity tracker back to its initial state. mVelocityTracker.clear(); } // Add a user's movement to the tracker. mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() // and getYVelocity() to retrieve the velocity for each pointer ID. mVelocityTracker.computeCurrentVelocity(1000); // Log velocity of pixels per second // Best practice to use VelocityTrackerCompat where possible. Log.d("", "X velocity: " + VelocityTrackerCompat.getXVelocity(mVelocityTracker, pointerId)); Log.d("", "Y velocity: " + VelocityTrackerCompat.getYVelocity(mVelocityTracker, pointerId)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // Return a VelocityTracker object back to be re-used by others. mVelocityTracker.recycle(); break; } return true; } }
GestureDetector
GestureDetector相比于OnTouchListener提供了更多的手势操作,GestureDetector类对外提供了两个接口:OnGestureListener,OnDoubleTapListener,还有一个内部类SimpleOnGestureListenerOnGestureListener有下面的几个动作:
按下(onDown): 刚刚手指接触到触摸屏的那一刹那,就是触的那一下。
抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作。
长按(onLongPress): 手指按在持续一段时间,并且没有松开。
滚动(onScroll): 手指在触摸屏上滑动。
按住(onShowPress): 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。
抬起(onSingleTapUp):手指离开触摸屏的那一刹那。
官网给的例子:Detecting Common Gestures :
public class MainActivity extends Activity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener{ private static final String DEBUG_TAG = "Gestures"; private GestureDetectorCompat mDetector; // Called when the activity is first created. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener mDetector = new GestureDetectorCompat(this,this); // Set the gesture detector as the double tap // listener. mDetector.setOnDoubleTapListener(this); } @Override public boolean onTouchEvent(MotionEvent event){ this.mDetector.onTouchEvent(event); // Be sure to call the superclass implementation return super.onTouchEvent(event); } @Override public boolean onDown(MotionEvent event) { Log.d(DEBUG_TAG,"onDown: " + event.toString()); return true; } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString()); return true; } @Override public void onLongPress(MotionEvent event) { Log.d(DEBUG_TAG, "onLongPress: " + event.toString()); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.d(DEBUG_TAG, "onScroll: " + e1.toString()+e2.toString()); return true; } @Override public void onShowPress(MotionEvent event) { Log.d(DEBUG_TAG, "onShowPress: " + event.toString()); } @Override public boolean onSingleTapUp(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString()); return true; } @Override public boolean onDoubleTap(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString()); return true; } @Override public boolean onDoubleTapEvent(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString()); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString()); return true; } }
更详细可以看下这两篇博客:
http://blog.csdn.net/xyz_lmn/article/details/16826669
http://blog.csdn.net/hpk1994/article/details/51224228
ScrollTo 、scrollBy
1、ScrollTo(int x, int y)、ScrollBy(int x, int y)ScrollBy默认也是调用的ScrollTo方法:
public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }
mScrollX 记录的是View的左边缘和View内容的左边缘之间的距离,mScrollY 记录的是View的上边缘和View内容的上边缘之间的距离
public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { postInvalidateOnAnimation(); } } }
看源码scrollTo(int x, int y)中将x值赋给了mScrollX ,y赋给了mScrollY ,所以scrollTo是相对于View左边缘的绝对滑动,scrollBy是相对滑动,另外要注意的是:scrollTo和scrollBy是对View内容的滑动而不是对View的滑动。
Scroller常用方法:
Scroller类并不会控制View进行滑动,它只是View滑动的辅助类,负责计算View滑动的一系列参数:方法 | 备注 |
---|---|
getCurrX()、getCurrY() | 距离原位置在X、Y轴方向的距离,往左滑动为正值,反之为负值;往上滑为正值,反之为负 |
getStartX()、getStartY() | 开始滑动时距离原位置在X、Y轴方向的距离,往左滑动为正值,反之为负值;往上滑为正值,反之为负 |
getFinalX()、getFinalY() | 滑动停止时距离原位置在X、Y轴方向的距离,往左滑动为正值,反之为负值;往上滑为正值,反之为负 |
startScroll(int startX, int startY, int dx, int dy) | 开始滑动,默认时间250毫秒,startX、startY为开始位置,左上为正值,右下为负值,dx、dy为要滑动的距离,方向同startX、startY |
startScroll(int startX, int startY, int dx, int dy, int duration) | 作用同上,多了一个滑动持续时间时间duration |
computeScrollOffset() | 当前滑动是否已经完成,true表示已经完成,false表示还未完成。 |
fling(int startX, int startY, int velocityX, int velocityY,int minX, int maxX, int minY, int maxY) | 手指在触摸屏上迅速移动,并松开,靠惯性滑动 |
abortAnimation() | 强制结束动画,并滑到最终位置 |
forceFinished(boolean finished) | 是否强制结束动画,true便是强制结束 |
public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; mFinished = false; mDuration = duration; mStartTime = AnimationUtils.currentAnimationTimeMillis(); mStartX = startX; mStartY = startY; mFinalX = startX + dx; mFinalY = startY + dy; mDeltaX = dx; mDeltaY = dy; mDurationReciprocal = 1.0f / (float) mDuration; }
可以看到只是一些赋值操作,没有任何滑动操作,所以如果想让View内容滑动,还需要invalidate()的参与,如:
private Scroller mScroller = new Scroller(context); ... public void zoomIn() { // Revert any animation currently in progress mScroller.forceFinished(true); // Start scrolling by providing a starting point and // the distance to travel mScroller.startScroll(0, 0, 100, 0,3000); // Invalidate to request a redraw invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { // Get current x and y positions int currX = mScroller.getCurrX(); int currY = mScroller.getCurrY(); scrollTo(currX, currY); invalidate(); } }
看下效果:
![](http://upload-images.jianshu.io/upload_images/587163-0167141bdd549d8f.gif?imageMogr2/auto-orient/strip)
流程是这样的:首先在startScroll中设置各种滑动信息,这时并没有进行滑动,当调用下面的invalidate时,View会进行重绘,重绘时又会调用View中的computeScroll()方法,View中的computeScroll()是个空实现,需要我们自己实现,在computeScroll()中,我们先通过computeScrollOffset判断是否滑动完成,如果没完成,通过mScroller.getCurrX()、mScroller.getCurrY()得到滑动的位置,然后通过scrollTo(currX, currY)来实现滑动,此时完成了一次滑动,然后调用invalidate()方法继续重绘,继续滑动到新位置,直到滑动完成。
OverScroller
OverScroller大部分API和Scroller是一样的,只是多了一些对滑动到边缘时的处理方法:方法 | 备注 |
---|---|
isOverScrolled() | 通过fling()方法只会判断是否滑动过界 |
springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) | 回弹效果 |
fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY) | 手指在触摸屏上迅速移动,并松开,靠惯性滑动 |
notifyHorizontalEdgeReached(int startX, int finalX, int overX) | 通知scroller已经在X轴方向到达边界 |
notifyVerticalEdgeReached(int startY, int finalY, int overY) | 通知scroller已经在Y轴方向到达边界 |
下一篇通过模仿一下QQ侧滑菜单例子来实践一下上述知识点:Android仿QQ侧滑菜单
相关文章推荐
- android学习中关于Textview,Button,EditText,连接的设置,Intent,Activity不同状态等的一系列基础知识点
- Android多屏滑动:ViewPager基础使用及PagerTabStrip先天缺陷(附源码)
- View的事件体系(上)(View基础知识,滑动,弹性滑动)
- Android ImageView长按保存图片及截屏相关知识
- android成长日记 11.详细学习了数据存储相关基础知识
- Android View&ViewGroup相关类基础关系图
- Android控件之WebView(基础知识)
- Android自定义View——基础知识篇
- Android图形显示系统——下层显示1:基础知识与相关文件
- Android中的RecyclerView: 基础知识
- Android图形显示系统——下层显示1:基础知识与相关文件
- Android基于客户端和服务器的Socket编程例子之Socket基础通讯--socket相关知识
- Android WebView相关知识(全)
- Android基础知识之控件系列(1)——TextView及自定义动态TextView
- android setSelected及view相关知识
- [Android基础知识] 之十一: View类的原理与使用
- Android View学习笔记(一):View基础知识
- Android 学习第16课,java 包、类等相关的一些基础知识
- Android 开发艺术与探究 第三章 View的事件体系之View的基础知识
- Android基础知识之控件系列(4)——CheckedTextView、Chronometer、DigitalClock类