为ListView添加滚动出边界回弹效果

目    录(本篇字数:1505)

介绍

实现方法

android:overScrollMode="always";

   overScrollBy()

实现思路及代码

如何使用

出现bug


  • 介绍

    ListView作为最常用的控件之一,虽然说它的性能不如RecyclerView,但是通过一定的优化性能也是很不错的。那我们今天来实现一种比较常见的效果——弹性ListView

    具有弹性ListView会有一种比较友好的UI体验,在滑动到ListView的顶部或者底部时候,我们会因为惯性继续滑动来确保是否是最开头或者最末尾的Item,那么我们给予它一种弹性效果,UI体验将显得更加友好。(例如QQ侧滑里的Item菜单效果)

  • 效果图

    说了这么多,那到底是怎样一种效果呢?我们来看一波效果图:

    其实百度一搜会有很多不同的实现方式,那我这里提供一种比较简单,容易理解的实现方法。

  • 实现方法

    首先,我们来看一个属性方法:

android:overScrollMode="always";

    它的作用是处理边界回弹效果,当视图滚动到边界时,它会再滚动一点距离,附带一个发光的效果。(例如上图滚动到底部的蓝色弧形效果)。

    它的属性总共有三种模式:

    1、always (表示:总是允许滚动)

    2、ifContentScrolls(表示:如果视图内容到达边界时滚动)

    3、never(表示:从不滚动)

    当然,我们还可以通过代码的方式设置,比如这样:

        /**
         * OVER_SCROLL_ALWAYS
         * OVER_SCROLL_IF_CONTENT_SCROLLS
         * OVER_SCROLL_NEVER
         */
        mListView.setOverScrollMode(View.OVER_SCROLL_ALWAYS);

   overScrollBy()

    这三种模式与上面介绍的一模一样,只是上面是在XML里的属性方法。最后,我们介绍一个至关重要的方法,也是通过这个方法来实现我们的弹性效果的:

    @Override
    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, maxOverScrollY, isTouchEvent);
    }

    这个方法呢比较长,也是比较复杂的一个方法,作用是当视图滚动到达屏幕的边缘时会回调这个方法,我们在这个方法里就可以定制一些我们自己想要到效果了。重点来看一下这个方法讲了哪些吧:

    1、int deltaX / int deltaY ,表示:偏移量,当前滚动的x,y值。

    2、int scrollX / int scrollY ,表示:其scroll的值。

    3、int scrollRangeX / int scrollRangeY ,表示:可以滚动的范围值。例如:内容视图大小(100,100)比父容器(50,50)大,则返回(100,100),反之。

    4、int maxOverScrollX / int maxOverScrollY ,表示:允许滚动的最大距离。X或Y方向,默认值为0。

    5、boolean isTouchEvent ,表示:是否在onTouchEvent()中调用此函数。

  • 实现思路及代码

    从此函数介绍中,我们可以发现有两个参数是我们可以用到的,int maxOverScrollX / int maxOverScrollY

    既然我们要对ListView的弹性做文章(我们用垂直方向的),那就得从  int maxOverScrollY 这里下手了。我们得知它的默认值是为0的,所以它能滚出边界外的最大是就是0,也就是滚动距离太小,我们看不到而已。

    所以我们这样,给予它应该大于0初始值,你便可以很清楚的看到效果了。当然,这么做的话,它滚动的距离是固定的,也不是很友好。那么,我们来实现上图动态更改的效果吧。我们看一下代码:

/**
 * @Created by xww.
 * @Creation time 2018/7/16.
 */

public class ElasticityListView extends ListView {
    private int maxOverScrollYDis;

    private float y1, y2;

    public ElasticityListView(Context context) {
        super(context);
    }

    public ElasticityListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ElasticityListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                y1 = ev.getY(0);//获取手指在屏幕按下第一个点y坐标
                break;
            case MotionEvent.ACTION_MOVE:
                y2 = ev.getY(0);//获取手指在移动过程中的y坐标
                maxOverScrollYDis = (int) Math.abs((y2 - y1));
                break;
            case MotionEvent.ACTION_UP:
//                maxOverScrollYDis = 0;
                break;
        }
        return super.onTouchEvent(ev);
    }

    @Override
    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,
                maxOverScrollYDis / 2, isTouchEvent);
    }
}

    这个就是我们最终的自定义的ListView了,除了重载overScrollBy()这个方法,我们还重载了onTouchEvent()方法。这里实现思路是这样的:在onTouchEvent()方法中,我们获取了手指按下去的第一个点y1坐标,在手指移动过程中再获取了y2坐标,最后通过两个坐标的绝对值来动态更改滚出屏幕的距离,达到回弹效果。maxOverScrollYDis / 2 ,这也我感觉是比较合适的。

    如果你想改变回弹速度的话,只需在UP事件里控制 maxOverScrollYDis 的值就可以了,我这里直接给了一个初始值0,我们看一下效果图:

  • 如何使用

    在我们需要的xml文件中引入我们自定义的listview,和平常我们使用listview方式没区别。

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.x.mycustomviews.my_listview.elasticity.ElasticityListView
        android:id="@+id/lv_elasicity"
     android:overScrollMode="ifContentScrolls"
        android:scrollbars="none"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
  • 出现bug

    我们可以看到它将快速的回弹至初始状态。当然,如果你想定制更多的效果,通过这种方式实现比较有局限性。这里会出现一个bug,不知道为什么会出现这样的原因。

    Bug情况:我们在回弹的时候,会发现它卡在那里不动了。

©原文链接:https://blog.csdn.net/smile_running/article/details/81128438

@作者博客:_Xu2WeI

@更多博文:查看作者的更多博文

posted @ 2018-07-20 13:06  爱写Bug的程序猿  阅读(1418)  评论(1编辑  收藏  举报