Android ScrollLayout 下拉回弹

Android ScrollLayout 下拉回弹

import android.content.Context;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;
import android.widget.OverScroller;

public class ScrollLayout extends LinearLayout {
    public static final int OVERSCROLL_DISTANCE = 200;
    protected static final int INVALID_POINTER_ID = -1;

    private OverScroller fScroller;
    // The ‘active pointer’ is the one currently moving our object.
    private int fTranslatePointerId = INVALID_POINTER_ID;
    private PointF fTranslateLastTouch = new PointF();

    private float firstX;
    private float firstY;

    public ScrollLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.initView(context, attrs);
    }

    public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.initView(context, attrs);
    }

    protected void initView(Context context, AttributeSet attrs) {
        fScroller = new OverScroller(this.getContext());

        this.setOverScrollMode(OVER_SCROLL_ALWAYS);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE: {
                final float translateX = ev.getX();
                final float translateY = ev.getY();

                //距离小于5认为是单击事件,传递给子控件
                if ((firstX - translateX < -5) || (firstX - translateX > 5) ||
                        (firstY - translateY < -5) || (firstY - translateY > 5)) {
                    return true;
                } else {
                    return false;
                }
            }
            case MotionEvent.ACTION_DOWN: {

                if (!fScroller.isFinished())
                    fScroller.abortAnimation();


                final float x = ev.getX();
                final float y = ev.getY();
                firstX = x;
                firstY = y;
                fTranslateLastTouch.set(x, y);

                //记录第一个手指按下时的ID
                fTranslatePointerId = ev.getPointerId(0);

                return false;
            }
            default: {
                return false;
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                if (!fScroller.isFinished())
                    fScroller.abortAnimation();

                final float x = event.getX();
                final float y = event.getY();

                fTranslateLastTouch.set(x, y);

                //记录第一个手指按下时的ID
                fTranslatePointerId = event.getPointerId(0);
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                /**
                 * 取第一个触摸点的位置
                 */
                final int pointerIndexTranslate = event.findPointerIndex(fTranslatePointerId);
                if (pointerIndexTranslate >= 0) {
                    float translateX = event.getX(pointerIndexTranslate);
                    float translateY = event.getY(pointerIndexTranslate);

                    //Log.i("com.zte.allowance", "fTranslatePointerId = " + fTranslatePointerId);
                    /**
                     * deltaX 将要在X轴方向上移动距离
                     * scrollX 滚动deltaX之前,x轴方向上的偏移
                     * scrollRangeX 在X轴方向上最多能滚动的距离
                     * maxOverScrollX 在x轴方向上,滚动到边界时,还能超出的滚动距离
                     */
                    //Log.i("com.zte.allowance", "delta y = " + (fTranslateLastTouch.y - translateY));
                    this.overScrollBy(
                            (int) (fTranslateLastTouch.x - translateX),
                            (int) (fTranslateLastTouch.y - translateY) / 4,
                            this.getScrollX(),
                            this.getScrollY(),
                            0,
                            0,
                            0,
                            OVERSCROLL_DISTANCE,
                            true);

                    fTranslateLastTouch.set(translateX, translateY);

                    this.invalidate();
                }

                break;
            }

            case MotionEvent.ACTION_UP: {
                /**
                 * startX 回滚开始时x轴上的偏移
                 * minX 和maxX 当前位置startX在minX和manX之 间时就不再回滚
                 *
                 * 此配置表示X和Y上的偏移都必须复位到0
                 */
                if (fScroller.springBack(this.getScrollX(), this.getScrollY(), 0, 0, 0, 0))
                    this.invalidate();

                fTranslatePointerId = INVALID_POINTER_ID;
                break;
            }
        }

        return true;
    }

    @Override
    public void computeScroll() {
        if (fScroller != null && fScroller.computeScrollOffset()) {
            int oldX = this.getScrollX();
            int oldY = this.getScrollY();

            /**
             * 根据动画开始及持续时间计算出当前时间下,view的X.Y方向上的偏移量
             * 参见OverScroller computeScrollOffset 的SCROLL_MODE
             */
            int x = fScroller.getCurrX();
            int y = fScroller.getCurrY();

            if (oldX != x || oldY != y) {
                //Log.i("com.zte.allowance", oldY + "  " + y);
                this.overScrollBy(
                        x - oldX,
                        (y - oldY),
                        oldX,
                        oldY,
                        0,
                        0,
                        0,
                        OVERSCROLL_DISTANCE,
                        false);
            }

            this.postInvalidate();
        }
    }

    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        // Treat animating scrolls differently; see #computeScroll() for why.
        if (!fScroller.isFinished()) {
            super.scrollTo(scrollX, scrollY);

            if (clampedX || clampedY) {
                fScroller.springBack(this.getScrollX(), this.getScrollY(), 0, 0, 0, 0);
            }
        } else {
            super.scrollTo(scrollX, scrollY);
        }
        awakenScrollBars();
    }

    @Override
    protected int computeHorizontalScrollExtent() {
        return this.getWidth();
    }

    @Override
    protected int computeHorizontalScrollOffset() {
        return this.getScrollX();
    }

    @Override
    protected int computeVerticalScrollExtent() {
        return this.getHeight();
    }


    @Override
    protected int computeVerticalScrollOffset() {
        return this.getScrollY();
    }


}


一个可以整体垂直滑动、具有展开折叠状态,并可以画出屏幕。他允许你在ScrollView或者ListView里面使用ViewPager,解决手势冲突问题。项目地址:https://github.com/xiongwei-git/ScrollDownLayoutdemo中使用了glide-3.6.1.jar效果图:使用很简单,只要将你需要滑动的布局外面包裹个ScrollDownLayout即可:<com.ted.coder.sdlayout.ScrollDownLayout         android:id="@ id/scroll_down_layout"         android:layout_width="match_parent"         android:layout_height="match_parent">         ...你的布局 </com.ted.coder.sdlayout.content.ContentScrollView>一些参数的设置:mScrollDownLayout = (ScrollDownLayout) findViewById(R.id.scroll_down_layout); mScrollDownLayout.setMinOffset(0); mScrollDownLayout.setMaxOffset(450);//距离顶端的最大偏移 mScrollDownLayout.setExitOffset(1674);//距离顶端多少时进入退出状态 mScrollDownLayout.setToOpen();//让其处于打开状态,与setToClosed()相反 mScrollDownLayout.setIsSupportExit(true);//是否支持划出底端退出 mScrollDownLayout.setAllowHorizontalScroll(true);//是否禁止手势水平滑动事件 mScrollDownLayout.setOnScrollChangedListener(mOnScrollChangedListener);监听滑动过程及状态://ScrollDownLayout滑动监听     private ScrollDownLayout.OnScrollChangedListener mOnScrollChangedListener = new ScrollDownLayout.OnScrollChangedListener() {         @Override         public void onScrollProgressChanged(float currentProgress) {//其值 0到1间,记录滑动过程中到达展开最大距离的百分比             System.out.println("ScrollDownLayout:" currentProgress);         }         @Override         public void onScrollFinished(ScrollDownLayout.Status currentStatus) {//监听状态变化             if(currentStatus.equals(ScrollDownLayout.Status.EXIT)){                 finish();             }         }     };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重播

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值