ScrollView下拉刷新

public class PullScrollView extends RelativeLayout {

    public PullScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

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

    public PullScrollView(Context context) {
        super(context);
        init(context);
    }

    /**
    *首先是初始化mTouchSlop  是触摸手势滑动的最小像素值,也就是说滑动多少
    *的距离才算是手势滑动,这样可以防止手势一点点的移动就引起的滑动事件。
    *mScroller  是用来处理平滑滚动的。之前的博客有介绍。
    **/
    private Scroller mScroller ;
    private int mTouchSlop ;
    private void init(Context context){

         ViewConfiguration configuration = ViewConfiguration.get(getContext());
         mTouchSlop = configuration.getScaledTouchSlop();

        mScroller = new Scroller(context, new DecelerateInterpolator());
    }


    /**
    * 在布局初始化结束之后,得到布局中的两个子孩子,为啥只能有两个孩子那??
    * BottomView是用来下拉刷新展示的View  contentView 就是我们的
    * ScrollView了。如果子孩子多了,怎么知道哪个VIew是需要被隐藏的?所以只
    * 处理两个View的情况,当然,如果还有上拉加载更多,就需要三个子孩子了。
    **/
    private ViewGroup bottomView ;
    private ScrollView contentView ; 


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        getTopPosition();
        return super.dispatchTouchEvent(ev);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() > 2) {
            throw new RuntimeException("子孩子只能有两个");
        }
        bottomView = (ViewGroup) getChildAt(0);
        contentView = (ScrollView) getChildAt(1);
    }

    private int startY ; 
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        if (getScrollY() < 0 ) {
            return true ;
        }

        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startY = (int) ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            int moveY = (int) ev.getY();
            int delayY = moveY - startY ;
            Log.i("Test", delayY + " =  " + mTouchSlop) ;
            if (getTopPosition() && delayY > mTouchSlop) {
                ev.setAction(MotionEvent.ACTION_DOWN);
                return true ;
            }
            break ;
        case MotionEvent.ACTION_UP:

            break;
        }
        return super.onInterceptTouchEvent(ev);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startY = (int) event.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            int delayY = (int) (event.getY() - startY) ;
            if (getTopPosition() && getScrollY() <= 0 ) {
                pullMove((int) (-delayY * 0.8));
            }
            startY = (int) event.getY();
            return true ;
        case MotionEvent.ACTION_UP:
            int scrollY = getScrollY();
            if (state == PullState.ON_REFRESH && scrollY < 0 && Math.abs(scrollY) > bottomHeight) {
                restView(-getScrollY() - bottomHeight);
                return true ;
            }else if (state == PullState.ON_REFRESH && scrollY < 0 && Math.abs(scrollY) < bottomHeight) {
                return true ;
            }
            if (scrollY < 0  &&  Math.abs(scrollY) < bottomHeight ) {
                returnView();
            }else if (scrollY < 0 && Math.abs(scrollY) > bottomHeight  && state != PullState.ON_REFRESH) {
                if (onreListener != null) {
                    state = PullState.ON_REFRESH ;
                    onreListener.refresh();
                }
                restView(-getScrollY() - bottomHeight);
            }
            break;
        }
        return true ;
    }


    private PullState state = PullState.REST ; 

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }


    private void returnView(){
        restView(-getScrollY());
    }


    private void restView(int dy){
        mScroller.startScroll(0, getScrollY(), 0, dy , 340);
        postInvalidate();
    }



    private void pullMove(int delay){
        if (getScrollY() <= 0 && (getScrollY() + delay) <= 0 ) {
            scrollBy(0, delay);
        }else {
            scrollTo(0, 0);
        }
    }

    /**
    *手势的拦截动作,通过getTopPosition()方式,来判断ScrollView时候处于
    *下拉需要显示隐藏子View的状态,delayY > mTouchSlop 是用来判断是不是
    *下拉的动作的。 如果符合条件  我们就需要将手势拦截掉,return true。
    *为什么我觉得ScrollView做下来刷新比较好做那? 就是因为ScrollView的判断比较好判断。
    */
    private boolean getTopPosition(){
        if (contentView.getScrollY() <= 0 ) {
            return true ;
        }
        return false ;
    }

    /**
    *onLayout,做过自定义的都应该很熟悉这个方法,放置子孩子位置的一个方法,
    *因为我们需要有一个子孩子隐藏掉,当我们需要它显示的时候才去显示,所以
    *需要手动的去将BottomView放到布局-hight到0的位置,这样下拉的时候才能
    *显示出来。
    **/
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        bottomHeight = getBottomViewHeight() ; 
        Log.i("Test", l + "ceshi" + " t="+t + " r"+r + " b=" + b + " height= "   + bottomHeight);
        bottomView.layout(l, - bottomHeight, r, t);
        contentView.layout(l, 0, r, b);
    }



    private int bottomHeight = 0 ;

    private int getBottomViewHeight(){
        return bottomView.getMeasuredHeight();
    }


    enum PullState{
        REST , ON_REFRESH 
    }


    public void stopRefresh(){
        state = PullState.REST; 
        returnView();
    }

    private onRefreshListener onreListener ; 

    public void setOnRefreshListener (onRefreshListener onreListener) {
        this.onreListener = onreListener ;
    }


    public interface onRefreshListener{
        public void refresh();
    }
}

源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值