3.1 View的基础知识android
主要有:View的位置参数,MotionEvent和TouchSlop对象,VelocityTracker,GestureDetector和Scroller对象ide
3.1.1 View布局
view是android中全部控件的基类,是一种界面层的控件的一种抽象.post
ViewGroup(控件组),内部包含了不少个控件,即一组view动画
3.1.2 View的位置参数this
View的位置主要由它的四个顶点来决定:top(左上角纵坐标),left(左上角横坐标),right(右下角横坐标),bottom(右下角纵坐标)spa
left = getLeft();code
right = getRight();对象
top = getTop();blog
bottom = getBottom();
从android3.0开始,增长了:x,y,translationX和translationY,x和y是View的左上角的左边,而translationX和translationY是View左上角相对于父容器的偏移量.
x = left + translationX;
y = right + translationY;
3.1.3 MotionEvent和TouchSlop
1. MotionEvent
手指接触屏幕后产生一些列的事件,典型的事件以下
2. TouchSlop
是系统所能识别出被认为是滑动的最小距离.若是两次滑动之间的距离小过这个常量,系统就不认为你是在进行滑动操做
经过ViewConfiguration.get(getContext()).getScaledTouchSlop()得到这个常量.
3.1.4 VelocityTracker,GestureDetector和Scroller
1.VelocityTracker
速度追踪:用来追踪手指在滑动过程当中的速度
首先在View的onTouchEvent方法中追踪当前点击事件的速度
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
注意两点:第一,获取速度以前必须先计算速度,即在getXVelocity()和getYVelocity()方法以前要先调用computeCurrentVelocity方法,第二点,这里的速度是指一段时间内手指所划过的像素数
velocityTracker.computeCurrentVelocity(1000);//时间间隔为1000ms;
int xVelocity = (int) velocityTracker.getXVelocity();
int yVelocity = (int) velocityTracker.getYVelocity();
2. GestureDetector
手势检测
首先须要建立一个GestureDetector对象并实现OnGestureListener接口
GestureDetector mGestureDetector = new GestureDetector(this);
//解决长按屏幕后没法拖动的现象
mGestureDetector.setIsLongpressEnabled(false);
接着,接管目标View的onTouchEvent方法,在待监听View的onTouchEvent方法中实现
boolean consume = mGestureDetector.onTouchEvent(event);
retrun consume;
3. Scroller
弹性滑动对象,用于实现View的弹性滑动,使用scrollTo/scrollBy方法来进行滑动时,其过程是瞬间完成的.
3.2 View的滑动
3.2.1 使用scrollTo/scrollBy
scrollBy实际上也是调用了scrollTo方法,scrollBy它实现基于当前位置的相对滑动,scrollTo是基于所传递参数的绝对滑动
View内部的两个属性mScrollX(等于View左边缘和View内容左边缘在水平方向的距离)和mScrollY(等于View的上边缘和View内容上边缘在竖直方向的距离)的改变规则,经过getScrollX和getScrollY得到
从左往右滑动,mScrollX为负值,反之为正值;若是从上往下滑动,那么mScrollY为负值,反之为正值.
3.2.2 使用动画
使用View动画,可是会形成View的位置没有改变,动画完成后,View仍是会停在原来的位置,因此要用属性动画
3.2.3 改变布局参数
布局参数:LayoutParams
MarginLayoutParams params = (MarginLayoutParams) mButton1.getLayoutParams(); params.width += 100; params.leftHeight += 100; mButton1.requestLayout(); //或者mButton1.setLayoutParams(params);
3.3 弹性滑动
3.3.1 使用scroller
Scroller Scroller = new Scroller(mContext); //缓慢滚动到指定位置 private void smoothScrollTo(int destX, int destY){ int scrollX = getScrollX(); int deltaX = destX - scrollX; //1000ms内滑向的destX,效果就是慢慢滑动 mScroller.startScroller(scrollX, 0, deltaX, 0, 1000); invalidate(); } @Override public void computeScroller(){ if (mScroller.computeScrollOfset()) { mscrollTo(mScroller.getScrollX, mScroller.getCurrY()); postInvalidate(); } }
Scroller自己并不能实现view的滑动,他须要配合View的computeScroll方法才能完成弹性滑动的效果它不断让view重绘,每一次重绘距滑动起始时间会有一个时间间隔,经过这个时间间隔就能够得出View当前的滑动位置,知道了滑动位置就能够经过scrollTo方法完成View的滑动.
3.3.2 经过动画
动画ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(1000).start();
3.3.3 使用延时策略
使用Handler发送消息sendMessage(),在handleMessage()方法中进行view的绘制
3.4 View的事件分发机制
3.4.1 点击事件的传递规则
有三个方法:
public boolean dispatchTouchEvent(MotionEvent ev):用来进行事件的分发.若是事件可以传递给当前View,那么次方法必定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件.
public boolean onInterceptTouchEvent(MotionEvent event):用来判断是否拦截某个事件,若是当前view拦截了某个事件,那么在同一个事件序列当中,此方法不会再次调用,返回结果表示是否拦截当前事件
public boolean onTouchEvent(MotionEvent event):在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,若是不消耗,则在同一事件序列中,当前View没法再次接收到事件.
一个点击事件产生后,它的传递过程遵循以下顺序:Activity->Window->View.
3.4.2 事件分发
1.Activity对点击事件的分发过程
点击事件用MotionEvent来表示,事件最早传递给当前Activity,由Activity的dispatchTouchEvent来进行事件派发,具体的工做是有Activity内部的Window来完成的.
Window会将事件传递给decor view ,decor view 通常就是当前界面的底层容器,经过Activity.getWindow.getDecorView()能够得到.
Window是一个抽象类,实现类是PhoneWindow,PhoneWindow将时间直接传递给DecorView
经过((ViewGroup)getWindow().getDecorView().findViewById(android.R.id.content)).getChildAt(0)这种方式获取Activity所设置的View
而咱们经过setContentView设置的View是它的一个子View.
2. 顶级View对点击事件的分发过程
点击事件到达顶级View(通常是一个ViewGroup)之后,会调用ViewGroup的dispatchTouchEvent方法
若是顶级ViewGroup拦截事件,即onInterceptTouchEvent返回true,则事件由ViewGroup处理,这时若是ViewGroup的mOnTouchListener被设置,则onTouchEvent会被调用,不然onTouchEvent会被调用.
onTouch会屏蔽掉onTouchEvent,
在onTouchEvent中,若是设置了mOnClickListener,则onClick会被调用.
若是顶级View不拦截,则事件会传递给它所在的点击事件链上的子View.
3. View对点击事件的处理过程
首先会判断有没有设置OnTouchListener,若是OnTouchListener中的onTouch方法返回true,那么onTouchEvent就不会被调用,OnTouchListener的优先级高于onTouchEvent
只要View的CLICKABLE和LONG_CLICKABLE有一个为true,那么他就会消耗这个事件,即onTouchEvent方法会返回true
3.5 View的滑动冲突
1. 外部滑动方向跟内部滑动方向不一致
2.外部滑动方向和内部滑动方向一致
3.上面两种状况的嵌套
3.5.2 滑动冲突的处理规则
根据滑动时水平滑动仍是竖直滑动来判断到底由谁来拦截事件
根据具体的业务寻找具体规则
3.5.3 滑动冲突的解决方法
1.外部拦截法
点击事件都先通过父容器的拦截处理,若是父容器须要此事件被拦截,若是不须要此事件就不拦截
外部拦截发须要重写父容器的onInterceptTouchEvent方法,在内部作相应的拦截便可
须要拦截,intercepted返回true,不拦截返回false
2.内部拦截法
指父容器不拦截任何事件,全部的事件都传递给子元素,若是子元素须要此事件就直接消耗掉,不然就交由父容器进行处理,须要配合requestDisallowInterptTouchEvent方法
须要重写子元素的dispatchTouchEvent方法.