Android——终极版上拉刷新下拉加载(兼容ScrlooView、ListView、GridView以及各类布局)

来源:互联网 发布:史学方法导论知乎 编辑:程序博客网 时间:2024/05/17 08:52

1、上拉下载展示的页面可以自己定制,只需要需改头部或者底部的布局就ok了
2、可以根据需求实现下拉刷新上拉加载,也可以只实现下拉刷新,对上拉进行屏蔽。反之同理。
3、布局中不仅可包含ListView、GridView还能包含其他内容

使用方法:
1、上拉刷新:头部刷新利用PullToRefreshView,实现它的onHeaderRefresh

  mPullToRefreshView.setOnHeaderRefreshListener(this);      @Override    public void onHeaderRefresh(PullToRefreshView view) {        initData();    }

2、下拉加载:当滑动到底部时使用ListView等控件的OnScrollListener方法,监听到显示最后一项时,就加载数据。如下

 listview.setOnScrollListener(this);  @Override    public void onScrollStateChanged(AbsListView absListView, int scrollState) {        if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && (lastVisibileItem + 1) == projectlv.getCount()) {        //加载数据的方法            initData();        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {        lastVisibileItem = firstVisibleItem + visibleItemCount - 1;    }

附PullToRefreshView的源码

package com.ygline.frbao.view;import android.content.Context;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.animation.LinearInterpolator;import android.view.animation.RotateAnimation;import android.widget.AdapterView;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ProgressBar;import android.widget.ScrollView;import android.widget.TextView;import com.XX.R;public class PullToRefreshView extends LinearLayout{    //  private static final String TAG = "PullToRefreshView";    // refresh states    private static final int PULL_TO_REFRESH = 2;    private static final int RELEASE_TO_REFRESH = 3;    private static final int REFRESHING = 4;    // pull state    private static final int PULL_UP_STATE = 0;    private static final int PULL_DOWN_STATE = 1;    /**     * last y     */    private int mLastMotionY;    private int mLastMotionX;    /**     * lock     */    //  private boolean mLock;    /**     * header view     */    private View mHeaderView;    /**     * footer view     */    private View mFooterView;    /**     * list or grid     */    private AdapterView<?> mAdapterView;    /**     * scrollview     */    private ScrollView mScrollView;    /**     * header view height     */    private int mHeaderViewHeight;    /**     * footer view height     */    private int mFooterViewHeight;    /**     * header view image     */    private ImageView mHeaderImageView;    /**     * footer view image     */    private ImageView mFooterImageView;    /**     * header tip text     */    private TextView mHeaderTextView;    /**     * footer tip text     */    private TextView mFooterTextView;    /**     * header refresh time     */    private TextView mHeaderUpdateTextView;    /**     * footer refresh time     */    // private TextView mFooterUpdateTextView;    /**     * header progress bar     */    private ProgressBar mHeaderProgressBar;    /**     * footer progress bar     */    private ProgressBar mFooterProgressBar;    /**     * layout inflater     */    private LayoutInflater mInflater;    /**     * header view current state     */    private int mHeaderState;    /**     * footer view current state     */    private int mFooterState;    /**     * pull state,pull up or pull down;PULL_UP_STATE or PULL_DOWN_STATE     */    private int mPullState;    /**     * 变为向下的箭头,改变箭头方向     */    private RotateAnimation mFlipAnimation;    /**     * 变为逆向的箭头,旋转     */    private RotateAnimation mReverseFlipAnimation;    /**     * footer refresh listener     */    private OnFooterRefreshListener mOnFooterRefreshListener;    /**     * footer refresh listener     */    private OnHeaderRefreshListener mOnHeaderRefreshListener;    /**     * last update time     */    //  private String mLastUpdateTime;    private int position = 20;    public PullToRefreshView(Context context, AttributeSet attrs)    {        super(context, attrs);        init();    }    public PullToRefreshView(Context context)    {        super(context);        init();    }    private void init()    {        //需要设置成vertical        setOrientation(LinearLayout.VERTICAL);        // Load all of the animations we need in code rather than through XML        mFlipAnimation =                new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);        mFlipAnimation.setInterpolator(new LinearInterpolator());        mFlipAnimation.setDuration(250);        mFlipAnimation.setFillAfter(true);        mReverseFlipAnimation =                new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);        mReverseFlipAnimation.setInterpolator(new LinearInterpolator());        mReverseFlipAnimation.setDuration(250);        mReverseFlipAnimation.setFillAfter(true);        mInflater = LayoutInflater.from(getContext());        // header view 在此添加,保证是第一个添加到linearlayout的最上端        addHeaderView();    }    private void addHeaderView()    {        // header view        mHeaderView = mInflater.inflate(R.layout.refresh_header, this, false);        mHeaderImageView = (ImageView) mHeaderView.findViewById(R.id.pull_to_refresh_image);        mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_text);        mHeaderUpdateTextView = (TextView) mHeaderView.findViewById(R.id.pull_to_refresh_updated_at);        mHeaderProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.pull_to_refresh_progress);        // header layout        measureView(mHeaderView);        mHeaderViewHeight = mHeaderView.getMeasuredHeight();        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mHeaderViewHeight);        // 设置topMargin的值为负的header View高度,即将其隐藏在最上方        params.topMargin = -(mHeaderViewHeight);        // mHeaderView.setLayoutParams(params1);        addView(mHeaderView, params);    }    private void addFooterView()    {        // footer view        mFooterView = mInflater.inflate(R.layout.refresh_footer, this, false);        mFooterView.setVisibility(View.GONE);        mFooterImageView = (ImageView) mFooterView.findViewById(R.id.pull_to_load_image);        mFooterTextView = (TextView) mFooterView.findViewById(R.id.pull_to_load_text);        mFooterProgressBar = (ProgressBar) mFooterView.findViewById(R.id.pull_to_load_progress);        // footer layout        measureView(mFooterView);        mFooterViewHeight = mFooterView.getMeasuredHeight();        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mFooterViewHeight);        // int top = getHeight();        // params.topMargin        // =getHeight();//在这里getHeight()==0,但在onInterceptTouchEvent()方法里getHeight()已经有值了,不再是0;        // getHeight()什么时候会赋值,稍候再研究一下        // 由于是线性布局可以直接添加,只要AdapterView的高度是MATCH_PARENT,那么footer view就会被添加到最后,并隐藏        addView(mFooterView, params);    }    @Override    protected void onFinishInflate()    {        super.onFinishInflate();        // footer view 在此添加保证添加到linearlayout中的最后        addFooterView();        initContentAdapterView();    }    /**     * init AdapterView like ListView,GridView and so on;or init ScrollView     */    private void initContentAdapterView()    {        int count = getChildCount();        if (count < 3)        {            throw new IllegalArgumentException(                    "This layout must contain 3 child views,and AdapterView or ScrollView must in the second position!");        }        View view = null;        for (int i = 0; i < count - 1; ++i)        {            view = getChildAt(i);            if (view instanceof AdapterView<?>)            {                mAdapterView = (AdapterView<?>) view;            }            if (view instanceof ScrollView)            {                // finish later                mScrollView = (ScrollView) view;            }        }        if (mAdapterView == null && mScrollView == null)        {            throw new IllegalArgumentException("must contain a AdapterView or ScrollView in this layout!");        }    }    private void measureView(View child)    {        ViewGroup.LayoutParams p = child.getLayoutParams();        if (p == null)        {            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);        }        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);        int lpHeight = p.height;        int childHeightSpec;        if (lpHeight > 0)        {            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);        } else        {            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);        }        child.measure(childWidthSpec, childHeightSpec);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent e)    {        int y = (int) e.getRawY();        int x = (int) e.getRawX();        switch (e.getAction())        {            case MotionEvent.ACTION_DOWN:                // 首先拦截down事件,记录y坐标                mLastMotionY = y;                mLastMotionX = x;                break;            case MotionEvent.ACTION_MOVE:                // deltaY > 0 是向下运动,< 0是向上运动                int deltaY = y - mLastMotionY;                int deltaX = x - mLastMotionX;                if ((Math.abs(deltaX) - Math.abs(deltaY)) > 0)                {                    return false;                }                if ((Math.abs(deltaY)) < 10)                {                    return false;                }                if (isRefreshViewScroll(deltaY))                {                    return true;                }                break;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL:                break;        }        return false;    }    /*     * 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return     * false)则由PullToRefreshView 的子View来处理;否则由下面的方法来处理(即由PullToRefreshView自己来处理)     */    @Override    public boolean onTouchEvent(MotionEvent event)    {        //      if (mLock) {        //          return true;        //      }        int y = (int) event.getRawY();        switch (event.getAction())        {            case MotionEvent.ACTION_DOWN:                // onInterceptTouchEvent已经记录                // mLastMotionY = y;                break;            case MotionEvent.ACTION_MOVE:                int deltaY = y - mLastMotionY;                if (mPullState == PULL_DOWN_STATE)                {//执行下拉                    headerPrepareToRefresh(deltaY);                    // setHeaderPadding(-mHeaderViewHeight);                } else if (mPullState == PULL_UP_STATE)                {//执行上拉                    //                    footerPrepareToRefresh(deltaY);                }                mLastMotionY = y;                break;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL:                int topMargin = getHeaderTopMargin();                if (mPullState == PULL_DOWN_STATE)                {                    if (topMargin >= 0)                    {                        // 开始刷新                        headerRefreshing();                    } else                    {                        // 还没有执行刷新,重新隐藏                        setHeaderTopMargin(-mHeaderViewHeight);                    }                } else if (mPullState == PULL_UP_STATE)                {                    if (Math.abs(topMargin) >= mHeaderViewHeight + mFooterViewHeight)                    {                        // 开始执行footer 刷新                        //                        footerRefreshing();                    } else                    {                        // 还没有执行刷新,重新隐藏                        setHeaderTopMargin(-mHeaderViewHeight);                    }                }                break;        }        return super.onTouchEvent(event);    }    /**     * 是否应该到了父View,即PullToRefreshView滑动     *     * @param deltaY , deltaY > 0 是向下运动,< 0是向上运动     * @return     */    private boolean isRefreshViewScroll(int deltaY)    {        if (mHeaderState == REFRESHING || mFooterState == REFRESHING)        {            return false;        }        //对于ListView和GridView        if (mAdapterView != null)        {            // 子view(ListView or GridView)滑动到最顶端            if (deltaY > 0)            {                View child = mAdapterView.getChildAt(0);                if (child == null)                {                    // 如果mAdapterView中没有数据,不拦截                    return false;                }                if (mAdapterView.getFirstVisiblePosition() == 0 && child.getTop() == 0)                {                    mPullState = PULL_DOWN_STATE;                    return true;                }                int top = child.getTop();                int padding = mAdapterView.getPaddingTop();                if (mAdapterView.getFirstVisiblePosition() == 0 && Math.abs(top - padding) <= 8)                {//这里之前用3可以判断,但现在不行,还没找到原因                    mPullState = PULL_DOWN_STATE;                    return true;                }            } else if (deltaY < 0)            {                View lastChild = mAdapterView.getChildAt(mAdapterView.getChildCount() - 1);                if (lastChild == null)                {                    // 如果mAdapterView中没有数据,不拦截                    return false;                }                // 最后一个子view的Bottom小于父View的高度说明mAdapterView的数据没有填满父view,                // 等于父View的高度说明mAdapterView已经滑动到最后                if (lastChild.getBottom() <= getHeight()                        && mAdapterView.getLastVisiblePosition() == mAdapterView.getCount() - 1)                {                    mPullState = PULL_UP_STATE;                    return true;                }            }        }        // 对于ScrollView        if (mScrollView != null)        {            // 子scroll view滑动到最顶端            View child = mScrollView.getChildAt(0);            if (deltaY > 0 && mScrollView.getScrollY() == 0)            {                mPullState = PULL_DOWN_STATE;                return true;            } else if (deltaY < 0 && child.getMeasuredHeight() <= getHeight() + mScrollView.getScrollY())            {                mPullState = PULL_UP_STATE;                return true;            }        }        return false;    }    /**     * header 准备刷新,手指移动过程,还没有释放     *     * @param deltaY ,手指滑动的距离     */    private void headerPrepareToRefresh(int deltaY)    {        int newTopMargin = changingHeaderViewTopMargin(deltaY);        // 当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态        if (newTopMargin >= 0 && mHeaderState != RELEASE_TO_REFRESH)        {            mHeaderTextView.setText(R.string.pull_to_refresh_release_label);            mHeaderUpdateTextView.setVisibility(View.VISIBLE);            mHeaderImageView.clearAnimation();            mHeaderImageView.startAnimation(mFlipAnimation);            mHeaderState = RELEASE_TO_REFRESH;        } else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight)        {// 拖动时没有释放            mHeaderImageView.clearAnimation();            mHeaderImageView.startAnimation(mFlipAnimation);            // mHeaderImageView.            mHeaderTextView.setText(R.string.pull_to_refresh_pull_label);            mHeaderState = PULL_TO_REFRESH;        }    }    /**     * footer 准备刷新,手指移动过程,还没有释放 移动footer view高度同样和移动header view     * 高度是一样,都是通过修改header view的topmargin的值来达到     *     * @param deltaY ,手指滑动的距离     */    private void footerPrepareToRefresh(int deltaY)    {        int newTopMargin = changingHeaderViewTopMargin(deltaY);        // 如果header view topMargin 的绝对值大于或等于header + footer 的高度        // 说明footer view 完全显示出来了,修改footer view 的提示状态        if (Math.abs(newTopMargin) >= (mHeaderViewHeight + mFooterViewHeight) && mFooterState != RELEASE_TO_REFRESH)        {            mFooterTextView.setText(R.string.pull_to_refresh_footer_release_label);            mFooterImageView.clearAnimation();            mFooterImageView.startAnimation(mFlipAnimation);            mFooterState = RELEASE_TO_REFRESH;        } else if (Math.abs(newTopMargin) < (mHeaderViewHeight + mFooterViewHeight))        {            mFooterImageView.clearAnimation();            mFooterImageView.startAnimation(mFlipAnimation);            mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label);            mFooterState = PULL_TO_REFRESH;        }    }    /**     * 修改Header view top margin的值     *     * @param deltaY     */    private int changingHeaderViewTopMargin(int deltaY)    {        LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();        float newTopMargin = params.topMargin + deltaY * 0.3f;        //这里对上拉做一下限制,因为当前上拉后然后不释放手指直接下拉,会把下拉刷新给触发了,感谢网友yufengzungzhe的指出        //表示如果是在上拉后一段距离,然后直接下拉        if (deltaY > 0 && mPullState == PULL_UP_STATE && Math.abs(params.topMargin) <= mHeaderViewHeight)        {            return params.topMargin;        }        //同样地,对下拉做一下限制,避免出现跟上拉操作时一样的bug        if (deltaY < 0 && mPullState == PULL_DOWN_STATE && Math.abs(params.topMargin) >= mHeaderViewHeight)        {            return params.topMargin;        }        params.topMargin = (int) newTopMargin;        mHeaderView.setLayoutParams(params);        invalidate();        return params.topMargin;    }    /**     * header refreshing     */    private void headerRefreshing()    {        mHeaderState = REFRESHING;        setHeaderTopMargin(0);        mHeaderImageView.setVisibility(View.GONE);        mHeaderImageView.clearAnimation();        mHeaderImageView.setImageDrawable(null);        mHeaderProgressBar.setVisibility(View.VISIBLE);        mHeaderTextView.setText(R.string.pull_to_refresh_refreshing_label);        if (mOnHeaderRefreshListener != null)        {            mOnHeaderRefreshListener.onHeaderRefresh(this);        }    }    /**     * footer refreshing     */    private void footerRefreshing()    {        mFooterState = REFRESHING;        int top = mHeaderViewHeight + mFooterViewHeight;        setHeaderTopMargin(-top);        mFooterImageView.setVisibility(View.GONE);        mFooterImageView.clearAnimation();        mFooterImageView.setImageDrawable(null);        mFooterProgressBar.setVisibility(View.VISIBLE);        mFooterTextView.setText(R.string.pull_to_refresh_footer_refreshing_label);        if (mOnFooterRefreshListener != null)        {            mOnFooterRefreshListener.onFooterRefresh(this);        }    }    /**     * 设置header view 的topMargin的值     *     * @param topMargin ,为0时,说明header view 刚好完全显示出来; 为-mHeaderViewHeight时,说明完全隐藏了     */    private void setHeaderTopMargin(int topMargin)    {        LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();        params.topMargin = topMargin;        mHeaderView.setLayoutParams(params);        invalidate();    }    /**     * header view 完成更新后恢复初始状态     */    public void onHeaderRefreshComplete()    {        setHeaderTopMargin(-mHeaderViewHeight);        mHeaderImageView.setVisibility(View.VISIBLE);        mHeaderImageView.setImageResource(R.mipmap.ic_pulltorefresh_arrow);        mHeaderTextView.setText(R.string.pull_to_refresh_pull_label);        mHeaderProgressBar.setVisibility(View.GONE);        // mHeaderUpdateTextView.setText("");        mHeaderState = PULL_TO_REFRESH;    }    /**     * Resets the list to a normal state after a refresh.     *     * @param lastUpdated Last updated at.     */    public void onHeaderRefreshComplete(CharSequence lastUpdated)    {        setLastUpdated(lastUpdated);        onHeaderRefreshComplete();    }    /**     * footer view 完成更新后恢复初始状态     */    public void onFooterRefreshComplete()    {        setHeaderTopMargin(-mHeaderViewHeight);        mFooterImageView.setVisibility(View.VISIBLE);        mFooterImageView.setImageResource(R.mipmap.ic_pulltorefresh_arrow_up);        mFooterTextView.setText(R.string.pull_to_refresh_footer_pull_label);        mFooterProgressBar.setVisibility(View.GONE);        // mHeaderUpdateTextView.setText("");        mFooterState = PULL_TO_REFRESH;    }    /**     * Set a text to represent when the list was last updated.     *     * @param lastUpdated Last updated at.     */    public void setLastUpdated(CharSequence lastUpdated)    {        if (lastUpdated != null)        {            mHeaderUpdateTextView.setVisibility(View.VISIBLE);            mHeaderUpdateTextView.setText(lastUpdated);        } else        {            mHeaderUpdateTextView.setVisibility(View.GONE);        }    }    /**     * 获取当前header view 的topMargin     */    private int getHeaderTopMargin()    {        LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();        return params.topMargin;    }    //  /**    //   * lock    //   *     //   */    //  private void lock() {    //      mLock = true;    //  }    //    //  /**    //   * unlock    //   *     //   */    //  private void unlock() {    //      mLock = false;    //  }    /**     * set headerRefreshListener     *     * @param headerRefreshListener     */    public void setOnHeaderRefreshListener(OnHeaderRefreshListener headerRefreshListener)    {        mOnHeaderRefreshListener = headerRefreshListener;    }    public void setOnFooterRefreshListener(OnFooterRefreshListener footerRefreshListener)    {        mOnFooterRefreshListener = footerRefreshListener;    }    /**     * Interface definition for a callback to be invoked when list/grid footer     * view should be refreshed.     */    public interface OnFooterRefreshListener    {        public void onFooterRefresh(PullToRefreshView view);    }    /**     * Interface definition for a callback to be invoked when list/grid header     * view should be refreshed.     */    public interface OnHeaderRefreshListener    {        public void onHeaderRefresh(PullToRefreshView view);    }}
0 0