触摸[6] OverScroller

【参考链接】

ScrollViewListView剖析 -上下拉伸回弹阻尼效果http://www.jianshu.com/p/834e522d02dc

 

前面讲到了Scroller,这里就不得不讲下OverScroller

系统自带的ScrollView,只能在内容范围内滑动(在滑动到达边界时会有发光效果)



但是实际上,ScrollView内部已经实现了 能够使滑动超出内容边界以后继续滑动一段距离,并且手指松开以后可以回弹。只不过可能因为专利的原因,将这段距离的大小设置成了0,所以默认没有这个效果。

我们可以重写ScrollViewoverScrollBy()方法来加入这个效果。

@Override
protected booleanoverScrollBy(intdeltaX, intdeltaY, intscrollX, intscrollY, intscrollRangeX, intscrollRangeY, intmaxOverScrollX, intmaxOverScrollY, booleanisTouchEvent){
    Log.e(
"shadowfaxghh","MyScrollView overScrollBy()");

   
//maxOverScrollY赋值100
   
return super.overScrollBy(deltaX,deltaY,scrollX,scrollY,scrollRangeX,scrollRangeY,0,100,isTouchEvent);
}




这是为什么呢?

其实借助于ScrollViewOverScroller类。

OverScrollerScroller的增强类,既有ScrollerstartScroll()fling()方法,还提供了springBack()方法

下面通过分析ScrollView滑动处理的源码流程,搞清楚OverScroller各个方法的含义和功能。

基于android-2.3.3_r1源码

 


所以,其实就是

View提供了overScrollBy()方法,预留了onOverScrolled()方法用于重写。当调用overScrollBy()的时候,会调用到onOverScrolled(),自己的View可以重写onOverScrolled()进行处理

OverScrollerspringBack()方法其实只在超出滑动范围时起作用,设置数据和启动标志位,用于产生回弹效果。

(其实startScroll()fling()也只是在设置数据和启动标志位)

 

以如下场景为例



当向上滑动超出滑动范围,但未超出额外距离时松手,日志如下



当向上滑动超出滑动范围,并且超出额外距离时松手,日志如下


 

借助于上面ScrollView的思路,我们可以给自己的ViewGroup增加OverScroll效果。主要方法为

1、ACTION_MOVE中调用overScrollBy()方法,在ACTION_UP中调用OverScrollerspringBack()方法

2、重写onOverScrolled()

3、重写computeScroll()

 

在前面例子的基础上,不使用Scroller而使用OverScroller,实现滑动时的overScroll效果

 

public classMyFrameLayoutextendsFrameLayout {

   
//内容的最大值、最小值
   
private intminX=0;
    private int
maxX=0;

   
//滑动坐标的最大值、最小值//不加入额外距离
   
private intminScrollX;
    private int
maxScrollX;

   
//x方向上的额外距离
   
private intoverScrollX=200;

    private int
lastX;

   
VelocityTrackermVelocityTracker;
    int
scaledMaximumFlingVelocity;
    int
scaledMinimumFlingVelocity;

    private
OverScrollermOverScroller;

    public
MyFrameLayout(Context context) {
       
super(context);
       
init();
   
}

   
publicMyFrameLayout(Context context,AttributeSet attrs) {
       
super(context,attrs);
       
init();
   
}

   
publicMyFrameLayout(Context context,AttributeSet attrs, intdefStyleAttr) {
       
super(context,attrs,defStyleAttr);
       
init();
   
}

   
public voidinit() {
       ViewConfiguration configuration = ViewConfiguration.get(getContext())
;
       
scaledMinimumFlingVelocity=configuration.getScaledMinimumFlingVelocity();
       
scaledMaximumFlingVelocity=configuration.getScaledMaximumFlingVelocity();
       
Log.e("shadowfaxghh","scaledMinimumFlingVelocity="+scaledMinimumFlingVelocity+" scaledMaximumFlingVelocity="+scaledMaximumFlingVelocity);

       
mOverScroller=newOverScroller(getContext());
   
}

   
@Override
   
protected voidonMeasure(intwidthMeasureSpec, intheightMeasureSpec) {
       
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
   
}

   
@Override
   
protected voidonLayout(booleanchanged, intleft, inttop, intright, intbottom) {
       
super.onLayout(changed,left,top,right,bottom);

       
//计算出内容的最大值最小值
       
for(inti =0;i < getChildCount();i++) {
           View childView = getChildAt(i)
;

           if
(minX> childView.getLeft())
               
minX= childView.getLeft();

           if
(maxX< childView.getRight())
               
maxX= childView.getRight();
       
}

       
//计算出滑动坐标的最大值、最小值//只有内容大小超出自身大小时,才能进行滑动
       
if(minX<0)
           
minScrollX=minX;
       else
           
minScrollX=0;

       if
(maxX> getWidth())
           
maxScrollX=maxX- getWidth();
        else
           
maxScrollX=0;
   
}

   
@Override
   
public booleanonTouchEvent(MotionEvent event) {
       
super.onTouchEvent(event);

       switch
(event.getAction()){
           
caseMotionEvent.ACTION_DOWN:
//               Log.e("shadowfaxghh", "ACTION_DOWN");
               
lastX= (int) event.getRawX();

               
obtaionVelocityTracker();
               
mVelocityTracker.addMovement(event);

               if
(mOverScroller!=null& !mOverScroller.isFinished()) {
                   
mOverScroller.abortAnimation();
               
}
               
break;

           case
MotionEvent.ACTION_MOVE:
//               Log.e("shadowfaxghh", "ACTION_MOVE");
               
intnewX = (int) event.getRawX();
               int
deltaX = newX -lastX;

               
//这里的deltaX要取反,是根据滑动事件计算出来的//跟屏幕坐标系方向相同
               
overScrollBy(-deltaX,0,getScrollX(),getScrollY(),maxScrollX,100,overScrollX,0, true);

               
//更新坐标
               
lastX= newX;

               
obtaionVelocityTracker();
               
mVelocityTracker.addMovement(event);
               break;

           case
MotionEvent.ACTION_UP:
           
caseMotionEvent.ACTION_CANCEL:
               Log.e(
"shadowfaxghh","ACTION_UP");
               
lastX= (int) event.getRawX();

               if
(mVelocityTracker!=null) {
                   
//计算速度
                   
mVelocityTracker.computeCurrentVelocity(1000,scaledMaximumFlingVelocity);

                   
//获取x方向的速度
                   
intxVelocity = (int)mVelocityTracker.getXVelocity();

                   if
(Math.abs(xVelocity)<3000) {
                       Log.e(
"shadowfaxghh","springBack()");
                       
mOverScroller.springBack(getScrollX(),0,minScrollX,maxScrollX,0,0);//只是设置了状态
                   
}else{
                       Log.e(
"shadowfaxghh","fling()");
                       
//原来OverScroller也重载的有带overScroll参数的fling()方法
                       
mOverScroller.fling(getScrollX(),0,-xVelocity,0,minScrollX,maxScrollX,0,0,overScrollX,0);
                   
}
                   invalidate()
;//需要触发重绘

                   
recycleVelocityTracker();
               
}
               
break;
           default
:
               
break;
       
}
       
return true;
   
}

   
@Override
   
public voidcomputeScroll() {
//       super.computeScroll();

       
if(mOverScroller!=null&&mOverScroller.computeScrollOffset()) {
           
intcurrX =mOverScroller.getCurrX();
           int
deltaX=currX -getScrollX();

           
//这里的deltaX不需要取反//OverScroller计算出来的
           
overScrollBy(deltaX,0,getScrollX(),0,maxScrollX,0,overScrollX,0, false);

           
invalidate();//继续触发重绘
       
}
    }

   
@Override
   
protected voidonOverScrolled(intscrollX, intscrollY, booleanclampedX, booleanclampedY) {
//       super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);//父类实现为空
       
if(mOverScroller!=null&& !mOverScroller.isFinished()) {

           
intoldScrollX = getScrollX();
           int
oldScrollY =getScrollY();
           
setScrollX(scrollX);//这两个方法需要API14及以上//其实也可以使用scrollTo()
           
setScrollY(scrollY);
           
onScrollChanged(scrollX,scrollY,oldScrollX,oldScrollY);

           if
(clampedX) {//针对fling()的情况
               
mOverScroller.springBack(getScrollX(),0,minScrollX,maxScrollX,0,0);
           
}
       }
else{
           scrollTo(scrollX
,scrollY);
       
}
    }

   
private voidobtaionVelocityTracker() {
       
if(mVelocityTracker==null)
           
mVelocityTracker= VelocityTracker.obtain();
   
}

   
private voidrecycleVelocityTracker() {
       
if(mVelocityTracker!=null) {
           
mVelocityTracker.clear();
           
mVelocityTracker.recycle();
           
mVelocityTracker=null;
       
}
    }

   
@Override
   
protected voidonScrollChanged(intl, intt, intoldl, intoldt) {
       
super.onScrollChanged(l,t,oldl,oldt);
    
}
}

 

<?xml version="1.0"encoding="utf-8"?>
<com.example.shadowfaxghh.demo.MyFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
   
xmlns:tools="http://schemas.android.com/tools"
   
android:layout_width="match_parent"
   
android:layout_height="match_parent"
   
android:overScrollMode="always"
   
tools:context="com.example.shadowfaxghh.demo.MainActivity">

    <TextView
       
android:layout_width="300px"
       
android:layout_height="wrap_content"
       
android:text="0"
        
android:textColor="#000000"
       
android:background="#FFFFFF"
       
android:layout_marginLeft="0px"
       
/>
   
。。。省略一些TextView。。。
    <TextView
       
android:layout_width="300px"
       
android:layout_height="wrap_content"
       
android:text="12"
       
android:textColor="#000000"
       
android:background="#FFFFFF"
       
android:layout_marginLeft="3600px"
       
/>
</com.example.shadowfaxghh.demo.MyFrameLayout>


需要设置overScrollMode=always,因为View.overScrollBy()方法中会判断只有设置了overScrollMode才会使用overScroll

也移除了第一个marginLeft=-300TextView,因为View.overScrollBy()方法中会认为滑动范围是0~scrollRange。要实现-300效果可能需要重写overScrollBy()方法。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值