ListView下拉刷新

来源:互联网 发布:西安软件开发外包公司 编辑:程序博客网 时间:2024/06/07 14:36

       不知为何,一直对ListView的下拉刷新情有独钟。或许是在项目开发过程中,使用的频率太多了吧。ListView下拉刷新,和自动加载更多,对于大多数人来说,是一个头疼的问题。很多时候,我们都选择使用框架去实现下拉刷新,加载更多,无奈,很多框架很难修改成自己所需要的效果。如果这时候,自己有一个自己的下拉刷新框架,该有多好!我也曾使用过很多的下拉刷新框架,经过自己对这些框架的使用经历,我发现该如何做一个下拉刷新框架才是最好的。这里,我将介绍如何去简单的定义出一个流畅的下拉刷新ListView。

          先预览demo的效果


         在介绍ListView编写之前,有一些知识是需要先了解的。

          1.我们将使用LinearLayout里面嵌套ListView组合来实现。因此需要熟悉事件分发机制。这里,我们通过重写LinearLayout的   onInterceptTouchEvent 方法,来进行事件的拦截。如果当前LinearLayout需要事件,来进行下拉刷新,则onInterceptTouchEvent返回true;如果当前不需要下拉刷新,则返回false,事件交给ListView处理,此时ListView就可以滑动了。

          2.在LinearLayout中,调用scrollTo()方法,对ListView进行滑动。

          3.弹性滑动,ListView下拉刷新后,需要弹性回复,使用Scroller来完成该功能。

          4.我将使用mScrollY来进行下拉刷新的判断依据。mScollY初始值为0,随着调用scrollTo()下拉,将会逐渐变小。

         所以,要完成ListView的下拉刷新,需要掌握android事件分发机制,View的scrollTo()方法,Scroller弹性滑动。     


        我将通过代码的方式,使用注释来说明:

package com.mjc.pulltorefreshdemo.refresh;import android.content.Context;import android.graphics.Color;import android.graphics.drawable.ColorDrawable;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.view.ViewTreeObserver;import android.widget.AbsListView;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.Scroller;import static android.view.GestureDetector.*;/** * Created by mjc on 2015/12/26. */public class PullToRefreshViewListView extends LinearLayout implements AbsListView.OnScrollListener {    private static final String TAG = "PullToRefreshViewGroup";    //暂时未用到    private IFooterView mFooterView;    //自定义的一个布局,包含了刷新时,下拉时动画等方法。    private IHeaderView mHeaderView;    //HeaderView的高度    private int mHeaderViewHeight;    private ListView mListView;    //当前listview是否允许下拉    private boolean isPullDownEnable = false;    //按下时的坐标    private float downY = 0;    //阻尼系数    private float rate = 0.4f;    //弹性滑动需要使用Scroller    private Scroller mScroller;    //向下滑动,mScrollY变小;滑动临界值    private int mMaxScrollHeight;    //表示是否正在刷新    private boolean isRefreshing = false;    //记录当前的一个mScrollY的值,做判断    private int mScrollTop;    private int mTouchSlop;    public PullToRefreshViewListView(Context context) {        super(context);        init(context);    }    public PullToRefreshViewListView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    private void init(Context context) {        this.setOrientation(VERTICAL);        mListView = new ListView(context);        LayoutParams listParams = new LayoutParams(-1, -1);        mListView.setLayoutParams(listParams);        mListView.setOnScrollListener(this);        addView(mListView);        //添加所有的子View        mHeaderView = new CustomHeaderView(context);        LayoutParams params = new LayoutParams(-1, -2);        mHeaderView.setLayoutParams(params);        addView(mHeaderView, 0);        //getViewTreeObserver()来获取我们的headerView的高度        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                //获取各个视图的高度                mHeaderViewHeight = mHeaderView.getHeight();                mHeaderView.getViewTreeObserver()                        .removeGlobalOnLayoutListener(this);                //隐藏headerView                HideHeaderView(-mHeaderViewHeight);                //初始化我们需要的当前滑动距离和滑动临界点                mScrollTop = 0;                mMaxScrollHeight = mHeaderViewHeight * 2;            }        });        mScroller = new Scroller(context);        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();    }    /**     * 我们需要隐藏HeaderView     *     * @param margin     */    private void HideHeaderView(int margin) {        LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();        int topMargin = params.topMargin;        int newMargin = topMargin + margin;        params.topMargin = newMargin;        mHeaderView.requestLayout();    }    public View getRefreshView() {        return mListView;    }    /**     * 用来确定是否允许事件传递给ListView     *     * @param ev     * @return     */    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        //getScrollY()<0,表示当前正在下拉刷新的过程中,事件需要拦截,由LinearLayout处理        boolean isHandled = false;        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                //ACTION_DOWN触发时,无法判断是否拦截事件                downY = ev.getY();                isHandled = false;                break;            case MotionEvent.ACTION_MOVE:                float dY = ev.getY() - downY;                //isPullDownEnable在ListView的onScrollChange方法中,监听,达到了ListView的最顶端,允许拉下                if (isPullDownEnable && dY >= mTouchSlop) {                    isHandled = true;                }                if(dY<mTouchSlop){                    isHandled =false;                }        }        return isHandled;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                downY = event.getY();                break;            case MotionEvent.ACTION_MOVE:                float moveY = event.getY();                float delta = (moveY - downY);                //获取需要滑动到的位置,mScrollTop为滑动前的位置,其值等于getScrollY()。                delta = mScrollTop - rate * delta;                /**                 * della为滑动的目标位置。可以取值范围为, -mHeaderViewHeight - mMaxScrollHeight到0                 * 这里,0到-mHeaderViewHeight表示下拉刷新状态                 *   -mHeaderViewHeight到 -mHeaderViewHeight - mMaxScrollHeight,表示松开刷新状态                 *   因为mScrollY向正方向滑动时,是负值,逐渐变小的过程                 *   表示为:0 ----下拉刷新状态----  (-mHeaderViewHeight)  ----松开刷新--- (-mHeaderViewHeight - mMaxScrollHeight)                 */                //如果目标位置大于0,则让它等于0                if (delta >= 0) {                    delta = 0;                }                if (delta <= -mHeaderViewHeight - mMaxScrollHeight) {                    delta = -mHeaderViewHeight - mMaxScrollHeight;                }                //滑动到对应位置                scrollTo(0, (int) delta);                //根据位置更新状态                resetRefreshState((int) delta);                return true;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL:                //记录下次要布局的位置                mScrollTop = getScrollY();                //根据位置处理结果                doReslut(mScrollTop);                break;        }        return true;    }    /**     * 处理松开后的结果     *     * @param mScrollTop -mHeaderViewHeight - mMaxScrollHeight  -松开刷新->-mHeaderViewHeight-下拉刷新->0     */    private void doReslut(int mScrollTop) {        if (mScrollTop < 0 && mScrollTop > -mHeaderViewHeight) {            //当前为下拉刷新状态,弹性回复到初始状态            //todo 弹性回复到正常状态            if (!isRefreshing)                mHeaderView.onNormal();            smoothScrollTo(0);        } else if (mScrollTop >= -mHeaderViewHeight - mMaxScrollHeight && mScrollTop <= -mHeaderViewHeight) {            //todo 弹性滑动到-mHeaderViewHeight位置,然后开始刷新            smoothScrollTo(-mHeaderViewHeight);            //正在刷新            mHeaderView.onRefreshing();            if (isRefreshing) {                //如果当前正在刷新,无作为            } else {                //如果当前没有刷新,刷新                isRefreshing = true;                if (mRefresh != null)                    mRefresh.onPullDownRefresh();            }        }    }    /**     * @param delta 值为  -mHeaderViewHeight - mMaxScrollHeight  -松开刷新->-mHeaderViewHeight-下拉刷新->0     */    private void resetRefreshState(float delta) {        if (isRefreshing) {            return;        }        if (delta > -mHeaderViewHeight && delta <= 0) {            //下拉刷新状态            mHeaderView.onPullToRefresh((int) -delta);            Log.v(TAG, "下拉刷新");        } else if (delta >= -mHeaderViewHeight - mMaxScrollHeight && delta <= -mHeaderViewHeight) {            //松开刷新            mHeaderView.onReleaseToRefresh((int) -delta);            Log.v(TAG, "松开刷新");        }    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {        //只有当第一个显示,并且getTop==0,才可以下拉        if (mListView.getChildAt(0) == null) {            isPullDownEnable = firstVisibleItem == 0;        } else {            //如果当前第一个可见的为0并且第一个item的gettop>=0            isPullDownEnable = firstVisibleItem == 0 && mListView.getChildAt(0).getTop() >= 0;        }    }    private IRefresh mRefresh;    public void setOnRefreshListener(IRefresh mRefresh) {        this.mRefresh = mRefresh;    }    public void onPullDownRefreshComplete() {        isRefreshing = false;        //todo 弹性从 -mHeaderViewHeight回复到0        smoothScrollTo(0);    }    /**     * 使用Scroller弹性滑动到指定位置     *     * @param scrollValue     */    private void smoothScrollTo(int scrollValue) {        mScroller.startScroll(0, mScrollTop, 0, scrollValue - mScrollTop, 500);        //主动要求重绘视图        invalidate();    }    /**     * Scroller需要配合重写这个方法才能实现弹性滑动     */    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            Log.e(TAG, "" + mScroller.getCurrY());            scrollTo(0, mScroller.getCurrY());            mScrollTop = mScroller.getCurrY();            postInvalidate();        }    }}


        

         整个下拉刷新的流程,其实很简单。当ListView滑动到最顶端,通过判断是否向下滑动,来判断是否拦截事件,如果拦截事件,当前Linearlayout处理滑动;处理滑动,通过监听手指滑动的距离,使用scrollTo()来滑动Linearlayout的内容;并使用Scroller来进行弹性回复。在headerView中,我们可以自定义各种各样的动画,大家可以进行尝试。


         最后,我会将代码打包,,上传到csdn。有什么错误,希望大家指正。

      源码下载地址:点击打开链接











                                             
2 0
原创粉丝点击