反弹效果的 ScrollView(FlexibleScrollView)

来源:互联网 发布:3done软件下载 编辑:程序博客网 时间:2024/06/04 18:27

带有上下反弹效果的 scrollview,只是为了体验效果好一些,实际上用了这个控件后,复杂的滑动逻辑等可能会出现bug,但是简单布局用了后还挺不错的。

public class FlexibleScrollView extends ScrollView {    private static final String TAG = "FLEXIBLESCROLLVIEW";    // 移动因子,是一个百分比,比如手指移动了100px,那么view只移动50px,目的是达到一个延迟的效果。    private static final float MOVE_FACTOR = 0.5f;    // 手指松开时,界面回到原始位置动画所需的时间    private static final int ANIM_TIME = 300;    // ScrollView唯一的一个子view    private View contentView;    // 手指按下时的Y值,用于计算移动中的移动距离    // 如果按下时不能上拉或者下拉,会在手指移动时更新为当前手指的Y值。    private float startY;    // 用于记录正常的布局位置    private Rect originalRect = new Rect();    // 记录手指按下时是否可以下拉    private boolean canPullDown = false;    // 记录手指按下时是否可以上拉    private boolean canPullUp = false;    // 在手指滑动时的过程中记录是否移动了布局    private boolean isMoved = false;    public FlexibleScrollView(Context context) {        this(context, null);    }    public FlexibleScrollView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public FlexibleScrollView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    /**     * 在加载完xml后获取唯一的一个childview     */    @Override    protected void onFinishInflate() {        super.onFinishInflate();        if (getChildCount() > 0) {            // 获取第一个childview            contentView = getChildAt(0);        }    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        if (contentView == null)            return;        // scrollview唯一的一个子view的位置信息,这个位置信息在整个生命周期中保持不变        originalRect.set(contentView.getLeft(), contentView.getTop(), contentView.getRight(), contentView.getBottom());    }    /**     * 重写拦截事件,防止自动轮播时,关闭收藏按钮     *     * @param ev     * @return     */    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        return super.onTouchEvent(ev);    }    // 在触摸事件中处理上拉和下拉的逻辑    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        if (contentView == null) {            return super.dispatchTouchEvent(ev);        }        int action = ev.getAction();        switch (action) {            case MotionEvent.ACTION_DOWN:                // 判断是否可以上拉或者下拉                canPullDown = isCanPullDown();                canPullUp = isCanPullUp();                // 记录按下时的Y值                startY = ev.getY();                break;            case MotionEvent.ACTION_UP:                if (!isMoved)                    break; // 如果没有移动布局,则跳过执行                // 开启动画                TranslateAnimation anim = new TranslateAnimation(0, 0, contentView.getTop(), originalRect.top);                // 设置动画时间                anim.setDuration(ANIM_TIME);                // 给view设置动画                contentView.setAnimation(anim);                // 设置回到正常的布局位置                contentView.layout(originalRect.left, originalRect.top, originalRect.right, originalRect.bottom);                // 将标志位重置                canPullDown = false;                canPullUp = false;                isMoved = false;                break;            case MotionEvent.ACTION_MOVE:                // 在移动过程既没有达到上拉的程度,又没有达到下拉的程度                if (!canPullDown && !canPullUp) {                    startY = ev.getY();                    canPullDown = isCanPullDown();                    canPullUp = isCanPullUp();                    break;                }                // 计算手指移动的距离                float nowY = ev.getY();                int deltaY = (int) (nowY - startY);                // 是否应该移动布局                // 1.可以下拉,并且手指向下移动                // 2.可以上拉,并且手指向上移动                // 3.既可以上拉也可以下拉,当ScrollView包裹的控件比scrollView还小                boolean shouldMove = (canPullDown && deltaY > 0) || (canPullUp && deltaY < 0) || (canPullDown && canPullUp);                if (shouldMove) {                    // 计算偏移量                    int offset = (int) (deltaY * MOVE_FACTOR);                    contentView.layout(originalRect.left, originalRect.top + offset, originalRect.right, originalRect.bottom + offset);                    isMoved = true;                }                break;        }        return super.dispatchTouchEvent(ev);    }    /**     * 判断是否滚动到顶部     *     * @return     */    private boolean isCanPullDown() {        return getScrollY() == 0 || contentView.getHeight() < getHeight() + getScrollY();    }    /**     * 判断是否滚动到底部     */    private boolean isCanPullUp() {        return contentView.getHeight() <= getScrollY() + getHeight();    }}