《Android开发艺术探索》——View事件体系

QSNLin 7年前
   <p>自定义控件、滑动冲突解决</p>    <h2>View基础知识</h2>    <ol>     <li>View的位置参数</li>     <li>MotionEvent和TouchSlop对象</li>     <li>VelocityTracker</li>     <li>GestureDetector和Scroller对象</li>    </ol>    <h2>1. View的位置参数</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/07a53792ea46f4f7966feed15220a38f.png"></p>    <h2>2. MotionEvent和TouchSlop</h2>    <p>注意:各个方法相对目标不一样。</p>    <p>view获取自身坐标:getLeft(),getTop(),getRight(),getBottom()</p>    <p>view获取自身宽高:getHeight(),getWidth()</p>    <p>motionEvent获取坐标:getX(),getY(),getRawX(),getRawY()</p>    <ul>     <li>MotionEvent<br> Touch事件中,典型的事件有如下几种:      <ul>       <li>ACTION_DOWN —— 手指接触屏幕</li>       <li>ACTION_MOVE —— 手指在屏幕上移动</li>       <li>ACTION_UP —— 手指从屏幕离开</li>      </ul> </li>     <li>TouchSlop<br> TouchSlop是系统所能识别出的被认为是滑动的最小距离,换句话说,当手指在屏幕上滑动时,当两次滑动之间的距离小于这个常量,那么系统不认为是在进行滑动操作。通过以下方式获取这个常量值: <pre>  <code class="language-java">ViewConfiguration.get(getContext()).getScaledTouchSlop();</code></pre> 这个常量有神马意义呢?<br> 可以使用这个常量判断是否达到滑动条件,处理滑动时,做一些过滤。</li>    </ul>    <h2>3. VelocityTracker、GestureDetector和Scroller对象</h2>    <ul>     <li>VelocityTracker<br> 速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度。<br> 构造方法中初始化获取VelocityTracker对象 <pre>  <code class="language-java">VelocityTracker mVelocityTracker = VelocityTracker.obtain();</code></pre> 在 <strong>onTouchEvent</strong> 方法中添加追踪事件 <pre>  <code class="language-java">mVelocityTracker.addMovement(event);</code></pre> 在 <strong>ACTION_UP</strong> 事件中获取当前的速度。注意这里计算的是1000ms时间间隔移动的像素值,假设像素是100,即速度是每秒100像素。手指从右向左滑动,速度为负值。 <pre>  <code class="language-java">mVelocityTracker.computeCurrentVelocity(1000);  float xVelocity = mVelocityTracker.getXVelocity();  float yVelocity = mVelocityTracker.getYVelocity();</code></pre> 最后,当不需要它的时候需要调用 <strong>clear</strong> 方法来重置并回收内存。 <pre>  <code class="language-java">mVelocityTracker.clear();  mVelocityTracker.recycle();</code></pre> </li>     <li> <p>GestureDetecor</p> <p>手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。</p> <p>创建一个 <strong>GestureDetecor</strong> 对象并实现 <strong>OnGestureListener</strong> 接口,根据需要实现单击等方法:</p> <pre>  <code class="language-java">GestureDetector mGestureDetector = new GestureDetector(this);  // 解决长按屏幕后无法拖动的现象  mGestureDetector.setIsLongpressEnabled(false);</code></pre> <p>接管目标 <strong>View</strong> 的 <strong>onTouchEvent</strong> 方法,在待监听 <strong>View</strong> 的 <strong>onTouchEvent</strong> 方法中添加如下实现:</p> <pre>  <code class="language-java">boolean consume = mGestureDetector.onTouchEvent(event);  return consume;</code></pre> <p>建议:</p> <p>如果只是监听滑动操作,建议在onTouchEvent中实现;如果要监听双击这种行为,则使用GestureDetector 。</p> </li>     <li style="text-align:center">Scroller<br> 弹性滑动对象,用于实现View的弹性滑动。<br> View的scrollTo/scrollBy方法来滑动时,过程是瞬间完成的。使用Scroller则有过渡滑动的效果。注意,Scoller本身无法让View弹性滑动,它需要和View的computerScroller方法配合使用。<br> 构造方法初始化 <pre>  <code class="language-java">Scroller mScroller = new Scroller(getContext());</code></pre> 缓慢滑动到指定位置,一般在ACTION_UP 方法中执行,松手回弹效果。 <pre>  <code class="language-java">private void smoothScrollBy(int dx, int dy) {    mScroller.startScroll(getScrollX(), 0, dx, 0, 500);    invalidate();  }  @Override  public void computeScroll() {    if (mScroller.computeScrollOffset()) {        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());        postInvalidate();    }  }</code></pre> <strong>Scroller原理</strong><br> <img src="https://simg.open-open.com/show/43fbcc5556e465de7a30644303aa84c6.jpg"></li>    </ul>    <p>原理图</p>    <p>当在MotionEvent.ACTION_UP事件触发时,调用startScroll方法,并调用invalidate/postInvalidate方法,会导致View重绘,执行View.draw方法。在此方法中会调用View.computeScroll方法,此方法是空实现,需要我们自己处理逻辑。具体逻辑是:先判断computeScrollOffset,如果为true,表示滚动未结束。则执行scrollTo方法,再次调用postInvalidate,如此反复执行,直到结束。</p>    <p>computeScrollOffset方法计算了一小段时间间隔内偏移的距离,即CurrX,CurrY。并返回是否滚动结束的标记。true表示未结束,false表示结束。</p>    <p>View的scrollTo/scrollBy方法操作的View的内容滑动。</p>    <p>getScrollX返回的是View的左边缘到其内容左边缘的距离。相对于View的左边缘</p>    <p>getScrollY返回的是View的上边缘到其内容上边缘的距离。</p>    <p>如果View的内容向左滑,滑出View的左边界,getScrollX为正值,反之为负值。</p>    <p>如果View的内容向上滑,滑出View的上边界,getScrollY为正值,反之为负值。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/73ca77f5abacdb820c087a0c47fc743c.png"></p>    <p style="text-align:center">getScrollX和getScrollY的变化示意图</p>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/efcf275a7e00</p>    <p> </p>