最 近做的类似于微博的项目中,有个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" > <ImageView
android: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" /> <LinearLayout
android: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" > <TextView
android:id="@+id/pulldown_header_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="加载中..." /> <TextView
android: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> <ProgressBar
android: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" > <TextView
android:id="@+id/pulldown_footer_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="更多"
android:textSize="15dp" /> <ProgressBar
android: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;
} @Override
public 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(){ @Override
public boolean onListViewTopAndPullDown(int delta) {
return false;
} @Override
public boolean onListViewBottomAndPullUp(int delta) {
return false;
} @Override
public boolean onMotionDown(MotionEvent ev) {
return false;
} @Override
public boolean onMotionMove(MotionEvent ev, int delta) {
return false;
} @Override
public 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() {
@Override
public 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); // 空的listener
mOnPullDownListener = new OnPullDownListener() {
@Override
public void onRefresh() {}
@Override
public 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{
@Override
public 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{ @Override
public 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(){ @Override
public 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接口
*
*
* ==================================
*/ @Override
public 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;
} @Override
public 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;
} @Override
public boolean onMotionDown(MotionEvent ev) {
mIsDown = true;
mIsPullUpDone = false;
mMotionDownLastY = ev.getRawY();
return false;
} @Override
public 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;
} @Override
public 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>(); @Override
protected 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() { @Override
public 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();
} @Override
public void onRefresh() {
new Thread(new Runnable() { @Override
public 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();
} @Override
public void onMore() {
new Thread(new Runnable() { @Override
public 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(){ @Override
public 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;
}
} } }; @Override
public 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"
}; }

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

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

http://k-beta.com/android-listview-more-refresh.html

listview下拉刷新 上拉(滑动分页)加载更多的更多相关文章

  1. XRecyclerView:实现下拉刷新、滚动到底部加载更多以及添加header功能的RecyclerView

    介绍: 一个实现了下拉刷新,滚动到底部加载更多以及添加header功能的的RecyclerView.使用方式和RecyclerView完全一致,不需要额外的layout,不需要写特殊的adater. ...

  2. juery下拉刷新,ajax请求,div加载更多元素(一)

    ;//设置当前页数 var flag=true; //滑动加载 $(function(){ var winH = $(window).height(); //页面可视区域高度 $(window).sc ...

  3. react-native-page-listview使用方法(自定义FlatList/ListView下拉刷新,上拉加载更多,方便的实现分页)

    react-native-page-listview 对ListView/FlatList的封装,可以很方便的分页加载网络数据,还支持自定义下拉刷新View和上拉加载更多的View.兼容高版本Flat ...

  4. ListView下拉刷新上拉加载更多实现

    这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...

  5. listview下拉刷新上拉加载扩展(二)-仿美团外卖

    经过前几篇的listview下拉刷新上拉加载讲解,相信你对其实现机制有了一个深刻的认识了吧,那么这篇文章我们来实现一个高级的listview下拉刷新上拉加载-仿新版美团外卖的袋鼠动画: 项目结构: 是 ...

  6. MaterialRefreshLayout+ListView 下拉刷新 上拉加载

    效果图是这样的,有入侵式的,非入侵式的,带波浪效果的......就那几个属性,都给出来了,自己去试就行. 下拉刷新 上拉加载 关于下拉刷新-上拉加载的效果,有许许多多的实现方式,百度了一下竟然有几十种 ...

  7. 自定义ListView下拉刷新上拉加载更多

    自定义ListView下拉刷新上拉加载更多 自定义RecyclerView下拉刷新上拉加载更多 Listview现在用的很少了,基本都是使用Recycleview,但是不得不说Listview具有划时 ...

  8. ListView实现Item上下拖动交换位置 并且实现下拉刷新 上拉加载更多

    ListView实现Item上下拖动交换位置  并且实现下拉刷新  上拉加载更多 package com.example.ListViewDragItem; import android.app.Ac ...

  9. listview下拉刷新上拉加载扩展(三)-仿最新版美团外卖

    本篇是基于上篇listview下拉刷新上拉加载扩展(二)-仿美团外卖改造而来,主要调整了headview的布局,并加了两个背景动画,看似高大上,其实很简单: as源码地址:http://downloa ...

随机推荐

  1. Java Date 时分秒置0

    Date now = new Date(); Calendar cal1 = Calendar.getInstance(); cal1.setTime(now); // 将时分秒,毫秒域清零 cal1 ...

  2. Tornado开发技巧,简单了解tornado

    tornado基础入门(一)——简单了解tornado 参考:http://demo.pythoner.com/itt2zh/ch1.html tornado是一个轻量级的web框架,是一个用pyth ...

  3. information_schema系列九

    information_schema系列九   1:INNODB_SYS_FOREIGN 这个表存储的是INNODB关于外键的元数据信息 Column name Description ID 外键的名 ...

  4. .Net Standard简介

    .NET Standard 是一套正式的 .NET API 规范,有望在所有 .NET 运行时中推出. 推出 .NET Standard 的背后动机是要提高 .NET 生态系统中的一致性. ECMA ...

  5. SQLServer 日期函数大全 SQLServer 时间函数大全

    原文地址:https://www.cnblogs.com/zhangpengnike/p/6122588.html 一.统计语句 1.--统计当前[>当天00点以后的数据] SELECT * F ...

  6. JVM编译优化

    在部分的商用虚拟机中,Java 程序最初是通过解释器(Interpreter )进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码”.为了提高热点代码的执 ...

  7. JavaScript:Array 对象

    ylbtech-JavaScript:Array 对象 1. 返回顶部 Array 对象 Array 对象用于在单个的变量中存储多个值. 创建 Array 对象的语法: new Array(); ne ...

  8. uva 400 Unix ls 文件输出排版 排序题

    这题的需要注意的地方就是计算行数与列数,以及输出的控制. 题目要求每一列都要有能够容纳最长文件名的空间,两列之间要留两个空格,每一行不能超过60. 简单计算下即可. 输出时我用循环输出空格来解决对齐的 ...

  9. chain33 区块链开发框架诞生记

    chain33 诞生记 很多年没有写博客了,应该说,自从2013年开始玩比特币,就没有写过了.这5年来,做了很多事情,也见了很多以前做梦都没有想到过都事情.我做的最开心的事情,也是觉得最有意义的事情, ...

  10. 基于ZigBee和STM32的智能家居控制系统的设计与实现(三)

    基于ZigBee和STM32的智能家居控制系统的设计与实现(三) 自从前两篇博客介绍了智能家居系统的基本实现机理后,收到了好多朋友的来信,和我讨论了好多的这方面的知识,在此很高兴,虽然自己做的这个所谓 ...