PullScrollView详解(四)——完全使用listview实现下拉回弹(方法一)

2024-02-28 08:38

本文主要是介绍PullScrollView详解(四)——完全使用listview实现下拉回弹(方法一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在前面三篇中,我为大家展示了使用ScrollView实现下拉回弹的效果。但如果ScrollView里如果嵌套使用ListView就可能会出现问题,因为两者都会有滑动监听。操作起来可能会起冲突,然后解决了冲突问题,到后面页面性能也会很差强人意。即然如此,那我们就直接使用listview来实现下拉回弹的效果就好了。
在这篇中,我先给大家展示一种比较容易出效果的方法——重写overScrollBy()函数。在下一篇中,我们将模仿PullScrollView中的实现方式自己对OnTouchEvent()进行监听、操作。

注意注意!!!!本篇文章讲述的OverScrollBy(),大家应该把最大注意力放在《4、用途:捕捉当前listview是否到底或到顶》部分,至于下拉回弹,大家看看就好,OverScrollBy()实现的下拉回弹,bug一堆,根本无法实际运用到实际项目中,大家看看就好,下篇将带着大家利用OnTouchEvent()实现下拉回弹的效果。

一、setOverScrollMode()与OverScrollBy()
1、OverScrollBy()
OverScrollBy()是Android 9 之后才新增的API. 用于设定listview滚出屏幕后的回弹效果。先看OverScrollBy()函数的定义:

protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) 
它的参数多的有点让人模糊,下面给大家说下各个参数的意思。这些意思目前大家看不懂也没关系,先大致了解下,后面讲例子时还会再讲。
deltaX:当前X轴滑动的像素数
deltaY:当前Y轴滑动的像素数
scrollX:在加上deltaX值以前的X轴总的滑动量
scrollY:在加上deltaY值以前的Y轴总滑动量
scrollRangeX:
scrollRangeY:这两个什么意思,我也没弄懂
maxOverScrollX:X轴最大的OverScroll范围(最大可超过边界的像素)
maxOverscrollY:Y轴最大的OverScroll范围(最大可超过边界的像素)
isTouchEvent:当前overScrollBy函数的调用是否由Touch事件引起的
2、setOverScrollMode()
上面OverScrollBy()是当listview超过顶部或者底部的时候,会被调用。那定义listView能不能超过顶部或底部滑动,也就是说让不让OverScrollBy()调用,是通过setOverScrollMode()函数来定义的。

public void setOverScrollMode(int mode)
mode有三个取值:
//一直允许超过顶部/底部下拉
public static final int OVER_SCROLL_ALWAYS = 0;
//只有Content足够大到能scroll的时候,才允许超过顶部/底部下拉(系统默认值)
public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
//不允许超过顶部/底部下拉
public static final int OVER_SCROLL_NEVER = 2;
当然,我们也可以不设置setOverScrollMode(int mode),系统默认的滚动属性为OVER_SCROLL_IF_CONTENT_SCROLLS;即只有listview的content足够大到可以滚动的情况下,才允许超过顶部/底部下拉
这里大家应该对OverScrollBy()有个初步的认识了,下面我们先看看怎么实现下拉回弹,然后再讲解,为什么要这么做。
二、简单示例
先看下效果:

注意,overScrollBy()实现的回弹效果是不能设置下拉方向的,即,不但顶部下拉会回弹,在底部下拉时也会回弹。但从效果图中也可以看到,顶部下拉会回弹,底部下拉是不会回弹的,这是因为在最终代码中做了顶部还是底部判断,当在底部时,就不让用户上拉回弹了,至于怎么做到的,来一起看代码吧。

下面我们就通过一个最简单的例子来看下OverScrollBy()的用法及效果。

1、实现OverScrollView
首先,新建一个类OverScrollView,重写ListView
代码如下:
class OverScrollList extends ListView {
    //定义最大滚动高度
    int mContentMaxMoveHeight = 300;
 
    public OverScrollList(Context context) {
        super(context);
    }
 
    public OverScrollList(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    public OverScrollList(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
 
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
 
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mContentMaxMoveHeight, isTouchEvent);
    }
}

在这个类里面,只做了两件事:
第一:定义一下变量mContentMaxMoveHeight,来表示可滑动到最大高度
int mContentMaxMoveHeight = 300;
第二:重写overScrollBy函数,把return语句中的maxOverScrollY替换成我们定义的mContentMaxMoveHeight;
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
 
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mContentMaxMoveHeight, isTouchEvent);
    }
要实现可以overscroll的listview就需要做这么多,下面就是一如即往的往listview里填充数据的部分了。
2、ListView填充数据
在填充数据前,还是给大家看一下MainActivity的布局:(main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
        >
    <com.harvic.OverScrollDemo.OverScrollList
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
</LinearLayout>
下面就是正式填充数据的环节了。

先创建一个XML来做为Item的布局:(item_layout.xml)
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@android:id/text1"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:gravity="center_vertical"
          android:minHeight="40dp"
          android:background="#ffffff"/>
然后就是在MainActivity中的填充数据的部分了,代码如下 :
public class MainActivity extends Activity {
    private String[] mStrings = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
            "Allgauer Emmentaler", "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
            "Allgauer Emmentaler"};
    private LinkedList<String> mListItems;
    private OverScrollList mListView;
    private ArrayAdapter<String> mAdapter;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        mListView = (OverScrollList) findViewById(R.id.listview);
        //配置Adapter
        mListItems = new LinkedList<String>();
        mListItems.addAll(Arrays.asList(mStrings));
        mAdapter = new ArrayAdapter<String>(this,R.layout.item_layout, mListItems);
        
        mListView.setAdapter(mAdapter);
 
    }
}
这些都是有关ListView的最基本的部分了,没什么难度,也没什么讲解的必要。
源码在文章底部给出
3、疑问
(1)、为什么在OverScrollList中重写了overScrollBy后,还要设定 super.overScrollBy()里的maxOverScrollY值呢?

答:通过打日志,大家可以看到,overScrollBy里的maxOverScrollY的值一直是0!!!
也就是说,google给我们实现了overScrollBy函数,但需要我们自己设定可overScroll的最大值。不然overScroll的最大值默认是0,即不会上下拉动!!!

(2)、overScrollBy()函数什么时候会调?

答:在listview正常滑动时,overScrollBy()函数是不会被调用的,只有在超出ListView滑动界限时才会被调用。

4、用途:捕捉当前listview是否到底或到顶
现在我们先总结一下:
listview具不具有下拉反弹的功能是靠setOverScrollMode()来设定的。
具有下拉反弹功能以后下拉多少是靠 super.overScrollBy()里的maxOverScrollY值确定的。
overScrollBy()在listview到顶或到底时仍然下拉/上拉时才会被调用。
所以根据上面三点,我们就可以在listview中并不实现下拉回弹的效果时,判断当前Listview是否到顶或到底
(1)、简单计算方法

先列出代码如下:

protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
    if (deltaY>0){
        System.out.println("滑动到底端");
    }else if (deltaY < 0){
        System.out.println("滑动到顶端");
    }
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
很简单的原理:
1、首先,不改写super.overScrollBy()中的maxOverScrollY的值。因为maxOverScrollY默认是0,所以不会listview虽具有默认的滑动功能却不会滑动。
2、利用overScrollBy()只有在滑动到顶部、底部且超出滑动区域时,才会被调用的性质。当被overScrollBy调用时,肯定是到了顶部或者底部!!!所以,如果到了顶端,用户向下拉的时候overScrollBy才会被调用。此时手指向下滑动,deltaY必定小于0。同理,当listview到了底部时,只有用户向上拉的时候才会被调用,这时候手指肯定是向上移动的,所以deltaY肯定是大于0的。
deltaY的计算方法是用当前的触摸的手指位置减去上次捕捉到的手指位置。
(2)、系统给出的计算方法
看起来上面的方法好像是万无一失的,(其实我也没发现哪里会有问题),但系统研究了下源码,发现在View.java中的overScrollBy()的实现代码中有这么一段:

protected boolean overScrollBy(int deltaX, int deltaY,
        int scrollX, int scrollY,
        int scrollRangeX, int scrollRangeY,
        int maxOverScrollX, int maxOverScrollY,
        boolean isTouchEvent) {
    
    …………
 
int newScrollX = scrollX + deltaX;
int newScrollY = scrollY + deltaY;
 
    // Clamp values if at the limits and record
    final int left = -maxOverScrollX;
    final int right = maxOverScrollX + scrollRangeX;
    final int top = -maxOverScrollY;
    final int bottom = maxOverScrollY + scrollRangeY;
 
    boolean clampedX = false;
    if (newScrollX > right) {
        newScrollX = right;
        clampedX = true;
    } else if (newScrollX < left) {
        newScrollX = left;
        clampedX = true;
    }
 
    boolean clampedY = false;
    if (newScrollY > bottom) {
        newScrollY = bottom;
        clampedY = true;
    } else if (newScrollY < top) {
        newScrollY = top;
        clampedY = true;
    }
    …………
    return clampedX || clampedY;
}
我们把上拉、下拉的代码提取出来是这样的:
protected boolean overScrollBy(int deltaX, int deltaY,
        int scrollX, int scrollY,
        int scrollRangeX, int scrollRangeY,
        int maxOverScrollX, int maxOverScrollY,
        boolean isTouchEvent) {
    
    …………
int newScrollY = scrollY + deltaY;
 
    final int top = -maxOverScrollY;
    final int bottom = maxOverScrollY + scrollRangeY;
 
    boolean clampedY = false;
    if (newScrollY > bottom) {
        newScrollY = bottom;
        clampedY = true;
    } else if (newScrollY < top) {
        newScrollY = top;
        clampedY = true;
    }
    …………
    return clampedX || clampedY;
}
所以上面就是在overScrollBy中判断是否到顶、到底的方法,在OverScrollList中仿照一下,应该是这样写的:
protected boolean overScrollBy(int deltaX, int deltaY,
        int scrollX, int scrollY,
        int scrollRangeX, int scrollRangeY,
        int maxOverScrollX, int maxOverScrollY,
        boolean isTouchEvent) {
        
    int newScrollY = scrollY + deltaY;
    
    final int top = -maxOverScrollY;
    final int bottom = maxOverScrollY + scrollRangeY;
 
    if (newScrollY > bottom) {
        System.out.println("滑动到底端");
    } else if (newScrollY < top) {
         System.out.println("滑动到顶端");
    }
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
这才是正宗的判断是否到底、到顶的方法,至于为什么这么写,我也懒得研究了,无外乎就是弄懂scrollRangeY的含义而已。大家可以看看源码。其实,我觉得overScrollBy()的最大用途就是判断是否到底、到顶,至于什么下拉回弹,bug一堆!!!根本无法实际用到项目中!这里也只是给大家讲下这种实现方法而已,下篇我们会通过拦截OnTouchEvent()自己来实现下拉回弹。

三、带header的下拉回弹
这部分带着大家实现开篇的实现的效果。

1、listView添加透明header
(1)、概述

根据上面的效果图,我们直接来看看MainActivity的新布局代码:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">
    <ImageView
            android:id="@+id/background_img"
            android:layout_width="match_parent"
            android:layout_height="400dp"
            android:layout_marginTop="-100dp"
            android:scaleType="fitXY"
            android:src="@drawable/pic3" />
 
    <com.harvic.OverScrollDemo.OverScrollList
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:divider="@null"
            android:dividerPadding="0dp"
            android:dividerHeight="0dp"/>
</FrameLayout>
与前几篇一样,同样为ImageView添加layout_marginTop="-100dp",让其先向上移一部分,以防下拉时出现空白。
不一样的是,这里的OverScrollList控件,是全屏显示的,我们要怎么样把底部的小狗显示出来呢?
可能大家最先想到把底部的小狗图片给空出来的方法,就是给listview添加margin或者padding,那添加margin或者padding到底行不行呢?我们为OverScrollList添加一个android:paddingTop="100dp"看一下效果:

从效果图中可以看到,在上滑时,ListView没办法滑动到屏幕顶部。所以,我们唯一的解决方案就是为OverScrollList添加一个透明header来占据空间。

(2)、header布局

下面就为listview添加一个透明的header,布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="100dp">
</LinearLayout>
在这里什么都没有定义,只定义了一个高度。这里也可以定义背景是透明的,但如果listview有背景的话,依然会被盖住,所以要确保listview是没有背景的。不过我们不添加背景的话,默认就是透明的了。
(3)、MainActivity中添加header
给ListView添加Header很简单,直接调用addHeaderView(View v)就可以了。代码如下 :

public class MyActivity extends Activity {
    private String[] mStrings = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
            "Allgauer Emmentaler", "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
            "Allgauer Emmentaler"};
    private LinkedList<String> mListItems;
    private OverScrollListView mListView;
    private ArrayAdapter<String> mAdapter;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        mListView = (OverScrollListView) findViewById(R.id.listview);
 
        ImageView headerView = (ImageView)findViewById(R.id.background_img);
        mListView.setmHeaderView(headerView);
 
        mListItems = new LinkedList<String>();
        mListItems.addAll(Arrays.asList(mStrings));
        mAdapter = new ArrayAdapter<String>(this,R.layout.item_layout, mListItems);
 
        LayoutInflater inflater = getLayoutInflater();
        View view = inflater.inflate(R.layout.headerview,mListView,false);
        mListView.addHeaderView(view);
 
        mListView.setAdapter(mAdapter);
    }
}
其实最关键的就是这句:
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.headerview,mListView,false);
mListView.addHeaderView(view);
没什么难度,就是根据layout获取到view,然后将其设置给list
2、实现底部图片下拉回弹
(1)、OverScrollList中的实现

们现在能够通过重新OverScrollBy()函数让系统自已给我们实现ListView的下拉回弹,但底部的小狗图片可还是要我们自己实现下拉和回弹的。
非常庆幸的是,在OverScrollBy()的一堆参数中:
overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) 
有两个参数对我们来讲特别有用:
scrollY:在加上deltaY以前的滚动距离;(向上移动时为正值,向下移动时为负值)
deltaY:此次的手指移动距离(同样,向上移动时为正值,向下移动时为负值)
所以我们将scrollY与deltaY相加就可以得到当前的滚动距离了,由于在回弹时OverScrollBy()也会被调用,所以整个过程在下拉时newScrollY就会一直变到最大,而在回弹时,newScrollY会慢慢减为0;
所以我们根据这个特性,直接根据当前的滚动距离计算出当前小狗图片的位置即可
完整的代码如下:

/**
 * 阻尼系数,越小阻力就越大.
 */
public static final float SCROLL_RATIO = 0.25f;
private Rect mHeadInitRect = new Rect();
//设置topView
private View mTopView;
public void setTopView(View view) {
    mTopView = view;
}
//初始化TopView的原始位置
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        mHeadInitRect.set(mTopView.getLeft(), mTopView.getTop(), mTopView.getRight(), mTopView.getBottom());
    }
    return super.onInterceptTouchEvent(ev);
}
//随下拉滚动
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
 
    int headerMoveHeight = (int)Math.abs((scrollY + deltaY) * SCROLL_RATIO);
    int mHeaderCurTop = (int) (mHeadInitRect.top + headerMoveHeight);
    mTopView.layout(mHeadInitRect.left, mHeaderCurTop, mHeadInitRect.right, (int) (mHeadInitRect.bottom + headerMoveHeight));
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mContentMaxMoveHeight, isTouchEvent);
}
这里总共分为三部分:
第一:将topView设置进来:
//设置topView
private View mTopView;
public void setTopView(View view) {
    mTopView = view;
}
第二:在手指下按点击时,初始化mTopView的位置。
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        mHeadInitRect.set(mTopView.getLeft(), mTopView.getTop(), mTopView.getRight(), mTopView.getBottom());
    }
    return super.onInterceptTouchEvent(ev);
}
在第二篇中,我们有讲过为什么mTopView的位置初始化操作要放在onInterceptTouchEvent中,这里再絮叨一遍:因为,要获取mTopView的位置获取要在onLayout()以后才可以得到,也就是要在整个控件绘制完以后才能获取它的显示位置。所以在OverScrollList的构造函数中,是获取不到它的位置的。大家还记得《FlowLayout详解(一)——onMeasure()与onLayout()》   中说的那句吗:首先getMeasureWidth()方法在measure()过程结束后就可以获取到了,而getWidth()方法要在layout()过程结束后才能获取到.这里同样的道理。
第三:随下拉滚动
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
 
    int headerMoveHeight = (int)Math.abs((scrollY + deltaY) * SCROLL_RATIO);
    int mHeaderCurTop = (int) (mHeadInitRect.top + headerMoveHeight);
    mTopView.layout(mHeadInitRect.left, mHeaderCurTop, mHeadInitRect.right, (int) (mHeadInitRect.bottom + headerMoveHeight));
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mContentMaxMoveHeight, isTouchEvent);
}
要注意:
在向下移动时,scrollY和deltaY都是负值,所以要计算移动距离需要在外面加一层Math.abs()来取绝对值。
至于为什么在计算headerMoveHeight时要将移动距离乘以SCROLL_RATIO,在第二篇中也提到过,就是让小狗图片移动的慢一些,显得难拉一点,这比较符合正常思维
(2)、MainActivity中调用setmHeaderView
这里就非常简单了,就是调用OverScrollList的setTopView()将底部的小狗图片设置进去。
代码如下:(这段代码没什么难度,就不再细讲了)

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
    mListView = (OverScrollList) findViewById(R.id.listview);
 
    //设置topView
    ImageView topView = (ImageView)findViewById(R.id.background_img);
    mListView.setTopView(topView);
 
    //设置headerView
    LayoutInflater inflater = getLayoutInflater();
    View view = inflater.inflate(R.layout.headerview,mListView,false);
    mListView.addHeaderView(view);
 
    //初始化Adapter
    mListItems = new LinkedList<String>();
    mListItems.addAll(Arrays.asList(mStrings));
    mAdapter = new ArrayAdapter<String>(this,R.layout.item_layout, mListItems);
 
    mListView.setAdapter(mAdapter);
 
}
好了,到这里基本上就结束了,下面还有两个问题需要改进,然后就完整给出大家源码。
3、问题改进
(1)、OverScrollList的透明headerView在点击时会变白

答:我们就让它不可点击就可以了。在添加headview时,调用public void addHeaderView(View v, Object data, boolean isSelectable),将isSelectable设置为false即可,代码如下:

//设置headerview不可点击
mListView.addHeaderView(view, null, false);
非常注意,在headview对应的xml中设置clickable="false"是没有作用的;也就是下面的代码是没有作用的:(headview.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="100dp"
              android:clickable="false">
    <!--如果要让headview不可点击,在这里设置clickable="false"是没用的,只有通过ListView.addHeaderView(view,null,false);来设置-->
 
</LinearLayout>
(2)、到底部时,仍然可以向上拉,怎么实现只顶部下拉
答:还记得我们上面有讲过,如何利用overScrollView监听到底到顶吗?这里就派上用场啦。我们可以监听是否已经到底,如果到底就将maxOverScrollY设为0,如果是顶部就设为mContentMaxMoveHeight;
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
    //监听是否到底,如果到底就将maxOverScrollY设为0
    int newScrollY = scrollY + deltaY;
    final int bottom = maxOverScrollY + scrollRangeY;
    final int top = -maxOverScrollY;
    if (newScrollY > bottom) {
        maxOverScrollY = 0;
    } else if (newScrollY < top) {
        maxOverScrollY = mContentMaxMoveHeight;
    }
    //在向下移动时,scrollY是负值,所以scrollY + deltaY应该是当前应当所在位置。而由于scrollY + deltaY是负值,所以外层要包一个Math.abs()来取绝对值
    int headerMoveHeight = (int)Math.abs((scrollY + deltaY) * SCROLL_RATIO);
    int mHeaderCurTop = (int) (mHeadInitRect.top + headerMoveHeight);
    mTopView.layout(mHeadInitRect.left, mHeaderCurTop, mHeadInitRect.right, (int) (mHeadInitRect.bottom + headerMoveHeight));
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
好了,所有的都讲完了

四、OverScrollBy()存在问题
在实际应用中,发现有两个内部实现问题:
1、手指滑出屏幕后,不会回弹。
2、如果在下拉到底以后,弹出一个Fragment把当前页面盖在后面,即使这个Fragment是透明的,也会导致OverScrollBy()卡住不会回弹,即使自己回弹,也不会在回弹时调用overScrollBy(),导致其它底部小狗图片回弹出错。

好啦,这里就不再啰嗦了,下篇给大家讲述怎么使用OnTouchEvent()来实现下拉回弹的效果,这才正宗。
 

这篇关于PullScrollView详解(四)——完全使用listview实现下拉回弹(方法一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/754971

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Nginx部署HTTP/3的实现步骤

《Nginx部署HTTP/3的实现步骤》本文介绍了在Nginx中部署HTTP/3的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前提条件第一步:安装必要的依赖库第二步:获取并构建 BoringSSL第三步:获取 Nginx

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq