Scroller滑动相关类使用


    话接上文,在前一篇文章里面,咱们一起分析了“知乎”的回答详情页的需求,然后顺便用代码实现了下,忘了的可以再去看看【凯子哥带你夯实应用层】都说“知乎”逼格高,我们来实现“知乎”回答详情页动画效果 。其实在很多的界面效果中,这种“滚动”的效果能带来很多的惊喜,各种效果也很有搞头,说不定什么时候,Boss看着哪个界面好看,就让你去仿个过来,你要是说不会,那你下个月的工资还想发不!所以呢,今天这篇文章,就结合着一些案例,来稍微系统的总结一下Android系统中,如果要实现界面滚动,所涉及到的几个常用类。

    Scroller和OverScroller,这两个是AndroidUI框架下实现滚动效果的最关键的类,ScrollView内部的实现也是使用的OverScroller,所以熟练的使用这两个类的相关API,可以让我们满足大部分的开发需求。

    在View类里面,有两个和滚动相关的类,scrollTo()和scrollBy。这两个方法可以实现View内容的移动,注意,是内容,不是位置!是移动,不是滚动!什么叫做内容呢?比如说一个TextView,如果使用scrollTo(),那么移动的是里面的文字,而不是位置,scrollBy()也是一样的。那么为什么是移动,不是滚动呢?这是因为这两个方法完成的都是瞬间完成,即瞬移,而不是带有滚动过程的滚动,所以说,如果要实现效果比较好的滚动,光靠View自带的方法还是不行滴,还是要Scrollers出马~

    但是!Scrollers并不是控制View进行滚动,包括内容或者是位置,实际上,Scrollers只是一个控件移动轨迹的辅助计算类,如果你想滚,他能帮你计算什么时间应该滚到什么位置,但是滚不滚,全靠你自觉~所以说,滚动位置由Scrollers计算出来了,我们在什么时候滚呢?滚多少呢?这时候,就要View的一个回调函数computeScroll()出马了。

    我们看看View里面的computeScroll()做了些什么

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * Called by a parent to request that a child update its values for mScrollX 
  3.      * and mScrollY if necessary. This will typically be done if the child is 
  4.      * animating a scroll using a {@link android.widget.Scroller Scroller} 
  5.      * object. 
  6.      */  
  7.     public void computeScroll() {  
  8.     }  

     duang!空的!不过没事,看看注释,就是说,如果我们用Scroller实现一个滚动动画的时候,这个方法就会被调用,被谁调用呢?parent,谁改变呢?child。所以一般来说,这个方法可以用来更新mScrollX和mScrollY,但是其实不光可以改变这些,我们还能做其他事情。

    如果我们调用Scroller.startScroll(int startX, int startY, int dx, int dy),那么我们就可以在computeScroll()里面执行实际的操作了,就像下面这样

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.     public void computeScroll() {  
  3.   
  4.         // 先判断mScroller滚动是否完成  
  5.         if (mScroller.computeScrollOffset()) {  
  6.             // 这里调用View的scrollTo()完成实际的滚动  
  7.             scrollTo( mScroller.getCurrX(), mScroller .getCurrY());  
  8.             // 必须调用该方法,否则不一定能看到滚动效果  
  9.             invalidate();  
  10.         }  
  11.         super.computeScroll();  
  12.     }  

     Scroller.computeScrollOffset方法是来判断滚动过程是否完成的,如果没有完成,就需要不停的scrollTo下去,所以在最后需要加一个invalidate(),这样可以再次触发computScroll,直到滚动已经结束。


    其实说到这里,有的同学可能比较迷惑,OverScroller和Scroller有什么区别呢?事实上,这两个类都属于Scrollers,Scroller出现的比较早,在API1就有了,OverScroller是在API9才添加上的,出现的比较晚,所以功能比较完善,Over的意思就是超出,即OverScroller提供了对超出滑动边界的情况的处理,这两个类80%的API是一致的,OverScroller比Scroller添加了一下几个方法

    ☞ isOverScrolled()
    ☞ 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)
    ☞ notifyVerticalEdgeReached(int startY, int finalY, int overY)

    从名字也能看出来,都是对Over功能的支持,其他的API都一样,所以介绍通用API的时候,并不区分OverScroller和Scroller。


    下面简单介绍一下常用的API。

    ☞ computeScrollOffset() 这个就是来判断当前的滑动动作是否完成的,用法很单一,就是在computeScroll()里面来做判断滚动是否完成

    ☞ getCurrX() 这个就是获取当前滑动的坐标值,因为Scrollers只是一个辅助计算类,所以如果我们想获取滑动时的时时坐标,就可以通过这个方法获得,然后在computeScroll()里面调用

    ☞ getFinalX() 这个是用来获取最终滑动停止时的坐标

    ☞ isFinished() 用来判断当前滚动是否结束

    ☞ startScroll(int startX, int startY, int dx, int dy) 用来开始滚动,这个是很重要的一个触发computeScroll()的方法,调用这个方法之后,我们就可以在computeScroll里面获取滚动的信息,然后完成我们的需要。这个还有一个带有滚动持续时间的重载函数,可以根据需求自由使用。特别要注意这四个参数,startX和startY是开始的坐标位置,正数左上,负数右下,dx、dy同理,当在computeScroll()获取getCurrX()的时候,变化范围就与这里地设置有关。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.     * Start scrolling by providing a starting point and the distance to travel. 
  3.     * The scroll will use the default value of 250 milliseconds for the 
  4.     * duration. 
  5.     *  
  6.     * @param startX Starting horizontal scroll offset in pixels. Positive 
  7.     *        numbers will scroll the content to the left. 
  8.     * @param startY Starting vertical scroll offset in pixels. Positive numbers 
  9.     *        will scroll the content up. 
  10.     * @param dx Horizontal distance to travel. Positive numbers will scroll the 
  11.     *        content to the left. 
  12.     * @param dy Vertical distance to travel. Positive numbers will scroll the 
  13.     *        content up. 
  14.     */  
  15.    public void startScroll(int startX, int startY, int dx, int dy) {  
  16.        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);  
  17.    }  
    ☞ fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) 这个方法也很重要,如果你想实现滑动之后,布局能够根据移动速度,慢慢减速的话,就需要用这个来实现,这里需要加速度的参数,我们可以通过VelocityTracker这个类来获取,然后使用,具体参数函数,在下面的实例中进行说明。


    说了这么多东西,都是最基础的,也是最没意思的,下面通过几个小例子,我们来简单地使用以下这些API,加深理解。

    因为gif帧率太低,不能很好地展示效果,所以我录取了一个视频,请大家戳这里(演示视频)查看演示视频,选择720P高清播放。

    顺便贴一下代码,在后面对代码进行解读。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void click(View view) {  
  2.   
  3.         switch (view.getId()) {  
  4.             case R.id.btn_scroll_to:  
  5.                 textView.scrollTo(distance, 0);  
  6.                 distance += 10;  
  7.                 break;  
  8.             case R.id.btn_scroll_by:  
  9.                 textView.scrollBy(300);  
  10.                 break;  
  11.             case R.id.btn_sping_back:  
  12.                 //不知道为什么第一次调用会贴墙,即到达x=0的位置  
  13.                 textView.spingBack();  
  14.                 break;  
  15.         }  
  16.   
  17.     }  

     首先点击了scrollBy()三次,这个函数是相对坐标移动,与当前坐标无关,而scrollTo()则是绝对坐标移动,如果distance相同的话,第二次就不会移动了,其实scrollBy()在源码上也是scrollTo()

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Move the scrolled position of your view. This will cause a call to 
  3.  * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  4.  * invalidated. 
  5.  * @param x the amount of pixels to scroll by horizontally 
  6.  * @param y the amount of pixels to scroll by vertically 
  7.  */  
  8. public void scrollBy(int x, int y) {  
  9.     scrollTo(mScrollX + x, mScrollY + y);  
  10. }  

     这样就明白为什么3次scrollBy()之后,调用scrollTo()之后,内容会移动回来了,以为前面的移动是30*3=90,而scrollTo()第一次调用distance是30,所以坐标就回来了,视觉上就是后退回来。

    第三个拖拽回弹效果用的是一个自定义控件,下面我们会详细的分析实现。

    第四个效果是spingBack(),即OverScroller的回弹效果,我们顺便也介绍了。

    OK,咱们开始介绍这个可以回弹的自定义TextView是如何实现这种效果的。

    下面是实现的代码

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Created by zhaokaiqiang on 15/2/28. 
  3.  */  
  4. public class JellyTextView extends TextView {  
  5.   
  6.     private OverScroller mScroller;  
  7.   
  8.     private float lastX;  
  9.     private float lastY;  
  10.   
  11.     private float startX;  
  12.     private float startY;  
  13.   
  14.     public JellyTextView(Context context, AttributeSet attrs) {  
  15.         super(context, attrs);  
  16.         mScroller = new OverScroller(context, new BounceInterpolator());  
  17.     }  
  18.   
  19.   
  20.     @Override  
  21.     public boolean onTouchEvent(MotionEvent event) {  
  22.   
  23.         switch (event.getAction()) {  
  24.             case MotionEvent.ACTION_DOWN:  
  25.                 lastX = event.getRawX();  
  26.                 lastY = event.getRawY();  
  27.                 break;  
  28.             case MotionEvent.ACTION_MOVE:  
  29.                 float disX = event.getRawX() - lastX;  
  30.                 float disY = event.getRawY() - lastY;  
  31.   
  32.                 offsetLeftAndRight((int) disX);  
  33.                 offsetTopAndBottom((int) disY);  
  34.                 lastX = event.getRawX();  
  35.                 lastY = event.getRawY();  
  36.                 break;  
  37.             case MotionEvent.ACTION_UP:  
  38.                 mScroller.startScroll((int) getX(), (int) getY(), -(int) (getX() - startX),  
  39.                         -(int) (getY() - startY));  
  40.                 invalidate();  
  41.                 break;  
  42.         }  
  43.   
  44.         return super.onTouchEvent(event);  
  45.     }  
  46.   
  47.   
  48.     @Override  
  49.     public void computeScroll() {  
  50.   
  51.         if (mScroller.computeScrollOffset()) {  
  52.             setX(mScroller.getCurrX());  
  53.             setY(mScroller.getCurrY());  
  54.             invalidate();  
  55.         }  
  56.   
  57.     }  
  58.   
  59.     @Override  
  60.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  61.         super.onSizeChanged(w, h, oldw, oldh);  
  62.         startX = getX();  
  63.         startY = getY();  
  64.     }  
  65.   
  66.     public void spingBack() {  
  67.   
  68.         if (mScroller.springBack((int) getX(), (int) getY(), 0, (int) getX(), 0,  
  69.                 (int) getY() - 100)) {  
  70.             Log.d("TAG""getX()=" + getX() + "__getY()=" + getY());  
  71.             invalidate();  
  72.         }  
  73.   
  74.     }  
  75.   
  76.   
  77. }  

     代码不到一百行,是不是很简单呀,实现的效果是类似果冻的颤动效果,来来来,凯子哥带你分析下代码实现。

    首先我们用的是OverScroller,因为和Scroller非常类似,而且增加了回弹支持,所以大部分情况下我们都可以使用OverScroller。我们在构造函数完成初始化,然后因为我们需要记录最开始的位置,在回弹的时候需要用,所以在onSizeChange()完成了起始坐标的初始化。为了完成拖拽功能,我们需要重写onTouch,然后在MOVE事件中,完成控件的位置移动,用offsetLeftAndRight和offsetTopAndBottom即可,参数是一个相对位移的距离,所以很简单就完成了控件跟随手指移动的效果。

    最后的效果当然是控件回弹,但是这里的回弹并不是用spingBack()完成,而是通过startScroll()完成,只要设置好当前的位置和我们需要位移的距离,然后记住invalidate一下,我们就可以去computeScroll()里面实际的改变控件的位置了,通过getCurrX()就可以获取到如果当前滚动应该的位置,所以setX()就OK啦,很简单是不是?不过要记住invalidate(),这样才能继续往下触发未完成的滚动操作。

    另外发现没,这个控件叫JellyTextView,就是果冻TextView,因为实现的是有来回颤动的效果,这个怎么实现呢?也很简单,设置一个BounceInterpolation就可以了,so easy~

    OK,其实现在大部分的Scroller的用法我们都用过了,还剩下一个OverScroll特有的spingBack()和fling(),我们先介绍一个spingBack的用法。

    springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) 

    看上面的参数,前两个是开始位置,是绝对坐标,minX和maxX是用来设定滚动范围的,也是绝对坐标范围,如果startX不在这个范围里面,比如大于maxX,就会触发computeScroll(),我们可以移动距离,最终回弹到maxX所在的位置,并返回true,从而完成后续的滚动效果,比minX小的话,就会回弹到minX,一样的道理。所以我们可以像上面代码里面一样,判断是否在范围内,在的话,就invalidate()一下,触发滚动动画,所以名字叫spingBack(),即回弹,在上面的视频里有演示效果。参照效果和代码,你应该能看明白用法。


    OK,分析完上面的代码,咱们就还有一个fling()没用了,这个代码咱们可以借助鸿洋的Android 自定义控件 轻松实现360软件详情页 里面用到了这个方法,我简单贴一下代码,不过下面代码经过了我的改造,添加了依附功能

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.     public boolean onTouchEvent(MotionEvent event) {  
  3.   
  4.         mVelocityTracker.addMovement(event);  
  5.         int action = event.getActionMasked();  
  6.         float y = event.getY();  
  7.   
  8.         switch (action) {  
  9.         case MotionEvent.ACTION_DOWN:  
  10.             if (!mScroller.isFinished())  
  11.                 mScroller.abortAnimation();  
  12.             mVelocityTracker.clear();  
  13.             mVelocityTracker.addMovement(event);  
  14.             mLastY = y;  
  15.             // return true;  
  16.             break;  
  17.         case MotionEvent.ACTION_MOVE:  
  18.             float dy = y - mLastY;  
  19.   
  20.             if (!mDragging && Math.abs(dy) > mTouchSlop) {  
  21.                 mDragging = true;  
  22.             }  
  23.             // 如果滑动的距离到达系统默认的最小值,就进行整体布局的移动  
  24.             if (mDragging) {  
  25.                 scrollBy(0, (int) -dy);  
  26.                 mLastY = y;  
  27.             }  
  28.             break;  
  29.         case MotionEvent.ACTION_CANCEL:  
  30.             mDragging = false;  
  31.             if (!mScroller.isFinished()) {  
  32.                 mScroller.abortAnimation();  
  33.             }  
  34.             break;  
  35.         case MotionEvent.ACTION_UP:  
  36.             mDragging = false;  
  37.             mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);  
  38.             int velocityY = (int) mVelocityTracker.getYVelocity();  
  39.             // 手指离开之后,根据加速度进行滑动  
  40.             if (Math.abs(velocityY) > mMinimumVelocity) {  
  41.                 fling(-velocityY);  
  42.             }  
  43.             mVelocityTracker.clear();  
  44.   
  45.             int currentY = getScrollY();  
  46.             // 下拉  
  47.             isDownSlide = (event.getY() - mFirstY) > 0;  
  48.   
  49.             if (isDownSlide) {  
  50.                 if (currentY < mTopViewHeight) {  
  51.                     Log.d(TAG, "下拉---下滑显示");  
  52.                     mScroller.startScroll(0, currentY, 0, -currentY);  
  53.                     invalidate();  
  54.                 }  
  55.             } else {  
  56.                 if (currentY > 0) {  
  57.                     Log.d(TAG, "上拉---上滑隐藏");  
  58.                     mScroller.startScroll(0, currentY, 0, mTopViewHeight  
  59.                             - currentY);  
  60.                     invalidate();  
  61.                 }  
  62.             }  
  63.             break;  
  64.         }  
  65.   
  66.         return super.onTouchEvent(event);  
  67.     }  

    fling()方法里面有一个加速度的参数,我们需要通过VelocityTracker来获取到加速度,VelocityTracker的用法很单一,就像上面一样,当滑动的速度大于最小速度之后,调用fling(),我们再看看fling()的代码


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void fling(int velocityY) {  
  2.         mScroller.fling(0, getScrollY(), 0, velocityY, 000, mTopViewHeight);  
  3.         invalidate();  
  4.     }  

  这个几个参数如下,在上面的用法中,mxaY是上部布局高度,所以就可以实现向上滑动的时候,松手后若加速度达到一定值,就能在布局不显示的时候停止,就完成了我们想要的效果。
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void fling(int startX, int startY, int velocityX, int velocityY,  
  2.             int minX, int maxX, int minY, int maxY)  

   不过为了更好的模拟360的布局效果,我对代码进行了一点修改,主要是增加了依附效果,即上部布局的依附,下面附上修改后的代码,有兴趣的可以试一下~

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.zhy.view;  
  2.   
  3. import android.content.Context;  
  4. import android.support.v4.app.Fragment;  
  5. import android.support.v4.app.FragmentPagerAdapter;  
  6. import android.support.v4.app.FragmentStatePagerAdapter;  
  7. import android.support.v4.view.PagerAdapter;  
  8. import android.support.v4.view.ViewPager;  
  9. import android.util.AttributeSet;  
  10. import android.util.Log;  
  11. import android.view.MotionEvent;  
  12. import android.view.VelocityTracker;  
  13. import android.view.View;  
  14. import android.view.ViewConfiguration;  
  15. import android.view.ViewGroup;  
  16. import android.widget.LinearLayout;  
  17. import android.widget.OverScroller;  
  18. import android.widget.ScrollView;  
  19.   
  20. import com.zhy.sample.StickyNavLayout.R;  
  21.   
  22. public class StickyNavLayout extends LinearLayout {  
  23.   
  24.     private static String TAG = "TAG";  
  25.   
  26.     /** 
  27.      * 最顶部的View 
  28.      */  
  29.     private View mTop;  
  30.     /** 
  31.      * 导航的View 
  32.      */  
  33.     private View mNav;  
  34.     private ViewPager mViewPager;  
  35.   
  36.     private int mTopViewHeight;  
  37.     private ScrollView mInnerScrollView;  
  38.     private boolean isTopHidden = false;  
  39.   
  40.     private OverScroller mScroller;  
  41.     private VelocityTracker mVelocityTracker;  
  42.     private int mTouchSlop;  
  43.     private int mMaximumVelocity, mMinimumVelocity;  
  44.   
  45.     private float mLastY;  
  46.     // Down时纪录的Y坐标  
  47.     private float mFirstY;  
  48.     // 是否是下拉  
  49.     private boolean isDownSlide;  
  50.   
  51.     private boolean mDragging;  
  52.   
  53.     public StickyNavLayout(Context context, AttributeSet attrs) {  
  54.         super(context, attrs);  
  55.         setOrientation(LinearLayout.VERTICAL);  
  56.   
  57.         mScroller = new OverScroller(context);  
  58.   
  59.         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
  60.         mMaximumVelocity = ViewConfiguration.get(context)  
  61.                 .getScaledMaximumFlingVelocity();  
  62.         mMinimumVelocity = ViewConfiguration.get(context)  
  63.                 .getScaledMinimumFlingVelocity();  
  64.   
  65.         mVelocityTracker = VelocityTracker.obtain();  
  66.   
  67.     }  
  68.   
  69.     @Override  
  70.     protected void onFinishInflate() {  
  71.         super.onFinishInflate();  
  72.         mTop = findViewById(R.id.id_stickynavlayout_topview);  
  73.         mNav = findViewById(R.id.id_stickynavlayout_indicator);  
  74.         View view = findViewById(R.id.id_stickynavlayout_viewpager);  
  75.         if (!(view instanceof ViewPager)) {  
  76.             throw new RuntimeException(  
  77.                     "id_stickynavlayout_viewpager show used by ViewPager !");  
  78.         }  
  79.         mViewPager = (ViewPager) view;  
  80.     }  
  81.   
  82.     @Override  
  83.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  84.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  85.         // 这是为了设置ViewPager的高度,保证TopView消失之后,能够正好和NavView填充整个屏幕  
  86.         ViewGroup.LayoutParams params = mViewPager.getLayoutParams();  
  87.         params.height = getMeasuredHeight() - mNav.getMeasuredHeight();  
  88.     }  
  89.   
  90.     @Override  
  91.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  92.         super.onSizeChanged(w, h, oldw, oldh);  
  93.         mTopViewHeight = mTop.getMeasuredHeight();  
  94.     }  
  95.   
  96.     @Override  
  97.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  98.         int action = ev.getAction();  
  99.         float y = ev.getY();  
  100.   
  101.         switch (action) {  
  102.         case MotionEvent.ACTION_DOWN:  
  103.             mLastY = y;  
  104.             mFirstY = y;  
  105.             break;  
  106.         case MotionEvent.ACTION_MOVE:  
  107.             float dy = y - mLastY;  
  108.             getCurrentScrollView();  
  109.             if (Math.abs(dy) > mTouchSlop) {  
  110.                 mDragging = true;  
  111.                 // 如果Top的View是显示状态,或者是Fragment位于最上面的位置的时候,就拦截  
  112.                 if (!isTopHidden  
  113.                         || (mInnerScrollView.getScrollY() == 0 && isTopHidden && dy > 0)) {  
  114.                     Log.d(TAG, "-----触摸拦截");  
  115.                     return true;  
  116.                 }  
  117.             }  
  118.             break;  
  119.         }  
  120.         return super.onInterceptTouchEvent(ev);  
  121.     }  
  122.   
  123.     /** 
  124.      * 获取当前布局里面的ScrollView 
  125.      */  
  126.     private void getCurrentScrollView() {  
  127.   
  128.         int currentItem = mViewPager.getCurrentItem();  
  129.         PagerAdapter a = mViewPager.getAdapter();  
  130.         if (a instanceof FragmentPagerAdapter) {  
  131.             FragmentPagerAdapter fadapter = (FragmentPagerAdapter) a;  
  132.             Fragment item = fadapter.getItem(currentItem);  
  133.             mInnerScrollView = (ScrollView) (item.getView()  
  134.                     .findViewById(R.id.id_stickynavlayout_innerscrollview));  
  135.         } else if (a instanceof FragmentStatePagerAdapter) {  
  136.             FragmentStatePagerAdapter fsAdapter = (FragmentStatePagerAdapter) a;  
  137.             Fragment item = fsAdapter.getItem(currentItem);  
  138.             mInnerScrollView = (ScrollView) (item.getView()  
  139.                     .findViewById(R.id.id_stickynavlayout_innerscrollview));  
  140.         }  
  141.   
  142.     }  
  143.   
  144.     @Override  
  145.     public boolean onTouchEvent(MotionEvent event) {  
  146.   
  147.         mVelocityTracker.addMovement(event);  
  148.         int action = event.getActionMasked();  
  149.         float y = event.getY();  
  150.   
  151.         switch (action) {  
  152.         case MotionEvent.ACTION_DOWN:  
  153.             if (!mScroller.isFinished())  
  154.                 mScroller.abortAnimation();  
  155.             mVelocityTracker.clear();  
  156.             mVelocityTracker.addMovement(event);  
  157.             mLastY = y;  
  158.             // return true;  
  159.             break;  
  160.         case MotionEvent.ACTION_MOVE:  
  161.             float dy = y - mLastY;  
  162.   
  163.             if (!mDragging && Math.abs(dy) > mTouchSlop) {  
  164.                 mDragging = true;  
  165.             }  
  166.             // 如果滑动的距离到达系统默认的最小值,就进行整体布局的移动  
  167.             if (mDragging) {  
  168.                 scrollBy(0, (int) -dy);  
  169.                 mLastY = y;  
  170.             }  
  171.             break;  
  172.         case MotionEvent.ACTION_CANCEL:  
  173.             mDragging = false;  
  174.             if (!mScroller.isFinished()) {  
  175.                 mScroller.abortAnimation();  
  176.             }  
  177.             break;  
  178.         case MotionEvent.ACTION_UP:  
  179.             mDragging = false;  
  180.             mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);  
  181.             int velocityY = (int) mVelocityTracker.getYVelocity();  
  182.             // 手指离开之后,根据加速度进行滑动  
  183.             if (Math.abs(velocityY) > mMinimumVelocity) {  
  184.                 fling(-velocityY);  
  185.             }  
  186.             mVelocityTracker.clear();  
  187.   
  188.             int currentY = getScrollY();  
  189.             // 下拉  
  190.             isDownSlide = (event.getY() - mFirstY) > 0;  
  191.   
  192.             if (isDownSlide) {  
  193.                 if (currentY < mTopViewHeight) {  
  194.                     Log.d(TAG, "下拉---下滑显示");  
  195.                     mScroller.startScroll(0, currentY, 0, -currentY);  
  196.                     invalidate();  
  197.                 }  
  198.             } else {  
  199.                 if (currentY > 0) {  
  200.                     Log.d(TAG, "上拉---上滑隐藏");  
  201.                     mScroller.startScroll(0, currentY, 0, mTopViewHeight  
  202.                             - currentY);  
  203.                     invalidate();  
  204.                 }  
  205.             }  
  206.             break;  
  207.         }  
  208.   
  209.         return super.onTouchEvent(event);  
  210.     }  
  211.   
  212.     public void fling(int velocityY) {  
  213.         mScroller.fling(0, getScrollY(), 0, velocityY, 000, mTopViewHeight);  
  214.         invalidate();  
  215.     }  
  216.   
  217.     @Override  
  218.     public void scrollTo(int x, int y) {  
  219.         if (y < 0) {  
  220.             y = 0;  
  221.         }  
  222.         if (y > mTopViewHeight) {  
  223.             y = mTopViewHeight;  
  224.         }  
  225.         if (y != getScrollY()) {  
  226.             super.scrollTo(x, y);  
  227.         }  
  228.   
  229.         isTopHidden = getScrollY() == mTopViewHeight;  
  230.   
  231.     }  
  232.   
  233.     @Override  
  234.     public void computeScroll() {  
  235.         if (mScroller.computeScrollOffset()) {  
  236.             scrollTo(0, mScroller.getCurrY());  
  237.             invalidate();  
  238.         }  
  239.     }  
  240.   
  241. }  


    OK,关于滚动的这些东西基本上就这些吧,不过也都是最基础的,如果能熟悉的运用这些API,就能创造出非常棒的用户体验,大家快来一起滚啊~~


演示代码下载:https://github.com/ZhaoKaiQiang/ScrollerDemo

转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值