android开发中ListView的妙用之:下拉刷新与到底部载入更多源码

来源:互联网 发布:域名被微信封了怎么办 编辑:程序博客网 时间:2024/04/29 17:09
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">今天项目也快结束了,就写篇博客吧,记录一下我对ListView的下拉刷新与底部载入更多的一点理解[/b]</span>
首先我们知道ListView是没有下拉刷新的接口的,我们要借助现有的功能去扩展更多的功能我们就需要对ListView做一点修改,使它满足我们的需求:
一、ListView的表面理解就是List的格式显示的View,也就是要一行一行的显示,对ListView添加适配器这是必不可少的。所以我们修改以后的ListView当然也可以添加适配器等等常见的对ListView的操作;
二、要捕捉我们对列表界面的操作,下拉或者滚动到底部必须要有对事件的监听器,这里就尤为重要了,我们知道对按钮添加监听器,我们可以捕获到我们对某个按钮施加了操作,然后就执行我们的操作,这里的下拉或者滚动到底部,我们当然也需要捕捉到这个动作,然后执行我们想要执行的动作;
三、原理我们知道啦,现在就剩下怎么去捕获这个动作,然后捕获动作后我们怎么添加入我们想要的操作。只要捕获到动作,你想干什么我不关心,因为需求各式各样;
四、在捕获到动作以后我们开始给用户点反应。。。。。
[b]下边我们来看看这个Demo的代码
一、继承ListView
package com.example.pulldownview;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.widget.ListView;/** * <p>一个可以监听ListView是否滚动到最顶部或最底部的自定义控件</p> * 只能监听由触摸产生的,如果是ListView本身Flying导致的,则不能监听</br> * 如果加以改进,可以实现监听scroll滚动的具体位置等 * @author solo ho</br> Email:kjsoloho@gmail.com */public class ScrollOverListView extends ListView {private static final String TAG = "ScrollOverListView";private static final boolean DEBUG = false;private int mLastY;private int mTopPosition;private int mBottomPosition;public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}public ScrollOverListView(Context context, AttributeSet attrs) {super(context, attrs);init();}public ScrollOverListView(Context context) {super(context);init();}private void init(){mTopPosition = 0;mBottomPosition = 0;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {if (ev.getAction() == MotionEvent.ACTION_DOWN) {if (DEBUG) Log.d(TAG, "onInterceptTouchEvent Action down");mLastY = (int) ev.getRawY();}return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {final int action = ev.getAction();final int y = (int) ev.getRawY();boolean isHandled = false;switch(action){case MotionEvent.ACTION_DOWN:{if (DEBUG) Log.d(TAG, "action down");mLastY = y;isHandled = mOnScrollOverListener.onMotionDown(ev);if (isHandled) {break;}break;}case MotionEvent.ACTION_MOVE:{if (DEBUG) Log.d(TAG, "action move");final int childCount = getChildCount();if(childCount == 0) {break;}final int itemCount = getAdapter().getCount() - mBottomPosition;final int deltaY = y - mLastY;if (DEBUG) Log.d(TAG, "lastY=" + mLastY +" y=" + y);final int firstTop = getChildAt(0).getTop();final int listPadding = getListPaddingTop();final int lastBottom = getChildAt(childCount - 1).getBottom();final int end = getHeight() - getPaddingBottom();final int firstVisiblePosition = getFirstVisiblePosition();isHandled = mOnScrollOverListener.onMotionMove(ev, deltaY);if(isHandled){break;}//DLog.d("firstVisiblePosition=%d firstTop=%d listPaddingTop=%d deltaY=%d", firstVisiblePosition, firstTop, listPadding, deltaY);if (firstVisiblePosition <= mTopPosition && firstTop >= listPadding && deltaY > 0) {if (DEBUG) Log.d(TAG, "action move pull down");            isHandled = mOnScrollOverListener.onListViewTopAndPullDown(ev, deltaY);            if(isHandled){            break;            }        }// DLog.d("lastBottom=%d end=%d deltaY=%d", lastBottom, end, deltaY);        if (firstVisiblePosition + childCount >= itemCount && lastBottom <= end && deltaY < 0) {        if (DEBUG) Log.d(TAG, "action move pull up");        isHandled = mOnScrollOverListener.onListViewBottomAndPullUp(ev, deltaY);        if(isHandled){        break;        }        }break;}case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:{if (DEBUG) Log.d(TAG, "action move pull up");isHandled = mOnScrollOverListener.onMotionUp(ev);if (isHandled) {break;}break;}}mLastY = y;if (isHandled) {return true;}return super.onTouchEvent(ev);}/**空的*/private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener(){@Overridepublic boolean onListViewTopAndPullDown(MotionEvent event, int delta) {return false;}@Overridepublic boolean onListViewBottomAndPullUp(MotionEvent event, int delta) {return false;}@Overridepublic boolean onMotionDown(MotionEvent ev) {return false;}@Overridepublic boolean onMotionMove(MotionEvent ev, int delta) {return false;}@Overridepublic boolean onMotionUp(MotionEvent ev) {return false;}};// =============================== public method ===============================/** * 可以自定义其中一个条目为头部,头部触发的事件将以这个为准,默认为第一个 *  * @param index 正数第几个,必须在条目数范围之内 */public void setTopPosition(int index){if(index < 0)throw new IllegalArgumentException("Top position must > 0");mTopPosition = index;}/** * 可以自定义其中一个条目为尾部,尾部触发的事件将以这个为准,默认为最后一个 *  * @param index 倒数第几个,必须在条目数范围之内 */public void setBottomPosition(int index){if(index < 0)throw new IllegalArgumentException("Bottom position must > 0");mBottomPosition = index;}/** * 设置这个Listener可以监听是否到达顶端,或者是否到达低端等事件</br> *  * @see OnScrollOverListener */public void setOnScrollOverListener(OnScrollOverListener onScrollOverListener){mOnScrollOverListener = onScrollOverListener;}/** * 滚动监听接口</br> * @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener) *  * @author solo ho</br> Email:kjsoloho@gmail.com */public interface OnScrollOverListener {/** * 到达最顶部触发 *  * @param delta 手指点击移动产生的偏移量 * @return  */boolean onListViewTopAndPullDown(MotionEvent event, int delta);/** * 到达最底部触发 *  * @param delta 手指点击移动产生的偏移量 * @return  */boolean onListViewBottomAndPullUp(MotionEvent event, int delta);/** * 手指触摸按下触发,相当于{@link MotionEvent#ACTION_DOWN} *  * @return 返回true表示自己处理 * @see View#onTouchEvent(MotionEvent) */boolean onMotionDown(MotionEvent ev);/** * 手指触摸移动触发,相当于{@link MotionEvent#ACTION_MOVE} *  * @return 返回true表示自己处理 * @see View#onTouchEvent(MotionEvent) */boolean onMotionMove(MotionEvent ev, int delta);/** * 手指触摸后提起触发,相当于{@link MotionEvent#ACTION_UP}  *  * @return 返回true表示自己处理 * @see View#onTouchEvent(MotionEvent) */boolean onMotionUp(MotionEvent ev);}}



二、下来刷新
package com.example.pulldownview;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Timer;import java.util.TimerTask;import android.content.Context;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.util.Log;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.animation.Animation;import android.view.animation.RotateAnimation;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import com.example.pulldownview.ScrollOverListView.OnScrollOverListener;/** * 下拉刷新控件</br> * 真正实现下拉刷新的是这个控件, * ScrollOverListView只是提供触摸的事件等 * @author Solo Email:kjsoloho@gmail.com */public class PullDownView extends LinearLayout implements OnScrollOverListener, OnScrollListener {private static final String TAG = "PullDownView";private static final boolean DEBUG = false;private static final int AUTO_INCREMENTAL = 10;// 自增量,用于回弹private static final int WHAT_SET_HEADER_HEIGHT = 1;// Handler what 设置高度private static SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm");private View mHeaderView;private LayoutParams mHeaderViewParams;private TextView mHeaderViewDateView;private TextView mHeaderTextView;private ImageView mHeaderArrowView;private View mHeaderLoadingView;private View mFooterView;private TextView mFooterTextView;private View mFooterLoadingView;private ScrollOverListView mListView;private OnPullDownListener mOnPullDownListener;private RotateAnimation mRotateOTo180Animation;private RotateAnimation mRotate180To0Animation;private Context mContext;private Field overScrollModeField;private int mMoveDeviation;// 移动误差private int mHeaderIncremental;// 头部文件自增量增量private int mDefaultHeaderViewHeight;// 头部文件原本的高度private int mStartIndex;// 当前List可显示第一项//private int mEndIndex;// 当前List可显示最后一项private float mMotionDownLastY;// 按下时候的Y轴坐标private boolean mEnablePullDown;// 是否可以下拉private boolean mIsPullUpDone;// 是否回推完成private boolean mEnableLoadMore;// 是否启用更多private boolean mEnableAutoFetchMore;// 是否允许自动获取更多private boolean mIsNoMoreData;// 没有更多的数据了private boolean mIsDidLoad;// 是否加载了数据// 头部文件的状态private static final int HEADER_VIEW_STATE_IDLE = 0;// 空闲private static final int HEADER_VIEW_STATE_NOT_OVER_HEIGHT = 1;// 没有超过默认高度private static final int HEADER_VIEW_STATE_OVER_HEIGHT = 2;// 超过默认高度private int mHeaderViewState = HEADER_VIEW_STATE_IDLE;private static final int STATE_NONE = 0;private static final int STATE_REFRESHING = 1;// 刷新中private static final int STATE_LOADING_MORE = 2;// 加载更多中private static final int STATE_DRAGING = 4;// 拖动中private static final int STATE_MOTION_DOWN = 8;// 按下private int state = STATE_NONE;public PullDownView(Context context, AttributeSet attrs) {super(context, attrs);initHeaderViewAndFooterViewAndListView(context);}public PullDownView(Context context) {super(context);initHeaderViewAndFooterViewAndListView(context);}/* * ================================== * Public method * 外部使用,具体就是用这几个就可以了 *  * ================================== *//** * 刷新事件接口 * @author Solo Email:kjsoloho@gmail.com */public interface OnPullDownListener {void onRefresh();void onLoadMore();}/** * 通知加载完了数据,要放在Adapter.notifyDataSetChanged后面 * 当你加载完数据的时候,调用这个notifyDidLoad() * 才会隐藏头部,并初始化数据等 */public void notifyDidDataLoad(boolean isNoMoreData) {mIsDidLoad = true;mIsNoMoreData = isNoMoreData;mFooterView.setVisibility(View.VISIBLE);updateFooter();mListView.setFooterDividersEnabled(true);mHeaderViewParams.height = 0;mHeaderView.setLayoutParams(mHeaderViewParams);updateHeader();doListViewIdleActionOnDataDidLoad();}/** * 通知已经刷新完了,要放在Adapter.notifyDataSetChanged后面 * 当你执行完刷新任务之后,调用这个notifyDidRefresh() * 才会隐藏掉头部文件等操作 */public void notifyDidRefresh(boolean isNoMoreData) {mIsNoMoreData = isNoMoreData;updateFooter();state &= ~STATE_REFRESHING;mHeaderViewState = HEADER_VIEW_STATE_IDLE;setHeaderHeight(0);updateHeader();doListViewIdleActionOnDataDidLoad();}/** * 通知已经获取完更多了,要放在Adapter.notifyDataSetChanged后面 * 当你执行完更多任务之后,调用这个notyfyDidMore() * 才会隐藏加载圈等操作 */public void notifyDidLoadMore(boolean isNoMoreData) {mIsNoMoreData = isNoMoreData;state &= ~STATE_LOADING_MORE;updateFooter();}/** * 设置监听器 * @param listener */public void setOnPullDownListener(OnPullDownListener listener){mOnPullDownListener = listener;}/** * 获取内嵌的listview * @return ScrollOverListView */public ScrollOverListView getListView(){return mListView;}/** * 是否开启自动获取更多</br> * 自动获取更多,并在触摸到达底部的时候自动刷新 * @param index 倒数第几个触发 */public void enableAutoFetchMore(boolean enable, int index){if(!mEnableLoadMore) return;mEnableAutoFetchMore = enable;if(enable){mListView.setBottomPosition(index);}else{updateFooter();}}/** * 是否启用加载更多</br> * 只能在初始化的时候调用 */public void enableLoadMore(boolean enable) {mEnableLoadMore = enable;if (!enable) {// TODO 暂时使用这种错开的方法// 更好的办法是写到属性里面mUIHandler.post(new Runnable() {@Overridepublic void run() {removeFooter();}});}}/** * 是否启用下拉刷新 */public void enablePullDown(boolean enable) {mEnablePullDown = enable;}/* * ================================== * Private method * 具体实现下拉刷新等操作 *  * ================================== *//** * 初始化界面 */private void initHeaderViewAndFooterViewAndListView(Context context){setOrientation(LinearLayout.VERTICAL);mContext = context;/* * 自定义头部文件 * 放在这里是因为考虑到很多界面都需要使用 * 如果要修改,和它相关的设置都要更改 */mEnablePullDown = true;mHeaderView = LayoutInflater.from(context).inflate(R.layout.pulldown_header, null);mHeaderViewParams = new LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);mHeaderIncremental = mDefaultHeaderViewHeight;addView(mHeaderView, 0, mHeaderViewParams);mDefaultHeaderViewHeight = getResources().getDimensionPixelSize(R.dimen.pulldown_headerview_height);mMoveDeviation = getResources().getDimensionPixelSize(R.dimen.pulldown_move_deviation);mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_text);mHeaderArrowView = (ImageView) mHeaderView.findViewById(R.id.pulldown_header_arrow);mHeaderViewDateView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_date);mHeaderLoadingView = mHeaderView.findViewById(R.id.pulldown_header_loading);// 注意,图片旋转之后,再执行旋转,坐标会重新开始计算mRotateOTo180Animation = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);mRotateOTo180Animation.setDuration(250);mRotateOTo180Animation.setFillAfter(true);mRotate180To0Animation = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);mRotate180To0Animation.setDuration(250);mRotate180To0Animation.setFillAfter(true);/* * 自定义脚部文件 */mEnableLoadMore = true;mFooterView = LayoutInflater.from(mContext).inflate(R.layout.pulldown_footer, null);mFooterView.setVisibility(View.GONE);mFooterTextView = (TextView) mFooterView.findViewById(R.id.pulldown_footer_text);mFooterLoadingView = mFooterView.findViewById(R.id.pulldown_footer_loading);mFooterView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if (mIsNoMoreData || !mIsDidLoad || (state & STATE_LOADING_MORE) == STATE_LOADING_MORE) return;ScrollOverListView listView = mListView;if (listView.getCount() - listView.getHeaderViewsCount() - listView.getFooterViewsCount() > 0) {state |= STATE_LOADING_MORE;updateFooter();mOnPullDownListener.onLoadMore();}}});/* * ScrollOverListView 同样是考虑到基本都要使用,所以放在这里 * 同时因为,需要它的监听事件 */mListView = new ScrollOverListView(context);mListView.setFooterDividersEnabled(false);mListView.setId(android.R.id.list);mListView.addFooterView(mFooterView);mListView.setOnScrollOverListener(this);mListView.setOnScrollListener(this);// 因为2.3之后的某些ListView控件自己实现了pull阴影动画效果// 所以我们在这里屏蔽他们try {Method method = AbsListView.class.getDeclaredMethod("setOverScrollMode", int.class);method.setAccessible(true);method.invoke(mListView, 2);//View.OVER_SCROLL_NEVER} catch (Exception e) {e.printStackTrace();}addView(mListView, android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.FILL_PARENT);// set ListView animation/*AnimationSet set = new AnimationSet(true);        Animation animation = new AlphaAnimation(0.0f, 1.0f);        animation.setDuration(50);        set.addAnimation(animation);        animation = new TranslateAnimation(            Animation.RELATIVE_TO_SELF, 0.0f,Animation.RELATIVE_TO_SELF, 0.0f,            Animation.RELATIVE_TO_SELF, -1.0f,Animation.RELATIVE_TO_SELF, 0.0f        );        animation.setDuration(100);        set.addAnimation(animation);        LayoutAnimationController controller = new LayoutAnimationController(set, 0.5f);        mListView.setLayoutAnimation(controller);*/// 空的listenermOnPullDownListener = new OnPullDownListener() {@Overridepublic void onRefresh() {}@Overridepublic void onLoadMore() {}};}/** * 去掉底部文件 */private void removeFooter(){if(mListView.getFooterViewsCount() > 0 && mListView != null && mFooterView != null){mListView.removeFooterView(mFooterView);}}/** * 更新头部文件</br> * 如果刷新中显示Loading, * 如果拖动中,显示拖动效果, * 否则显示更新时间。 */private void updateHeader() {if ((state & STATE_REFRESHING) == STATE_REFRESHING) {mHeaderArrowView.clearAnimation();mHeaderArrowView.setVisibility(View.GONE);mHeaderLoadingView.setVisibility(View.VISIBLE);mHeaderTextView.setText("正在刷新中...");} else if ((state & STATE_DRAGING) == STATE_DRAGING) {if(mHeaderViewParams.height >= mDefaultHeaderViewHeight){if(mHeaderViewState == HEADER_VIEW_STATE_OVER_HEIGHT) return;mHeaderArrowView.setVisibility(View.VISIBLE);mHeaderLoadingView.setVisibility(View.GONE);mHeaderViewDateView.setVisibility(View.VISIBLE);mHeaderViewState = HEADER_VIEW_STATE_OVER_HEIGHT;mHeaderTextView.setText("松开可以刷新");mHeaderArrowView.startAnimation(mRotateOTo180Animation);}else{if(mHeaderViewState == HEADER_VIEW_STATE_NOT_OVER_HEIGHT|| mHeaderViewState == HEADER_VIEW_STATE_IDLE) return;mHeaderArrowView.setVisibility(View.VISIBLE);mHeaderLoadingView.setVisibility(View.GONE);mHeaderViewDateView.setVisibility(View.VISIBLE);mHeaderViewState = HEADER_VIEW_STATE_NOT_OVER_HEIGHT;mHeaderTextView.setText("下拉可以刷新");mHeaderArrowView.startAnimation(mRotate180To0Animation);}} else {mHeaderLoadingView.setVisibility(View.GONE);mHeaderViewDateView.setVisibility(View.VISIBLE);mHeaderArrowView.setVisibility(View.VISIBLE);mHeaderTextView.setText("下拉可以刷新");mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));}}/**  * 更新脚部文件</br> * 正常显示"更多", * 如果没有更多的数据就显示"已加载完全部", * 如果加载中就显示"加载中..."。 */private void updateFooter() {if (!mEnableLoadMore) return;if (mIsNoMoreData) {mFooterTextView.setText("已加载完全部");mFooterLoadingView.setVisibility(View.GONE);} else if ((state & STATE_LOADING_MORE) == STATE_LOADING_MORE) {mFooterTextView.setText("加载更多中...");mFooterLoadingView.setVisibility(View.VISIBLE);} else {mFooterTextView.setText("更多");mFooterLoadingView.setVisibility(View.GONE);}}private void setHeaderHeight(final int height){mHeaderIncremental = height;mHeaderViewParams.height = height;mHeaderView.setLayoutParams(mHeaderViewParams);}/** * 自动隐藏动画 */class HideHeaderViewTask extends TimerTask{@Overridepublic void run() {if((state & STATE_MOTION_DOWN) == STATE_MOTION_DOWN) {cancel();return;}mHeaderIncremental -= AUTO_INCREMENTAL;if(mHeaderIncremental > 0){mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);}else{mHeaderIncremental = 0;mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);cancel();}}}/** * 自动显示动画 */class ShowHeaderViewTask extends TimerTask{@Overridepublic void run() {if((state & STATE_MOTION_DOWN) == STATE_MOTION_DOWN) {cancel();return;}mHeaderIncremental -= AUTO_INCREMENTAL;if(mHeaderIncremental > mDefaultHeaderViewHeight){mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);}else{mHeaderIncremental = mDefaultHeaderViewHeight;mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);if (mIsDidLoad && (state & STATE_REFRESHING) != STATE_REFRESHING) {state |= STATE_REFRESHING;mUIHandler.post(new Runnable() {@Overridepublic void run() {// 要清除掉动画,否则无法隐藏updateHeader();mHeaderArrowView.clearAnimation();mHeaderArrowView.setVisibility(View.INVISIBLE);mHeaderLoadingView.setVisibility(View.VISIBLE);mOnPullDownListener.onRefresh();}});}cancel();}}}private Handler mUIHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case WHAT_SET_HEADER_HEIGHT :{setHeaderHeight(mHeaderIncremental);return;}}}};/** * 当刷新完数据时,手动调用onIdle事件 */private void doListViewIdleActionOnDataDidLoad(){new Handler().postDelayed(new Runnable() {@Overridepublic void run() {if(mOnListViewIdleListener != null){// 不需要考虑FooterViewfinal int firstVisiblePosition = mListView.getFirstVisiblePosition();final int childCount = mListView.getChildCount(); //Log.d(TAG, "[doListViewIdleActionOnDataDidLoad] firstVisiblePosition:" + firstVisiblePosition + " childCount:" + childCount);mOnListViewIdleListener.onIdle(firstVisiblePosition, childCount);}}}, 0);}/** * 条目是否填满整个屏幕 */private boolean isFillScreenItem(){final int firstVisiblePosition = mListView.getFirstVisiblePosition();final int lastVisiblePosition = mListView.getLastVisiblePosition() - mListView.getFooterViewsCount();final int visibleItemCount = lastVisiblePosition - firstVisiblePosition + 1;final int totalItemCount = mListView.getCount() - mListView.getFooterViewsCount();if(visibleItemCount < totalItemCount) return true;return false;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {if (ev.getAction() == MotionEvent.ACTION_DOWN) {state |= STATE_MOTION_DOWN;mIsPullUpDone = false;mMotionDownLastY = ev.getRawY();Log.d(TAG, "pulldownview.onIntercept:" + mMotionDownLastY);}return super.onInterceptTouchEvent(ev);}/* * ================================== * 实现 OnScrollOverListener接口 *  *  * ================================== */@Overridepublic boolean onListViewTopAndPullDown(MotionEvent event, int delta) {// 因为某些ListView控件自己已经添加了下拉效果// 所以我们必须返回true,控制ListView不会滚动,达到屏蔽的效果if ((state & STATE_REFRESHING) == STATE_REFRESHING || !mEnablePullDown || !mIsDidLoad) {return true;}if (mListView.getCount() - mListView.getFooterViewsCount() == 0) {return true;}// 如果开始按下到滑动距离不超过误差值,则不滑动if(mHeaderViewParams.height <= 0){final int absMotionY = (int) Math.abs(event.getRawY() - mMotionDownLastY);if(absMotionY < mMoveDeviation) {return true;}}int absDelta = Math.abs(delta);final int i = (int) Math.ceil((double)absDelta / 2);mHeaderIncremental += i;if(mHeaderIncremental >= 0){ // && mIncremental <= mMaxHeightsetHeaderHeight(mHeaderIncremental);updateHeader();}return true;}@Overridepublic boolean onListViewBottomAndPullUp(MotionEvent event, int delta) {if((state & STATE_LOADING_MORE) == STATE_LOADING_MORE|| !mIsDidLoad || !mEnableAutoFetchMore || mIsNoMoreData) return false;ScrollOverListView listView = mListView;if (listView.getCount() - listView.getHeaderViewsCount() - listView.getFooterViewsCount() > 0) {state |= STATE_LOADING_MORE;updateFooter();mOnPullDownListener.onLoadMore();}return true;}@Overridepublic boolean onMotionDown(MotionEvent ev) {return false;}@Overridepublic boolean onMotionMove(MotionEvent ev, int delta) {state |= STATE_DRAGING;//当头部文件回推消失的时候,不允许滚动if(mIsPullUpDone) return true;// onTopDown在顶部,并向上回推和onTopUp相对if(mHeaderViewParams.height > 0 && delta < 0){final int absDelta = Math.abs(delta);final int i = (int) Math.ceil((double)absDelta / 2);mHeaderIncremental -= i;if(mHeaderIncremental > 0){setHeaderHeight(mHeaderIncremental);updateHeader();}else{mHeaderViewState = HEADER_VIEW_STATE_IDLE;mHeaderIncremental = 0;setHeaderHeight(mHeaderIncremental);mIsPullUpDone = true;}return true;}return false;}@Overridepublic boolean onMotionUp(MotionEvent ev) {state &= ~STATE_DRAGING;state &= ~STATE_MOTION_DOWN;// 避免和点击事件冲突if(mHeaderViewParams.height > 0 || mIsPullUpDone){// 判断头文件拉动的距离与设定的高度,小了就隐藏,多了就固定高度int x = mHeaderIncremental - mDefaultHeaderViewHeight;Timer timer = new Timer(true);if(x < 0){timer.scheduleAtFixedRate(new HideHeaderViewTask(), 0, 10);}else{timer.scheduleAtFixedRate(new ShowHeaderViewTask(), 0, 10);}return true;}return false;//TODO}/* * ===================================== * 监听ListView停止事件 * ===================================== */private OnListViewIdleListener mOnListViewIdleListener;public interface OnListViewIdleListener {void onIdle(int startIndex, int count);}public void setOnListViewIdleListener(OnListViewIdleListener listener){if(mListView != null){mOnListViewIdleListener = listener;}}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if(scrollState == SCROLL_STATE_IDLE){if (DEBUG) Log.d(TAG, "IDLE");if(mOnListViewIdleListener != null){int count = mListView.getChildCount();final int childEndIndex = mStartIndex + mListView.getChildCount() -1;final int listEndIndex = mListView.getCount() -1;if(childEndIndex == listEndIndex){count -= mListView.getFooterViewsCount();}mOnListViewIdleListener.onIdle(mStartIndex, count);}}switch (scrollState) {case SCROLL_STATE_FLING:if (DEBUG) Log.d(TAG, "FLING");break;case SCROLL_STATE_TOUCH_SCROLL:if (DEBUG) Log.d(TAG, "SCROLL");default:break;}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {if (DEBUG) Log.d(TAG, "onScroll");mStartIndex = firstVisibleItem;// 因为三星I91002.3版本如果设置over_scroll_never会出现回弹问题,所以在这里处理一下// 更新12-9-9 2:08:晕死,测试三星2.3.3版本如果反射使用这些属性会报错。/*final ScrollOverListView localListView = this.mListView;final boolean hasItem = localListView.getCount() > 0;try {if (overScrollModeField == null) return;final Integer mode = (Integer) overScrollModeField.get(localListView);if (firstVisibleItem <= 0 && hasItem) {if (mode != View.OVER_SCROLL_NEVER) {if (DEBUG) Log.w(TAG, "set over scroll never");overScrollModeField.set(localListView, View.OVER_SCROLL_NEVER);}} else if (firstVisibleItem + visibleItemCount >= totalItemCount && hasItem) {if (mode != View.OVER_SCROLL_ALWAYS) {if (DEBUG) Log.w(TAG, "set over scroll always");overScrollModeField.set(localListView, View.OVER_SCROLL_ALWAYS);}}} catch (Exception e) {e.printStackTrace();}*/}}



三、Demo界面


package com.example.pulldownview;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import com.example.pulldownview.PullDownView.OnPullDownListener;public class MainActivity extends Activity {    private PullDownView pullDownView;private ScrollOverListView listView;private MyAdapter adapter;private List<String> arrays;private LayoutInflater inflater;@Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                inflater = getLayoutInflater();                pullDownView = (PullDownView) findViewById(R.id.pulldownview);        pullDownView.enableAutoFetchMore(true, 0);        listView = pullDownView.getListView();        adapter = new MyAdapter();        listView.setAdapter(adapter);                initArrays(new Handler(){        @Override        public void handleMessage(Message msg) {        arrays = (List<String>) msg.obj;        adapter.notifyDataSetChanged();        pullDownView.notifyDidDataLoad(false);        }        });                pullDownView.setOnPullDownListener(new OnPullDownListener() {@Overridepublic void onRefresh() {getNewString(new Handler(){@Overridepublic void handleMessage(Message msg) {arrays.add(0, (String) msg.obj);adapter.notifyDataSetChanged();pullDownView.notifyDidRefresh(arrays.isEmpty());}});}@Overridepublic void onLoadMore() {getNewString(new Handler(){@Overridepublic void handleMessage(Message msg) {arrays.add((String) msg.obj);adapter.notifyDataSetChanged();pullDownView.notifyDidLoadMore(arrays.isEmpty());}});}});    }private void initArrays(final Handler handler) {new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);} catch (Exception e) {Thread.interrupted();e.printStackTrace();}List<String> as = new ArrayList<String>();as.add("first");as.add("second");as.add("third");as.add("four");as.add("five");as.add("first");as.add("second");as.add("third");as.add("four");as.add("five");as.add("first");as.add("second");as.add("third");as.add("four");as.add("five");as.add("first");as.add("second");as.add("third");as.add("four");as.add("five");as.add("first");as.add("second");as.add("third");as.add("four");as.add("five");handler.obtainMessage(0, as).sendToTarget();}}).start();}private void getNewString(final Handler handler) {new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (Exception e) {Thread.interrupted();e.printStackTrace();}handler.obtainMessage(0, "New Text " + System.currentTimeMillis()).sendToTarget();}}).start();}private class MyAdapter extends BaseAdapter {@Overridepublic int getCount() {return arrays == null ? 0 : arrays.size();}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if (convertView == null) {holder = new ViewHolder();convertView = inflater.inflate(R.layout.item_list, null);holder.textView = (TextView) convertView.findViewById(R.id.text);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}holder.textView.setText(arrays.get(position));return convertView;}}private static class ViewHolder {TextView textView;}}




0 0
原创粉丝点击