《android开发艺术探索》读书笔记(三)--分发机制和滑动冲突
No1:
通过MotionEvent对象可以得到点击事件发生的x和y坐标,getX/getY返回的是相对于当前View左上角的x和y坐标,而getRawX/getRawY返回的是相对于手机屏幕左上角的x和y坐标。
No2:
TouchSlop是系统所能识别的被认为是滑动的最小距离,这是一个常量,和设备有关,在不同设备上这个值可能是不同的,通过如下的方式可以获取这个常量
ViewConfiguration.get(getContext()).getScaledTouchSlop();
No3:
VelocityTracker -速度追踪
首先在View的onTouchEvent方法中追踪
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
采用如下方式来获取当前的速度
velocityTracker.computeCurrentVelocity(1000); int xVelocity = (int)velocityTracker.getXVelocity(); int yVelocity = (int)velocityTracker.getYVelocity();
这里是指1000ms内手指在水平和竖直方向上滑过的像素
速度=(终点位置-起点位置)/时间段
重置并回收内存
velocityTracker.clear();
velocityTracker.recycle();
No4:
GestureDetector -手势检测
首先需要创建一个GestureDetector对象并实现OnGestrueListener接口
GestureDetector mGestureDetector = new GestureDetector(this); //解决长按屏幕后无法拖动的现象 mGestureDetector.setIsLongpressEnabled(false);
接着,在待监听View的onTouchEvent方法中添加如下实现
boolean consume = mGestureDetector.onTouchEvent(event); return consume;
No5:
No6:
如果只是监听滑动相关的,建议自己在onTouchEvent中实现
如果要监听双击这种行为的话,那么就使用GestureDector
No7:
View的scrollTo/scrollBy的滑动过程是瞬间完成的。
Scroller可以实现有过渡效果的滑动,Scroller本身无法让View弹性滑动,它需要和View的computeScroll方法配合才能实现。
Scroller scroller = new Scroller(mContext); //缓慢滚动到指定位置 private void smoothScrollTo(int destX,int dextY){ int scrollX = getScrollX; int delta = destX - scrollX; //1000ms内滑动destX,效果就是慢慢滑动 mScroller.startScroll(scrollX,0,delta,0,1000); invalidate(); } @Override public void computeScroll(){ if(mScroller.computeScrollOffset()){ scrollTo(mScroller.getCurrX(),mScroller.getCurrY); postInvalidate(); } }
No8:
View的滑动有三种实现方式:
1.通过View本身提供的scrollTo/scrollBy实现滑动
2.通过动画给View施加平移效果来实现滑动
3.通过改变View的LayoutParams使得View重新布局从而实现滑动
No9:
scrollBy实际上也是调用了scrollTo方法,它实现了基于当前位置的相对滑动,而scrollTo则实现了基于所传递参数的绝对滑动。
scrollTo和scrollBy只能将View的内容进行移动,并不能将View本身进行移动。例如:TextView移动的是TextView中的文字,而不是TextView本身
No10:
View的三种滑动方式比较:
scrollTo/scrollBy:操作简单,适合对View内容的滑动
动画:操作简单,主要适用于没有交互的View和实现复杂的动画效果
改变布局参数:操作稍微复杂,适用于有交互的View
No11:
View跟手滑动效果
public boolean onTouchEvent(MotionEvent event){ int x = (int)event.getRawX(); int y = (int)event.getRawY(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastX; int deltaY = y - mLastY; int translationX = (int)ViewHelper.getTranslationX(this) + deltaX; int translationY = (int)ViewHelper.getTranslationY(this) + deltaY; ViewHelper.setTranslationX(this,translationX); ViewHelper.setTranslationY(this,translationY); break; case MotionEvent.ACTION_UP; break; default: break; } mLastX = x; mLastY = y; return true; }
No12:
Scroller滑动原理:当View重绘后会在draw方法中调用computeScroll,而computeScroll又会去向Scroller获取当前的scrollX和scrollY;然后通过scrollTo方法实现滑动;接着又调用postInvalidata方法来进行第二次重绘,这一次重绘的过程和第一次重绘一样,还是会导致computeScroll方法被调用;然后继续向Scroller获取scrollX和scrollY,并通过scrollTo方法滑动到新的位置,如此反复,知道整个滑动过程结束。
其中,computeScroll的作用就是计算当前View的滑动位置
No13:
ViewGroup默认不拦截任何事件,Android源码中ViewGroup的onInterceptTouchEvent方法默认返回false,
View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么它的onTouchEvent方法就会被调用。
View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)。
No14:
Window的实现类是PhoneWindow
No15:
如何根据坐标来得到滑动的方向呢?
1.可以依据滑动路径和水平方向所形成的夹角
2.依据水平方向和竖直方向上的距离差来判断
3.依据水平和竖直方向的速度差来做判断
No16:
外部拦截法需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可
public boolean onInterceptTouchEvent(MotionEvent event){ boolean intercepted = false; int x = (int)event.getX(); int y = (int)event.getY(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN:{ intercepted = false; } case MotionEvent.ACTION_MOVE:{ if(父容器需要当前点击事件){ intercepted = true; }else{ intercepted = false; } break; } case MotionEvent.ACTION_UP:{ intercepted = false; break; } default: break; } mLastXIntercept = x; mLastYIntercept = y; return intercepted; }
内部拦截法需要配合requestDisallowInterceptTouchEvent,需要重写子元素的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent event){ int x = (int)event.getX(); int y = (int)event.getY(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN:{ parent.requestDisallowInterceptTouchEvent(true); break; } case MotionEvent.ACTION_MOVE:{ int deltaX = x - mLastX; int deltaY = y - mLastY; if(父容器需要当前点击事件){ parent.requestDisallowInterceptTouchEvent(false); } break; } case MotionEvent.ACTION_UP:{ break; } default: break; } mLastX = x; mLastY = y; return super.dispatchTouchEvent(event); }
推荐外部拦截法