最 近做的类似于微博的项目中,有个Android功能要使用到listview的向下拉刷新来刷新最新消息,向上拉刷新(滑动分页)来加载更多。
新浪微博就是使用这种方式的典型。
当用户从网络上读取微博的时候,如果一下子全部加载用户未读的微博这将耗费比较长的时间,造成不好的用户体验,同时一屏的内容也不足以显示如此多的内容。这时候,我们就需要用到另一个功能,那就是listview的分页了,其实这个分页可以做成客户端的分页,也可以做成服务器端的分页(点击加载时,从服务器对应的加载第N页就好了!!!)。通过分页分次加载数据,用户看多少就去加载多少。
通常这也分为两种方式,一种是设置一个按钮,用户点击即加载。另一种是当用户滑动到底部时自动加载。今天我就和大家分享一下滑动到底端时自动加载这个功能的实现。
效果图如下所示:

下拉刷新最主要的流程是:
(1). 下拉,显示提示头部界面(HeaderView),这个过程提示用户”下拉刷新”
(2). 下拉到一定程度,超出了刷新最基本的下拉界限,我们认为达到了刷新的条件,提示用户可以”松手刷新”了,效果上允许用户继续下拉
(3). 用户松手,可能用户下拉远远不止提示头部界面,所以这一步,先反弹回仅显示提示头部界面,然后提示用户”正在加载”。
(4). 加载完成后,隐藏提示头部界面。

那么让我们看看怎么才能实现呢???
第一步:既然是要显示listview ,那么就应该有个listview 的容器pulldown.xml

<?xml version="1.0" encoding="utf-8"?>
<com.solo.pulldown.PullDownView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/pull_down_view"android:layout_width="fill_parent"android:layout_height="fill_parent"android:background="@android:color/white"></com.solo.pulldown.PullDownView>

第二步:自定义一个listview中显示的item对象pulldown_item.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@android:id/text1"android:layout_width="fill_parent"android:layout_height="wrap_content"android:textAppearance="?android:attr/textAppearanceLarge"android:gravity="center_vertical"android:paddingLeft="6dip"android:minHeight="?android:attr/listPreferredItemHeight"android:textColor="@android:color/black"
/>

第三步:定义一个header的xml布局文件pulldown_header.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content"android:paddingBottom="10dp"android:paddingTop="10dp" ><ImageViewandroid:id="@+id/pulldown_header_arrow"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_marginLeft="20dp"android:scaleType="centerCrop"android:src="@drawable/z_arrow_down"android:visibility="invisible" /><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignBottom="@+id/pulldown_header_arrow"android:layout_alignTop="@+id/pulldown_header_arrow"android:layout_centerHorizontal="true"android:gravity="center_vertical"android:orientation="vertical" ><TextViewandroid:id="@+id/pulldown_header_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="加载中..." /><TextViewandroid:id="@+id/pulldown_header_date"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="更新于:"android:visibility="gone" /></LinearLayout><ProgressBarandroid:id="@+id/pulldown_header_loading"style="@android:style/Widget.ProgressBar.Small.Inverse"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginLeft="20dp" /></RelativeLayout>

第四步:如果需要向上拉更新更多的话,那就定义一个底部的footer的布局文件,在此为方便起见,只定义一个progressbar跟textview,更加复杂的显示,就交给你们了~~~~~pulldown_footer.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content"android:paddingBottom="10dp"android:paddingTop="10dp" ><TextViewandroid:id="@+id/pulldown_footer_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="更多"android:textSize="15dp" /><ProgressBarandroid:id="@+id/pulldown_footer_loading"style="@android:style/Widget.ProgressBar.Small.Inverse"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginLeft="20dp"android:visibility="gone" /></RelativeLayout>

第五步:那么主要的文件这才登场:::::::重写listview这个文件主要任务是提供触摸的事件的处理方法。

/*** <p>一个可以监听ListView是否滚动到最顶部或最底部的自定义控件</p>* 只能监听由触摸产生的,如果是ListView本身Flying导致的,则不能监听</br>* 如果加以改进,可以实现监听scroll滚动的具体位置等*/public class ScrollOverListView extends ListView {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 onTouchEvent(MotionEvent ev) {final int action = ev.getAction();final int y = (int) ev.getRawY();switch(action){case MotionEvent.ACTION_DOWN:{mLastY = y;final boolean isHandled = mOnScrollOverListener.onMotionDown(ev);if (isHandled) {mLastY = y;return isHandled;}break;}case MotionEvent.ACTION_MOVE:{final int childCount = getChildCount();if(childCount == 0) return super.onTouchEvent(ev);final int itemCount = getAdapter().getCount() - mBottomPosition;final int deltaY = y - mLastY;//DLog.d("lastY=%d y=%d", mLastY, 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();final boolean isHandleMotionMove = mOnScrollOverListener.onMotionMove(ev, deltaY);if(isHandleMotionMove){mLastY = y;return true;}//DLog.d("firstVisiblePosition=%d firstTop=%d listPaddingTop=%d deltaY=%d", firstVisiblePosition, firstTop, listPadding, deltaY);if (firstVisiblePosition <= mTopPosition && firstTop >= listPadding && deltaY > 0) {final boolean isHandleOnListViewTopAndPullDown;isHandleOnListViewTopAndPullDown = mOnScrollOverListener.onListViewTopAndPullDown(deltaY);if(isHandleOnListViewTopAndPullDown){mLastY = y;return true;}}// DLog.d("lastBottom=%d end=%d deltaY=%d", lastBottom, end, deltaY);if (firstVisiblePosition + childCount >= itemCount && lastBottom <= end && deltaY < 0) {final boolean isHandleOnListViewBottomAndPullDown;isHandleOnListViewBottomAndPullDown = mOnScrollOverListener.onListViewBottomAndPullUp(deltaY);if(isHandleOnListViewBottomAndPullDown){mLastY = y;return true;}}break;}case MotionEvent.ACTION_UP:{final boolean isHandlerMotionUp = mOnScrollOverListener.onMotionUp(ev);if (isHandlerMotionUp) {mLastY = y;return true;}break;}}mLastY = y;return super.onTouchEvent(ev);}/**空的*/private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener(){@Overridepublic boolean onListViewTopAndPullDown(int delta) {return false;}@Overridepublic boolean onListViewBottomAndPullUp(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(getAdapter() == null)throw new NullPointerException("You must set adapter before setTopPosition!");if(index < 0)throw new IllegalArgumentException("Top position must > 0");mTopPosition = index;}/*** 可以自定义其中一个条目为尾部,尾部触发的事件将以这个为准,默认为最后一个** @param index 倒数第几个,必须在条目数范围之内*/public void setBottomPosition(int index){if(getAdapter() == null)throw new NullPointerException("You must set adapter before setBottonPosition!");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)**/public interface OnScrollOverListener {/*** 到达最顶部触发** @param delta 手指点击移动产生的偏移量* @return*/boolean onListViewTopAndPullDown(int delta);/*** 到达最底部触发** @param delta 手指点击移动产生的偏移量* @return*/boolean onListViewBottomAndPullUp(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);}}

第六步:下拉刷新控件,真正实现下拉刷新的是这个控件,而上面的那个ScrollOverListView只是提供触摸的事件等

/*** 下拉刷新控件</br>* 真正实现下拉刷新的是这个控件,* ScrollOverListView只是提供触摸的事件等*/
public class PullDownView extends LinearLayout implements OnScrollOverListener{private static final String TAG = "PullDownView";private static final int START_PULL_DEVIATION = 50; // 移动误差private static final int AUTO_INCREMENTAL = 10;     // 自增量,用于回弹private static final int WHAT_DID_LOAD_DATA = 1;    // Handler what 数据加载完毕private static final int WHAT_ON_REFRESH = 2;       // Handler what 刷新中private static final int WHAT_DID_REFRESH = 3;      // Handler what 已经刷新完private static final int WHAT_SET_HEADER_HEIGHT = 4;// Handler what 设置高度private static final int WHAT_DID_MORE = 5;         // Handler what 已经获取完更多private static final int DEFAULT_HEADER_VIEW_HEIGHT = 105;  // 头部文件原本的高度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 int mHeaderIncremental; // 增量private float mMotionDownLastY; // 按下时候的Y轴坐标private boolean mIsDown;            // 是否按下private boolean mIsRefreshing;      // 是否下拉刷新中private boolean mIsFetchMoreing;    // 是否获取更多中private boolean mIsPullUpDone;      // 是否回推完成private boolean mEnableAutoFetchMore;   // 是否允许自动获取更多// 头部文件的状态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;public PullDownView(Context context, AttributeSet attrs) {super(context, attrs);initHeaderViewAndFooterViewAndListView(context);}public PullDownView(Context context) {super(context);initHeaderViewAndFooterViewAndListView(context);}/** ==================================* Public method* 外部使用,具体就是用这几个就可以了** ==================================*//*** 刷新事件接口*/public interface OnPullDownListener {void onRefresh();void onMore();}/*** 通知加载完了数据,要放在Adapter.notifyDataSetChanged后面* 当你加载完数据的时候,调用这个notifyDidLoad()* 才会隐藏头部,并初始化数据等*/public void notifyDidLoad() {mUIHandler.sendEmptyMessage(WHAT_DID_LOAD_DATA);}/*** 通知已经刷新完了,要放在Adapter.notifyDataSetChanged后面* 当你执行完刷新任务之后,调用这个notifyDidRefresh()* 才会隐藏掉头部文件等操作*/public void notifyDidRefresh() {mUIHandler.sendEmptyMessage(WHAT_DID_REFRESH);}/*** 通知已经获取完更多了,要放在Adapter.notifyDataSetChanged后面* 当你执行完更多任务之后,调用这个notyfyDidMore()* 才会隐藏加载圈等操作*/public void notifyDidMore() {mUIHandler.sendEmptyMessage(WHAT_DID_MORE);}/*** 设置监听器* @param listener*/public void setOnPullDownListener(OnPullDownListener listener){mOnPullDownListener = listener;}/*** 获取内嵌的listview* @return ScrollOverListView*/public ListView getListView(){return mListView;}/*** 是否开启自动获取更多* 自动获取更多,将会隐藏footer,并在到达底部的时候自动刷新* @param index 倒数第几个触发*/public void enableAutoFetchMore(boolean enable, int index){if(enable){mListView.setBottomPosition(index);mFooterLoadingView.setVisibility(View.VISIBLE);}else{mFooterTextView.setText("更多");mFooterLoadingView.setVisibility(View.GONE);}mEnableAutoFetchMore = enable;}/** ==================================* Private method* 具体实现下拉刷新等操作** ==================================*//*** 初始化界面*/private void initHeaderViewAndFooterViewAndListView(Context context){setOrientation(LinearLayout.VERTICAL);//setDrawingCacheEnabled(false);/** 自定义头部文件* 放在这里是因为考虑到很多界面都需要使用* 如果要修改,和它相关的设置都要更改*/mHeaderView = LayoutInflater.from(context).inflate(R.layout.pulldown_header, null);mHeaderViewParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);addView(mHeaderView, 0, mHeaderViewParams);mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_text);mHeaderArrowView = (ImageView) mHeaderView.findViewById(R.id.pulldown_header_arrow);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);/*** 自定义脚部文件*/mFooterView = LayoutInflater.from(context).inflate(R.layout.pulldown_footer, null);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(!mIsFetchMoreing){mIsFetchMoreing = true;mFooterLoadingView.setVisibility(View.VISIBLE);mOnPullDownListener.onMore();}}});/** ScrollOverListView 同样是考虑到都是使用,所以放在这里* 同时因为,需要它的监听事件*/mListView = new ScrollOverListView(context);mListView.setOnScrollOverListener(this);mListView.setCacheColorHint(0);addView(mListView, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);// 空的listenermOnPullDownListener = new OnPullDownListener() {@Overridepublic void onRefresh() {}@Overridepublic void onMore() {}};}/*** 在下拉和回推的时候检查头部文件的状态</br>* 如果超过了默认高度,就显示松开可以刷新,* 否则显示下拉可以刷新*/private void checkHeaderViewState(){if(mHeaderViewParams.height >= DEFAULT_HEADER_VIEW_HEIGHT){if(mHeaderViewState == HEADER_VIEW_STATE_OVER_HEIGHT) return;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;mHeaderViewState = HEADER_VIEW_STATE_NOT_OVER_HEIGHT;mHeaderTextView.setText("下拉可以刷新");mHeaderArrowView.startAnimation(mRotate180To0Animation);}}private void setHeaderHeight(final int height){mHeaderIncremental = height;mHeaderViewParams.height = height;mHeaderView.setLayoutParams(mHeaderViewParams);}/*** 自动隐藏动画*/class HideHeaderViewTask extends TimerTask{@Overridepublic void run() {if(mIsDown) {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(mIsDown) {cancel();return;}mHeaderIncremental -= AUTO_INCREMENTAL;if(mHeaderIncremental > DEFAULT_HEADER_VIEW_HEIGHT){mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);}else{mHeaderIncremental = DEFAULT_HEADER_VIEW_HEIGHT;mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);if(!mIsRefreshing){mIsRefreshing = true;mUIHandler.sendEmptyMessage(WHAT_ON_REFRESH);}cancel();}}}private Handler mUIHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case WHAT_DID_LOAD_DATA:{mHeaderViewParams.height = 0;mHeaderLoadingView.setVisibility(View.GONE);mHeaderTextView.setText("下拉可以刷新");mHeaderViewDateView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_date);mHeaderViewDateView.setVisibility(View.VISIBLE);mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));mHeaderArrowView.setVisibility(View.VISIBLE);showFooterView();return;}case WHAT_ON_REFRESH:{// 要清除掉动画,否则无法隐藏
                    mHeaderArrowView.clearAnimation();mHeaderArrowView.setVisibility(View.INVISIBLE);mHeaderLoadingView.setVisibility(View.VISIBLE);mOnPullDownListener.onRefresh();return;}case WHAT_DID_REFRESH :{mIsRefreshing = false;mHeaderViewState = HEADER_VIEW_STATE_IDLE;mHeaderArrowView.setVisibility(View.VISIBLE);mHeaderLoadingView.setVisibility(View.GONE);mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));setHeaderHeight(0);showFooterView();return;}case WHAT_SET_HEADER_HEIGHT :{setHeaderHeight(mHeaderIncremental);return;}case WHAT_DID_MORE :{mIsFetchMoreing = false;mFooterTextView.setText("更多");mFooterLoadingView.setVisibility(View.GONE);}}}};/*** 显示脚步脚部文件*/private void showFooterView(){if(mListView.getFooterViewsCount() == 0 && isFillScreenItem()){mListView.addFooterView(mFooterView);mListView.setAdapter(mListView.getAdapter());}}/*** 条目是否填满整个屏幕*/private boolean isFillScreenItem(){final int firstVisiblePosition = mListView.getFirstVisiblePosition();final int lastVisiblePostion = mListView.getLastVisiblePosition() - mListView.getFooterViewsCount();final int visibleItemCount = lastVisiblePostion - firstVisiblePosition + 1;final int totalItemCount = mListView.getCount() - mListView.getFooterViewsCount();if(visibleItemCount < totalItemCount) return true;return false;}/** ==================================* 实现 OnScrollOverListener接口*** ==================================*/@Overridepublic boolean onListViewTopAndPullDown(int delta) {if(mIsRefreshing || mListView.getCount() - mListView.getFooterViewsCount() == 0) return false;int absDelta = Math.abs(delta);final int i = (int) Math.ceil((double)absDelta / 2);mHeaderIncremental += i;if(mHeaderIncremental >= 0){ // && mIncremental <= mMaxHeight
            setHeaderHeight(mHeaderIncremental);checkHeaderViewState();}return true;}@Overridepublic boolean onListViewBottomAndPullUp(int delta) {if(!mEnableAutoFetchMore || mIsFetchMoreing) return false;// 数量充满屏幕才触发if(isFillScreenItem()){mIsFetchMoreing = true;mFooterTextView.setText("加载更多中...");mFooterLoadingView.setVisibility(View.VISIBLE);mOnPullDownListener.onMore();return true;}return false;}@Overridepublic boolean onMotionDown(MotionEvent ev) {mIsDown = true;mIsPullUpDone = false;mMotionDownLastY = ev.getRawY();return false;}@Overridepublic boolean onMotionMove(MotionEvent ev, int delta) {//当头部文件回推消失的时候,不允许滚动if(mIsPullUpDone) return true;// 如果开始按下到滑动距离不超过误差值,则不滑动final int absMotionY = (int) Math.abs(ev.getRawY() - mMotionDownLastY);if(absMotionY < START_PULL_DEVIATION) return true;final int absDelta = Math.abs(delta);final int i = (int) Math.ceil((double)absDelta / 2);// onTopDown在顶部,并上回推和onTopUp相对if(mHeaderViewParams.height > 0 && delta < 0){mHeaderIncremental -= i;if(mHeaderIncremental > 0){setHeaderHeight(mHeaderIncremental);checkHeaderViewState();}else{mHeaderViewState = HEADER_VIEW_STATE_IDLE;mHeaderIncremental = 0;setHeaderHeight(mHeaderIncremental);mIsPullUpDone = true;}return true;}return false;}@Overridepublic boolean onMotionUp(MotionEvent ev) {mIsDown = false;// 避免和点击事件冲突if(mHeaderViewParams.height > 0){// 判断头文件拉动的距离与设定的高度,小了就隐藏,多了就固定高度int x = mHeaderIncremental - DEFAULT_HEADER_VIEW_HEIGHT;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;}}

第七步:这个java文件就是封装数据,然后调用上两个文件就好了。废话不多说,上代码:::::

public class PullDownActivity extends Activity implements OnPullDownListener, OnItemClickListener{private static final int WHAT_DID_LOAD_DATA = 0;private static final int WHAT_DID_REFRESH = 1;private static final int WHAT_DID_MORE = 2;private ListView mListView;private ArrayAdapter<String> mAdapter;private PullDownView mPullDownView;private List<String> mStrings = new ArrayList<String>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.pulldown);/** 1.使用PullDownView* 2.设置OnPullDownListener* 3.从mPullDownView里面获取ListView*/mPullDownView = (PullDownView) findViewById(R.id.pull_down_view);mPullDownView.setOnPullDownListener(this);mListView = mPullDownView.getListView();mListView.setOnItemClickListener(this);mAdapter = new ArrayAdapter<String>(this, R.layout.pulldown_item, mStrings);mListView.setAdapter(mAdapter);mPullDownView.enableAutoFetchMore(true, 1);loadData();}private void loadData(){new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}List<String> strings = new ArrayList<String>();for (String body : mStringArray) {strings.add(body);}Message msg = mUIHandler.obtainMessage(WHAT_DID_LOAD_DATA);msg.obj = strings;msg.sendToTarget();}}).start();}@Overridepublic void onRefresh() {new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}Message msg = mUIHandler.obtainMessage(WHAT_DID_REFRESH);msg.obj = "After refresh " + System.currentTimeMillis();msg.sendToTarget();}}).start();}@Overridepublic void onMore() {new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}Message msg = mUIHandler.obtainMessage(WHAT_DID_MORE);msg.obj = "After more " + System.currentTimeMillis();msg.sendToTarget();}}).start();}private Handler mUIHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case WHAT_DID_LOAD_DATA:{if(msg.obj != null){List<String> strings = (List<String>) msg.obj;if(!strings.isEmpty()){mStrings.addAll(strings);mAdapter.notifyDataSetChanged();}}// 诉它数据加载完毕;
                    mPullDownView.notifyDidLoad();break;}case WHAT_DID_REFRESH :{String body = (String) msg.obj;mStrings.add(0, body);mAdapter.notifyDataSetChanged();// 告诉它更新完毕
                    mPullDownView.notifyDidRefresh();break;}case WHAT_DID_MORE:{String body = (String) msg.obj;mStrings.add(body);mAdapter.notifyDataSetChanged();// 告诉它获取更多完毕
                    mPullDownView.notifyDidMore();break;}}}};@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {Toast.makeText(this, "啊,你点中我了 " + position, Toast.LENGTH_SHORT).show();}// 模拟数据private String[] mStringArray = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi","Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale","Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese"};}

整个程序就这么完成了,总之,可以使用前两个封装好的文件,然后,调用就好了~~~~没什么太难的地方。

源代码下载地址:我是传送门   我也是个传送门

自定义listview完成上拉刷新,下拉加载的功能,重写ontouchevent方法相关推荐

  1. Android之 RecyclerView,CardView 详解和相对应的上拉刷新下拉加载

    为什么80%的码农都做不了架构师?>>>    随着 Google 推出了全新的设计语言 Material Design,还迎来了新的 Android 支持库 v7,其中就包含了 M ...

  2. Android——Xlistview上拉刷新下拉加载

    配置网络权限+xutils包+gson包 代码如下: values下修改strings添加 直接粘 <?xml version="1.0" encoding="ut ...

  3. jQuery模拟原生态App上拉刷新下拉加载

    jQuery模拟原生态App上拉刷新下拉加载效果代码,鼠标上拉时会显示loading字样,并且会模拟加载一条静态数据,支持触屏设备使用. <!doctype html> <html ...

  4. jQuery模拟原生态App上拉刷新下拉加载效果代码

    以下任意均可: 1. jQuery模拟原生态App上拉刷新下拉加载效果代码,鼠标上拉时会显示loading字样,并且会模拟加载一条静态数据,支持触屏设备使用. 原文:http://www.sucaij ...

  5. android上拉刷新下拉加载

    本文用的XlistView的上拉刷新下拉加载 主Activity package org.com.cctest;import java.text.SimpleDateFormat; import ja ...

  6. uniapp小程序如何通过虚拟列表配合节流,完成上拉刷新下拉加载,避免页面卡顿,提升性能呢?

    本文将介绍uniapp小程序中如何使用虚拟列表和节流两种技术实现上拉刷新下拉加载功能,同时避免因渲染大量数据导致的页面卡顿问题. 一.虚拟列表 在uniapp小程序开发中,当我们需要渲染大量列表数据时 ...

  7. XListView下拉刷新下拉加载

    自定义控件XlistView实现刷新及加载 1.要想实现数据可以上拉也可以下拉效果,可以使用自定义控件XlistView, 配置网络权限+xutils包+gson包 代码如下: values下修改st ...

  8. (仿头条APP项目)6.点击过的新闻列表文字变灰和下拉刷新与滚动加载新闻数据

    文章目录 一.点击过的新闻列表文字变灰 效果图 实现思路 导入ormlite数据库类依赖 利用ormlite创建数据库和表 创建数据库类MyDbHelper 创建数据库中的新闻实体类NewInfo 页 ...

  9. uni-app下拉刷新触底加载更多

    首先在pages.json 配置文件中配置    "enablePullDownRefresh": true  需要在哪用加载就配置在路由的style里 两个事件 //下拉刷新 o ...

  10. 上拉刷新下拉加载PullToRefreshLayout

    2019独角兽企业重金招聘Python工程师标准>>> java pullable代码 package com.example.lsy.tianmi.base.commonview; ...

最新文章

  1. 蓝鸥Unity开发基础二——课时20 接口
  2. JavaScript-变量与作用域链
  3. [分享]极富挑战性的大公司面试的智力题
  4. Google的Project Stream准备在Chrome中播放AAA控制台游戏
  5. leetcode 27. 移除元素(双指针)
  6. linux c++ queue 多线程,C++多线程,消息队列用法
  7. postman使用之二:数据同步和创建测试集
  8. nginx的web基础
  9. ubuntu12.04-- vi 使用
  10. Java中swing使用ImageIcon类添加图片
  11. 主编推荐 | 南大周志华、俞扬、钱超最新力作导读
  12. python数据可视化-简单案例
  13. 用c++做文本加密与解密程序(源码)
  14. JS逆向-易班登录password参数(RSA加密)
  15. 如何制作一个优秀的个人网站?
  16. 数据治理系列:数据血缘关系
  17. 知人者智自知者明--所有的懊恼都是实力的差劲--我的第一次面试
  18. shell脚本括号使用
  19. 图像处理:以图像分类和图像深度估计为例,如何将研究想法进行迁移学习应用?
  20. 企业购置新车,各项费用会计入账以及案例分析

热门文章

  1. 轨迹数据压缩算法-经典Douglas-Peucker算法 c++实现
  2. vista怎么看计算机配置,查看电脑配置软件_无需软件 三方法查看电脑配置
  3. 【Python】基于Python的机器学习回归:可视化、预测及预测结果保存(附代码)
  4. HTML表格布局实际使用详解,是HTML入门学习中的基础知识
  5. Linux操作系统webmin的安装
  6. php(TP5)+redis实现秒杀抢购(限制用户购买次数)(附源码)
  7. 对话 ONES 联合创始人兼 CTO 冯斌:技术管理者如何打造一支自驱型团队?
  8. Ubuntu下架设FTP服务器
  9. 物联网MQTT协议详解
  10. socket阻塞和非阻塞模式