实现有阻尼下拉/上拉刷新列表

        在上一篇文章《有阻尼下拉刷新列表的实现》中,我解析了如何基于重载dispatchDraw方法重画子View和重载onTouchEvent方法监控受试来实现下拉刷新列表,而在这篇文章中,我将会基于上一篇文章介绍的技术,在下拉刷新列表PullToRefreshListVIew的基础上,加上有阻尼上拉刷新功能。上一篇文章《有阻尼下拉刷新列表的实现》的链接如下。

http://blog.csdn.net/ivan_zgj/article/details/50664780

        好,我们还是先来看看效果。

实现有阻尼下拉/上拉刷新列表_第1张图片

实现有阻尼下拉/上拉刷新列表_第2张图片

1. 通过onScrollListener监控listVIew是否滚动到底部

        在PullToRefreshListView中,我们通过onScrollListener回调来监控PullToRefreshListView是否已经滚动到顶部,当时的代码是这样的:

    setOnScrollListener(new OnScrollListener() {  
        @Override  
        public void onScrollStateChanged(AbsListView view, int scrollState) {  
      
        }  
      
        @Override  
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {  
            // 没有子view的时候(没有数据,或者被拉到看不到子view),意味着该listView滚动到顶部  
            if (getChildCount() == 0) {  
                isTop = true;  
                return;  
            }  
            if (firstVisibleItem == 0) {  
                View firstView = getChildAt(0);  
                if (firstView.getTop() + distanceY >= 0) {  
                    // 第一个view可见且其相对parent(该listView)的顶部距离大于等于0,意味着该listView也是滚动到顶部  
                    isTop = true;  
                    return;  
                }  
            }  
            isTop = false;  
        }  
    });    
         当时我们通过判断firstVisibleItem==0以及第一个子View是否完全可见,从而确定PullToRefreshListView是否滚动到顶部。现在,我们加入以下代码来判断PullToRefreshListView是否滚动到底部。

                if (firstVisibleItem + visibleItemCount == totalItemCount) {
                    View firstView = getChildAt(visibleItemCount - 1);
                    if (firstView.getBottom() + distanceY <= getHeight()) {
                        // 最后一个view可见且其相对parent(该listView)的底部距离小于等于listView高度,意味着该listView也是滚动到底部
                        isBottom = true;
                        return;
                    }
                }
                isBottom = false;
        在onScroll方法中,fisrstVisibleItem是指列表当前可见的第一个子View在其adapter中的position,而visibleItemCount是指可见的子View的个数,totalItemCount是指adapter总共有多少个View。因此,我们可以很容易得出结论,当:
firstVisibleItem + visibleItemCount == totalItemCount
成立时,PullToRefreshListView就已经滚动到底部了。


2. 监控滑动手势使listView进入上拉状态

        在PullToRefreshListView中,我们通过重载onTouchEvent方法来监控用户手势,从而判断PullToRefreshListView是否要进行下拉,当时的代码是这样的:

@Override  
public boolean onTouchEvent(MotionEvent ev) {  
    if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) {  
        // 按下的时候  
        lastAction = MotionEvent.ACTION_DOWN;  
        cancelAnimating();  
        L.d(TAG, "touch down");  
    } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {  
        // 放开手指,开始回滚  
        isPulling = false;  
        lastAction = -1;  
        startAnimating();  
        L.d(TAG, "touch up");  
    } else if (lastAction == MotionEvent.ACTION_DOWN) {  
        if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {  
            // 在按下手指的基础上,开始滑动  
            if (isTop && !isPulling) {  
                // listView在顶部而且不处于下拉刷新状态,开始下拉  
                pullStartY = ev.getY();  
                lastAction = MotionEvent.ACTION_MOVE;  
                isPulling = true;  
            }  
        }  
    } else if (lastAction == MotionEvent.ACTION_MOVE) {  
        if (isTop) {  
            // 下拉  
            distanceY = ev.getY() - pullStartY;  
            L.d(TAG, distanceY + "");  
            if (distanceY > 0) {  
                distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY);  
                // 在下拉状态时取消系统对move动作的响应,完全由本类响应  
                ev.setAction(MotionEvent.ACTION_DOWN);  
            } else {  
                distanceY = 0;  
                // 在下拉过程中往上拉动该listView使得其回到顶部位置,则将该move动作交由系统进行响应  
                ev.setAction(MotionEvent.ACTION_MOVE);  
            }  
        } else {  
            // 在下拉过程中往上拉动listView使listView往下滚动到其没有滚动到顶部,则取消其下拉状态,回到手指按下的初始状态  
            lastAction = MotionEvent.ACTION_DOWN;  
            isPulling = false;  
            distanceY = 0;  
        }  
    }  
    return super.onTouchEvent(ev);  
} 

         现在,我们只需要在lastAction == MotionEvent.ACTION_MOVE判断里面,与isTop判断并列,再加一个判断。

            if (isBottom) {
                // 上拉
                configureHeader(false);
                distanceY = ev.getY() - pullStartY;
                L.d(TAG, distanceY + "");
                if (distanceY < 0) {
                    distanceY = (float) (Math.exp(-pullStartY / ev.getY() / 40) * distanceY);
                    // 在上拉状态时取消系统对move动作的响应,完全由本类响应
                    ev.setAction(MotionEvent.ACTION_DOWN);
                } else {
                    distanceY = 0;
                    // 在上拉过程中往上拉动该listView使得其回到顶部位置,则将该move动作交由系统进行响应
                    ev.setAction(MotionEvent.ACTION_MOVE);
                }
            }

        很明显,这个判断是在执行这样一个动作,如果PullToRefreshListView已经滚动到底部,那么就根据用户手指滑动的距离计算PullToRefreshListView上拉的距离,这一段代码与处理下拉的代码是基本一样的。

3. 重载dispatchDraw方法实现上拉

         在PullToRefreshListView的基础上,我们将重画下拉的子View的代码替换成以下代码。

            // 重画子view
            int left = getPaddingLeft();
            int top = getPaddingTop();
            int bottom = getPaddingBottom();
            canvas.save();
            if (distanceY > 0) {
                canvas.translate(left, top + distanceY);
            } else {
                canvas.translate(left, -bottom + distanceY);
            }
            for (int i=0;i<getChildCount();i++) {
                View child = getChildAt(i);
                drawChild(canvas, child, getDrawingTime());
            }
            canvas.restore();

        这里只需要注意下拉和上拉的时候canvas要translate的方向和距离的计算就可以了,原理在上一篇文章中已经解析过了,这里就不多说了。大家结合着看就好了。

4. 加上刷新头

        为了优化UI和给用户做出提示,我这次还加入刷新头,就是那个提示下拉刷新,释放以刷新blablabla的东西。同样的,我也是在dispatchDraw方法里面实现的。具体代码如下。

            canvas.save();
            // 画刷新头
            View header;
            if (distanceY > 0) {
                int whereToLoad = dp2px(onLoadCallBack.whereToLoad(true));
                if (distanceY > whereToLoad) {
                    topRefreshHeaderView.setText(HeaderView.STATE_CAN_RELEASE);
                    topRefreshHeaderView.setIcon(HeaderView.STATE_CAN_RELEASE);
                } else if (distanceY < whereToLoad) {
                    topRefreshHeaderView.setText(HeaderView.STATE_PULLING_DOWN);
                    topRefreshHeaderView.setIcon(HeaderView.STATE_PULLING_DOWN);
                }
                header = topRefreshHeaderView.getView();
                canvas.translate(left, top + distanceY - header.getHeight() - offset);
            } else {
                int whereToLoad = dp2px(onLoadCallBack.whereToLoad(false));
                if (-distanceY > whereToLoad) {
                    bottomRefreshHeaderView.setText(HeaderView.STATE_CAN_RELEASE);
                    bottomRefreshHeaderView.setIcon(HeaderView.STATE_CAN_RELEASE);
                } else if (-distanceY < whereToLoad) {
                    bottomRefreshHeaderView.setText(HeaderView.STATE_PULLING_UP);
                    bottomRefreshHeaderView.setIcon(HeaderView.STATE_PULLING_UP);
                }
                header = bottomRefreshHeaderView.getView();
                canvas.translate(left, -bottom + distanceY + getHeight() + offset);
            }
            drawChild(canvas, header, getDrawingTime());
            canvas.restore();
        我们看到这里有两个判断,第一个是下拉刷新头,第二个是上拉刷新头,其实原理也是一样,将canvas平移到合适的位置,然后调用drawChild方法就可以了。

        这里要简单地说一说topRefreshHeaderView和bottomRefreshHeaderView这两个东西,其实它们是我设计的一个抽象类实现对象,该抽象类如下。

    /**
     * 刷新头的抽象类
     */
    public abstract class HeaderView {
        private View headerView;
        public static final int STATE_PULLING_UP = 0x30;
        public static final int STATE_PULLING_DOWN = 0x31;
        public static final int STATE_CAN_RELEASE = 0x32;
        public static final int STATE_REFRESHING = 0x33;

        public HeaderView(View headerView) {
            this.headerView = headerView;
        }

        /**
         * 获得刷新头的View
         * @return 实例化是给予的一个自定义View
         */
        protected View getView() {
            return headerView;
        }

        /**
         * 根据状态设置显示内容
         * @param state 状态
         */
        protected abstract void setText(int state);

        /**
         * 根据状态设置显示图标
         * @param state 状态
         */
        protected abstract void setIcon(int state);
    }
        根据这个抽象类的设计,我们可以知道,该抽象类为刷新头的外观提供了一个标准,它应该有一个图标和一个提示的文字内容。当然,PullToRefreshListView有一个默认的刷新头。


        最后就是大家最喜欢的源码了。

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.TextView;
import com.ivan.healthcare.healthcare_android.log.L;

/**
 * 支持下拉刷新的的listView
 * Created by Ivan on 16/2/14.
 */
public class PullToRefreshListView extends ListView {

    private final String TAG = "PullToRefreshListView";
    /**
     * 默认回滚速度,millis/100dp
     */
    private final int DEFAULT_BASE_ANIMATING_TIME_PER_100DP = 100;
    /**
     * 默认刷新背景高度
     */
    public static final int DEFAULT_WHERE_TO_LOAD = 80;
    /**
     * 记录上一个手势动作
     */
    private int lastAction = -1;
    /**
     * 下拉起始位置
     */
    private float pullStartY = -1;
    /**
     * 是否处于“滚动到顶部”状态
     */
    private boolean isTop = true;
    /**
     * 是否处于“滚动到底部”状态
     */
    private boolean isBottom = true;
    /**
     * 下拉距离
     */
    private float distanceY = 0;
    /**
     * 是否处于下拉状态
     */
    private boolean isPulling = false;
    /**
     * 回滚动画控制器
     */
    private ValueAnimator pullCancelAnimator;

    private Context context;
    /**
     * 刷新背景
     */
    private Drawable refreshDrawable;
    /**
     * 下拉刷新头
     */
    private HeaderView topRefreshHeaderView;
    /**
     * 上拉刷新头
     */
    private HeaderView bottomRefreshHeaderView;
    /**
     * 刷新头位置偏移
     */
    private int offset;
    /**
     * 刷新回调
     */
    private OnLoadCallBack onLoadCallBack = new OnLoadCallBack(this) {

        @Override
        public void onLoad(boolean topOrBottom) {

        }

        @Override
        public void cancelLoad(boolean topOrBottom) {

        }
    };

    public PullToRefreshListView(Context context) {
        super(context);
        initView(context);
    }

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

    private void initView(Context context) {
        this.context = context;
        offset = dp2px(10);
        setOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {

            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                // 没有子view的时候(没有数据,或者被拉到看不到子view),意味着该listView滚动到顶部/底部
                if (getChildCount() == 0) {
                    isTop = true;
                    isBottom = true;
                    return;
                }

                if (firstVisibleItem == 0) {
                    View firstView = getChildAt(0);
                    if (firstView.getTop() + distanceY >= 0) {
                        // 第一个view可见且其相对parent(该listView)的顶部距离大于等于0,意味着该listView也是滚动到顶部
                        isTop = true;
                        return;
                    }
                }
                isTop = false;

                if (firstVisibleItem + visibleItemCount == totalItemCount) {
                    View firstView = getChildAt(visibleItemCount - 1);
                    if (firstView.getBottom() + distanceY <= getHeight()) {
                        // 最后一个view可见且其相对parent(该listView)的底部距离小于等于listView高度,意味着该listView也是滚动到底部
                        isBottom = true;
                        return;
                    }
                }
                isBottom = false;
            }
        });
    }

    /**
     * 配置刷新头,当开始下拉/上拉时会执行该方法
     * @param topOrBottom true for top, false for bottom
     */
    private void configureHeader(boolean topOrBottom) {
        final TextView header;
        if (topOrBottom) {
            if (topRefreshHeaderView == null) {
                topRefreshHeaderView = onLoadCallBack.getHeaderView(true);
                if (topRefreshHeaderView != null) {
                    return;
                }
                header = new TextView(context);
                topRefreshHeaderView = new HeaderView(header) {
                    @Override
                    public void setText(int state) {
                        switch (state) {
                            case STATE_PULLING_DOWN:
                                header.setText("下拉刷新");
                                break;
                            case STATE_PULLING_UP:
                                header.setText("上拉刷新");
                                break;
                            case STATE_CAN_RELEASE:
                                header.setText("释放以刷新");
                                break;
                            case STATE_REFRESHING:
                                header.setText("刷新中...");
                                break;
                            default:
                                break;
                        }
                    }

                    @Override
                    public void setIcon(int state) {

                    }
                };
            } else {
                return;
            }
            header.setText("下拉刷新");
        } else {
            if (bottomRefreshHeaderView == null) {
                bottomRefreshHeaderView = onLoadCallBack.getHeaderView(true);
                if (bottomRefreshHeaderView != null) {
                    return;
                }
                header = new TextView(context);
                bottomRefreshHeaderView = new HeaderView(header) {
                    @Override
                    public void setText(int state) {
                        switch (state) {
                            case STATE_PULLING_DOWN:
                                header.setText("下拉刷新");
                                break;
                            case STATE_PULLING_UP:
                                header.setText("上拉刷新");
                                break;
                            case STATE_CAN_RELEASE:
                                header.setText("释放以刷新");
                                break;
                            case STATE_REFRESHING:
                                header.setText("刷新中...");
                                break;
                            default:
                                break;
                        }
                    }

                    @Override
                    public void setIcon(int state) {

                    }
                };
            } else {
                return;
            }
            header.setText("上拉刷新");
        }
        header.setTextSize(20);
        header.setTextColor(Color.WHITE);
        header.setGravity(Gravity.CENTER);

        LayoutParams layoutParams = (LayoutParams) header.getLayoutParams();
        if (layoutParams == null) {
            layoutParams = (LayoutParams) generateDefaultLayoutParams();
            header.setLayoutParams(layoutParams);
        }

        int heightMode = MeasureSpec.getMode(layoutParams.height);
        int heightSize = MeasureSpec.getSize(layoutParams.height);

        if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;

        int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
        if (heightSize > maxHeight) heightSize = maxHeight;

        // measure & layout
        int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
        int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
        header.measure(ws, hs);
        header.layout(0, 0, header.getMeasuredWidth(), header.getMeasuredHeight());
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (lastAction == -1 && ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
            // 按下的时候
            lastAction = MotionEvent.ACTION_DOWN;
            cancelAnimating();
            L.d(TAG, "touch down");
        } else if (lastAction == MotionEvent.ACTION_MOVE && ev.getActionMasked() == MotionEvent.ACTION_UP) {
            // 放开手指,开始回滚
            isPulling = false;
            lastAction = -1;
            startAnimating(distanceY>0);
            L.d(TAG, "touch up");
        } else if (lastAction == MotionEvent.ACTION_DOWN) {
            if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
                // 在按下手指的基础上,开始滑动
                if ((isBottom || isTop) && !isPulling) {
                    // listView在顶部/底部而且不处于下拉刷新状态,开始下拉/上拉
                    pullStartY = ev.getY();
                    lastAction = MotionEvent.ACTION_MOVE;
                    isPulling = true;
                }
            }
        } else if (lastAction == MotionEvent.ACTION_MOVE) {
            if (isTop) {
                // 下拉
                configureHeader(true);
                distanceY = ev.getY() - pullStartY;
                L.d(TAG, distanceY + "");
                if (distanceY > 0) {
                    distanceY = (float) (Math.exp(-ev.getY() / pullStartY / 40) * distanceY);
                    // 在下拉状态时取消系统对move动作的响应,完全由本类响应
                    ev.setAction(MotionEvent.ACTION_DOWN);
                } else {
                    distanceY = 0;
                    // 在下拉过程中往上拉动该listView使得其回到顶部位置,则将该move动作交由系统进行响应
                    ev.setAction(MotionEvent.ACTION_MOVE);
                }
            }
            if (isBottom) {
                // 上拉
                configureHeader(false);
                distanceY = ev.getY() - pullStartY;
                L.d(TAG, distanceY + "");
                if (distanceY < 0) {
                    distanceY = (float) (Math.exp(-pullStartY / ev.getY() / 40) * distanceY);
                    // 在上拉状态时取消系统对move动作的响应,完全由本类响应
                    ev.setAction(MotionEvent.ACTION_DOWN);
                } else {
                    distanceY = 0;
                    // 在上拉过程中往上拉动该listView使得其回到顶部位置,则将该move动作交由系统进行响应
                    ev.setAction(MotionEvent.ACTION_MOVE);
                }
            }
            if (!isTop && !isBottom){
                // 在下拉过程中往上拉动listView使listView往下滚动到其没有滚动到顶部,则取消其下拉状态,回到手指按下的初始状态
                lastAction = MotionEvent.ACTION_DOWN;
                isPulling = false;
                distanceY = 0;
            }
        }
        return super.onTouchEvent(ev);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);

        if (distanceY != 0) {
            if (refreshDrawable == null) {
                refreshDrawable = onLoadCallBack.refreshDrawable();
            }
            if (refreshDrawable == null) {
                canvas.drawColor(Color.GRAY);
            } else {
                int left = getPaddingLeft();
                int top = getPaddingTop();
                refreshDrawable.setBounds(left, top, getWidth()+left, getHeight()+top);
                refreshDrawable.draw(canvas);
            }
            // 重画子view
            int left = getPaddingLeft();
            int top = getPaddingTop();
            int bottom = getPaddingBottom();
            canvas.save();
            if (distanceY > 0) {
                canvas.translate(left, top + distanceY);
            } else {
                canvas.translate(left, -bottom + distanceY);
            }
            for (int i=0;i<getChildCount();i++) {
                View child = getChildAt(i);
                drawChild(canvas, child, getDrawingTime());
            }
            canvas.restore();
            canvas.save();
            // 画刷新头
            View header;
            if (distanceY > 0) {
                int whereToLoad = dp2px(onLoadCallBack.whereToLoad(true));
                if (distanceY > whereToLoad) {
                    topRefreshHeaderView.setText(HeaderView.STATE_CAN_RELEASE);
                    topRefreshHeaderView.setIcon(HeaderView.STATE_CAN_RELEASE);
                } else if (distanceY < whereToLoad) {
                    topRefreshHeaderView.setText(HeaderView.STATE_PULLING_DOWN);
                    topRefreshHeaderView.setIcon(HeaderView.STATE_PULLING_DOWN);
                }
                header = topRefreshHeaderView.getView();
                canvas.translate(left, top + distanceY - header.getHeight() - offset);
            } else {
                int whereToLoad = dp2px(onLoadCallBack.whereToLoad(false));
                if (-distanceY > whereToLoad) {
                    bottomRefreshHeaderView.setText(HeaderView.STATE_CAN_RELEASE);
                    bottomRefreshHeaderView.setIcon(HeaderView.STATE_CAN_RELEASE);
                } else if (-distanceY < whereToLoad) {
                    bottomRefreshHeaderView.setText(HeaderView.STATE_PULLING_UP);
                    bottomRefreshHeaderView.setIcon(HeaderView.STATE_PULLING_UP);
                }
                header = bottomRefreshHeaderView.getView();
                canvas.translate(left, -bottom + distanceY + getHeight() + offset);
            }
            drawChild(canvas, header, getDrawingTime());
            canvas.restore();
        }
    }

    /**
     * 下拉结束时进行回滚动画并执行刷新动作
     * @param topOrBottom true for top, false for bottom
     */
    private void startAnimating(final boolean topOrBottom) {
        int whereToLoad = dp2px(onLoadCallBack.whereToLoad(topOrBottom));
        final boolean toLoad;

        if (distanceY > whereToLoad && topOrBottom) {
            // 下拉
            pullCancelAnimator = ValueAnimator.ofFloat(distanceY, whereToLoad);
            toLoad = true;
        } else if (-distanceY > whereToLoad && !topOrBottom) {
            // 上拉
            pullCancelAnimator = ValueAnimator.ofFloat(distanceY, -whereToLoad);
            toLoad = true;
        } else {
            // 回滚
            pullCancelAnimator = ValueAnimator.ofFloat(distanceY, 0);
            toLoad = false;
        }

        pullCancelAnimator.setDuration((long) (DEFAULT_BASE_ANIMATING_TIME_PER_100DP*px2dp(Math.abs(distanceY))/100));
        pullCancelAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        pullCancelAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                distanceY = (float) animation.getAnimatedValue();
                ViewCompat.postInvalidateOnAnimation(PullToRefreshListView.this);
            }
        });
        pullCancelAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                post(new Runnable() {
                    @Override
                    public void run() {
                        if (topOrBottom) {
                            topRefreshHeaderView.setText(HeaderView.STATE_REFRESHING);
                            topRefreshHeaderView.setIcon(HeaderView.STATE_REFRESHING);
                        } else {
                            bottomRefreshHeaderView.setText(HeaderView.STATE_REFRESHING);
                            bottomRefreshHeaderView.setIcon(HeaderView.STATE_REFRESHING);
                        }
                        pullCancelAnimator = null;
                        if (toLoad) {
                            onLoadCallBack.onLoad(topOrBottom);
                        }
                    }
                });
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                post(new Runnable() {
                    @Override
                    public void run() {
                        pullCancelAnimator = null;
                        if (toLoad) {
                            onLoadCallBack.cancelLoad(topOrBottom);
                        }
                    }
                });
            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        pullCancelAnimator.start();
    }

    /**
     * 取消回滚动画
     */
    private void cancelAnimating() {
        if (pullCancelAnimator != null) {
            pullCancelAnimator.cancel();
        }
    }

    private float px2dp(float pxvalue) {
        return (pxvalue - 0.5f) /context.getResources().getDisplayMetrics().density;
    }

    private int dp2px(float dpvalue) {
        return (int) (dpvalue * context.getResources().getDisplayMetrics().density + 0.5f);
    }

    /**
     * 下拉刷新的回调
     */
    public static abstract class OnLoadCallBack {

        private PullToRefreshListView listView;

        public OnLoadCallBack(PullToRefreshListView lv) {
            this.listView = lv;
        }

        /**
         * 下拉结束后将listView定位到哪个位置等待刷新完成
         * @param topOrBottom true for top, false for bottom
         * @return listView的定位y坐标值,in dp
         */
        public int whereToLoad(boolean topOrBottom) {
            return DEFAULT_WHERE_TO_LOAD;
        }

        /**
         * 下拉结束后进行刷新的回调
         * @param topOrBottom true for top, false for bottom
         */
        public abstract void onLoad(boolean topOrBottom);

        /**
         * 取消刷新
         * @param topOrBottom true for top, false for bottom
         */
        public abstract void cancelLoad(boolean topOrBottom);

        /**
         * 下拉刷新的背景
         * @return 背景drawable
         */
        public Drawable refreshDrawable() {
            return new ColorDrawable(Color.GRAY);
        }

        /**
         * 自定义刷新头
         * @param topOrBottom true for top, false for bottom
         */
        public HeaderView getHeaderView(boolean topOrBottom) {
            if (topOrBottom) {
                listView.destroyTopRefreshHeaderView();
            } else {
                listView.destroyBottomRefreshHeaderView();
            }
            return null;
        }
    }

    /**
     * 设置下拉刷新回调
     * @param cb 回调
     */
    public void setOnLoadCallBack(OnLoadCallBack cb) {
        this.onLoadCallBack = cb;
    }

    /**
     * 刷新动作结束后调用该方法结束刷新,使得listView回滚到顶部
     */
    public void setLoadingFinish() {
        startAnimating(distanceY>0);
    }

    /**
     * 刷新头的抽象类
     */
    public abstract class HeaderView {
        private View headerView;
        public static final int STATE_PULLING_UP = 0x30;
        public static final int STATE_PULLING_DOWN = 0x31;
        public static final int STATE_CAN_RELEASE = 0x32;
        public static final int STATE_REFRESHING = 0x33;

        public HeaderView(View headerView) {
            this.headerView = headerView;
        }

        /**
         * 获得刷新头的View
         * @return 实例化是给予的一个自定义View
         */
        protected View getView() {
            return headerView;
        }

        /**
         * 根据状态设置显示内容
         * @param state 状态
         */
        protected abstract void setText(int state);

        /**
         * 根据状态设置显示图标
         * @param state 状态
         */
        protected abstract void setIcon(int state);
    }

    protected void destroyTopRefreshHeaderView() {
        topRefreshHeaderView = null;
    }

    protected void destroyBottomRefreshHeaderView() {
        bottomRefreshHeaderView = null;
    }

}


你可能感兴趣的:(android,下拉刷新,上拉刷新)