一、前提

接到新需求,要求列表滑动过程增加阻尼回弹效果,且即使列表不能填充一整个屏幕的情况下也支持滑动。

有人说,给RecyclerView加上 android:overScrollMode="always" 就行了,事实证明,NO!这个东西只是在滑动到边缘是多了个水波阴影而已,没有阻尼回弹。

又有人说,给ListView加上 android:overScrollMode="always" 就行了,经过尝试,貌似可以,但是有bug,还相当严重。况且我还要把RecyclerView改成ListView,太麻烦了。

本着不重复造轮子的前提,搜索了一大波。github上面也有很多现成的框架,不过由于项目要求,不能随便引入框架,所以不敢直接depend,本来想copy源码改吧改吧,后来发现这些框架做的都很“大”,冗余功能代码量多,索性放弃。后来终于找到一个简单的实现方式。借鉴作者思路,继续修改使之符合我的功能要求。站在巨人的肩膀上会让成功来的更快!

二、直接上代码:

package com.zzz.test.view.widget;import android.content.Context;import android.graphics.Rect;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.animation.TranslateAnimation;import android.widget.LinearLayout;/** * @author xxx * @date 20-4-29 */public class OverScrollLayout extends LinearLayout {    private static final int ANIM_TIME = 400;    private RecyclerView childView;    private Rect original = new Rect();    private boolean isMoved = false;    private float startYpos;    /**     * 阻尼系数     */    private static final float DAMPING_COEFFICIENT = 0.3f;    private boolean isSuccess = false;    private ScrollListener mScrollListener;    public OverScrollLayout(Context context) {        this(context, null);    }    public OverScrollLayout(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public OverScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        childView = (RecyclerView) getChildAt(0);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        original.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());    }    public void setScrollListener(ScrollListener listener) {        mScrollListener = listener;    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        float touchYpos = ev.getY();        if (touchYpos >= original.bottom || touchYpos <= original.top) {            if (isMoved) {                recoverLayout();            }            return true;        }        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                startYpos = ev.getY();            case MotionEvent.ACTION_MOVE:                int scrollYpos = (int) (ev.getY() - startYpos);                boolean pullDown = scrollYpos > 0 && canPullDown();                boolean pullUp = scrollYpos < 0 && canPullUp();                if (pullDown || pullUp) {                    cancelChild(ev);                    int offset = (int) (scrollYpos * DAMPING_COEFFICIENT);                    childView.layout(original.left, original.top + offset, original.right, original.bottom + offset);                    if (mScrollListener != null) {                        mScrollListener.onScroll();                    }                    isMoved = true;                    isSuccess = false;                    return true;                } else {                    startYpos = ev.getY();                    isMoved = false;                    isSuccess = true;                    return super.dispatchTouchEvent(ev);                }            case MotionEvent.ACTION_UP:                if (isMoved) {                    recoverLayout();                }                return !isSuccess || super.dispatchTouchEvent(ev);            default:                return true;        }    }    /**     * 取消子view已经处理的事件     *     * @param ev event     */    private void cancelChild(MotionEvent ev) {        ev.setAction(MotionEvent.ACTION_CANCEL);        super.dispatchTouchEvent(ev);    }    /**     * 位置还原     */    private void recoverLayout() {        TranslateAnimation anim = new TranslateAnimation(0, 0, childView.getTop() - original.top, 0);        anim.setDuration(ANIM_TIME);        childView.startAnimation(anim);        childView.layout(original.left, original.top, original.right, original.bottom);        isMoved = false;    }    /**     * 判断是否可以下拉     *     * @return true:可以,false:不可以     */    private boolean canPullDown() {        final int firstVisiblePosition = ((LinearLayoutManager) childView.getLayoutManager()).findFirstVisibleItemPosition();        if (firstVisiblePosition != 0 && childView.getAdapter().getItemCount() != 0) {            return false;        }        int mostTop = (childView.getChildCount() > 0) ? childView.getChildAt(0).getTop() : 0;        return mostTop >= 0;    }    /**     * 判断是否可以上拉     *     * @return true:可以,false:不可以     */    private boolean canPullUp() {        final int lastItemPosition = childView.getAdapter().getItemCount() - 1;        final int lastVisiblePosition = ((LinearLayoutManager) childView.getLayoutManager()).findLastVisibleItemPosition();        if (lastVisiblePosition >= lastItemPosition) {            final int childIndex = lastVisiblePosition - ((LinearLayoutManager) childView.getLayoutManager()).findFirstVisibleItemPosition();            final int childCount = childView.getChildCount();            final int index = Math.min(childIndex, childCount - 1);            final View lastVisibleChild = childView.getChildAt(index);            if (lastVisibleChild != null) {                return lastVisibleChild.getBottom() <= childView.getBottom() - childView.getTop();            }        }        return false;    }    public interface ScrollListener {        /**         * 滚动事件回调         */        void onScroll();    }}

使RecyclerView支持阻尼回弹的话,把RecyclerView放到OverScrollView中,其他照旧。要想监听阻尼滑动,直接设置scrollListener。

                

 

更多相关文章

  1. Android(安卓)ORM 数据库的使用
  2. afinal logo Android的快速开发框架 afinal
  3. Android(安卓)开源照相和图片选择框架PictureSelector
  4. android真实项目教程(一)——App应用框架搭建_by_CJJ
  5. Android热补丁动态修复技术(四):完善框架①
  6. Android(安卓)ORM框架GreenDao入门学习
  7. (4.2.9)【android开源工具】Android(安卓)ORMLite 框架的入门用法
  8. Android中网络框架简单封装的实例方法
  9. Android端腾讯性能监控框架Matrix源码分析之第一篇

随机推荐

  1. Android NDK 配置与打包
  2. Android获取StatusBa有效高度
  3. Android开发遇到的问题----自定义Recycle
  4. Android积木之 点击空白区域隐藏小键盘
  5. Android 出错显示找不到id,但你发现id是存
  6. Android 下载服务器上的文件
  7. Android 调用系统的照相,浏览图片,转存并裁
  8. android上文件上传至tomcat服务器源码
  9. Android 的背光控制
  10. 开发安卓-android使用webview加载网页无法