1、继承ListView类来扩展实现
2、ListView类可以通过addFooterView和addHeaderView方法来添加列表的头和尾,可以用来实现一些拉动的动画和提示
3、重载public boolean onTouchEvent(MotionEvent ev)方法,可以监听到触摸点的点下、滑动和离开的操作,用来判断滑动的方向
4、 public void onScrollStateChanged(AbsListView view, int scrollState)事件,监控列表的滚动状态
import java.text.SimpleDateFormat; import java.util.Date; import android.content.Context; import android.util.AttributeSet; 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.ListView; import android.widget.ProgressBar; import android.widget.TextView; /** * 自定义ListView,下拉刷新、上拉自动加载更多 */ public class PullListView extends ListView implements OnScrollListener { private final int PULL_DOWN_REFRESH = 0;//下拉状态 private final int RELEASE_REFRESH = 1;//松开状态 private final int REFRESHING = 2;//刷新中状态 private int currentState = PULL_DOWN_REFRESH; private int mListViewOnScreenY = -1;//listview的y坐标 private int downY = -1;//触摸点的y坐标 private boolean isLoadingMore = false; private boolean isEnabledPullDownRefresh = false; private boolean isEnabledLoadMore = false; private OnPullDownRefresh mOnPullDownRefresh; //头布局、脚布局及高度 private View mFootView; private LinearLayout mHeaderView; private int mFooterViewHeight; private int mPullDownHeaderViewHeight; //mHeaderView中组件及动画 private View mCustomHeaderView;//用户自定义头布局 private View mPullDownHeader;//下拉刷新头布局 private RotateAnimation upAnimation,downAnimation; private ImageView ivArrow; private ProgressBar mProgressBar; private TextView tv_statue,tv_time; public PullListView(Context context) { this(context, null); } public PullListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PullListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initPullDownHeaderView(); initLoadMoreFooterView(); } private void initLoadMoreFooterView() { //加载更多的布局文件 mFootView = View.inflate(getContext(), R.layout.pull_listview_footer, null); mFootView.measure(0, 0);//测量 mFooterViewHeight = mFootView.getMeasuredHeight(); //隐藏脚布局 mFootView.setPadding(0,-mFooterViewHeight,0,0); addFooterView(mFootView); setOnScrollListener(this); } private void initPullDownHeaderView() { //下拉刷新的布局文件 mHeaderView = (LinearLayout) View.inflate(getContext(), R.layout.pull_listview_header, null); mPullDownHeader = mHeaderView .findViewById(R.id.ll_refresh_pull_down_header); ivArrow = (ImageView) mHeaderView .findViewById(R.id.iv_refresh_header_arrow); mProgressBar = (ProgressBar) mHeaderView .findViewById(R.id.pb_refresh_header); tv_statue = (TextView) mHeaderView .findViewById(R.id.tv_refresh_header_status); tv_time = (TextView) mHeaderView .findViewById(R.id.tv_refresh_header_time); mPullDownHeader.measure(0, 0);//测量 mPullDownHeaderViewHeight = mPullDownHeader.getMeasuredHeight(); //隐藏头布局 mPullDownHeader.setPadding(0, -mPullDownHeaderViewHeight, 0, 0); addHeaderView(mHeaderView); initAnimation(); } /** * 添加额外的头布局,比如轮播图 * @param v 自定义头布局 */ public void addListViewCustomHeaderView(View v) { mCustomHeaderView = v; mHeaderView.addView(mCustomHeaderView); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downY = (int) ev.getY(); break; case MotionEvent.ACTION_MOVE: if (downY == -1)//-1是初始值 downY = (int) ev.getY(); //是否启用下拉刷新 if (!isEnabledPullDownRefresh) break; if (currentState == REFRESHING) break; //解决用户添加header与下拉刷新header的冲突 if (mCustomHeaderView != null) { int[] location = new int[2]; if (mListViewOnScreenY == -1) { this.getLocationOnScreen(location);//获取控件在屏幕的xy坐标 左上角的 mListViewOnScreenY = location[1]; } mCustomHeaderView.getLocationOnScreen(location); if (location[1] < mListViewOnScreenY) { break; } } int moveY = (int) ev.getY(); int diffY = (moveY - downY)/2;//当前的点和上一次的点的距离 if (diffY > 0 && getFirstVisiblePosition() == 0) {//下拉操作 并且listview第一个item可见 int paddingTop = -mPullDownHeaderViewHeight + diffY;//距离顶部的距离 if (paddingTop < 0 && currentState != PULL_DOWN_REFRESH) { //当前没有完全显示且当前状态为松开刷新,进入下拉刷新 currentState = PULL_DOWN_REFRESH; refreshPullDownState(); } else if (paddingTop > 0 && currentState != RELEASE_REFRESH) { //当前完全显示且当前状态为下拉刷新,进入松开刷新 currentState = RELEASE_REFRESH; refreshPullDownState(); } mPullDownHeader.setPadding(0, paddingTop, 0, 0); return true; }else if(diffY < 0 && getLastVisiblePosition() == getCount()-1){//上拉操作 并且最后一个item可见 //脚布局可向上滑动 mFootView.setPadding(0,0,0,0); } break; case MotionEvent.ACTION_UP: downY = -1; if (currentState == PULL_DOWN_REFRESH) { //隐藏header mPullDownHeader.setPadding(0, -mPullDownHeaderViewHeight, 0, 0); } else if (currentState == RELEASE_REFRESH) { currentState = REFRESHING; refreshPullDownState(); mPullDownHeader.setPadding(0, 0, 0, 0); //回调刷新方法 if (mOnPullDownRefresh != null) { mOnPullDownRefresh.onPullDownRefresh(); } } break; } return super.onTouchEvent(ev); } /** * 隐藏头布局或脚布局并重置控件 */ public void OnRefreshDataFinish() { if (isLoadingMore) { isLoadingMore = false; mFootView.setPadding(0,-mFooterViewHeight,0,0); } else { ivArrow.setVisibility(View.VISIBLE); mProgressBar.setVisibility(View.INVISIBLE); tv_statue.setText("下拉刷新"); tv_time.setText("最后刷新时间:" + getCurrentTime()); mPullDownHeader.setPadding(0, -mPullDownHeaderViewHeight, 0, 0); currentState = PULL_DOWN_REFRESH; } } private String getCurrentTime() { SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); return format.format(new Date()); } private void refreshPullDownState() { switch (currentState) { case PULL_DOWN_REFRESH: ivArrow.startAnimation(downAnimation); tv_statue.setText("下拉刷新"); break; case RELEASE_REFRESH: ivArrow.startAnimation(upAnimation); tv_statue.setText("松开刷新"); break; case REFRESHING: ivArrow.clearAnimation(); ivArrow.setVisibility(View.INVISIBLE); mProgressBar.setVisibility(View.VISIBLE); tv_statue.setText("正在刷新"); break; default: break; } } /** * 箭头旋转动画 */ private void initAnimation() { upAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); upAnimation.setDuration(200); upAnimation.setFillAfter(true); downAnimation = new RotateAnimation(-180, -360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); downAnimation.setDuration(200); downAnimation.setFillAfter(true); } /** * 回调方法,用于刷新数据及加载更多 * @param listener */ public void setOnPullDownRefresh(OnPullDownRefresh listener) { this.mOnPullDownRefresh = listener; } public interface OnPullDownRefresh { public void onPullDownRefresh(); public void onLoadingMore(); } /* 下拉加载更多通过onScrollStateChanged事件来实现 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (!isEnabledLoadMore) { return; } //listView停止状态或惯性滑动状态 if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) { //listView已到达最底部 if ((getLastVisiblePosition() == getCount() - 1) && !isLoadingMore) { isLoadingMore = true; //展示脚布局 mFootView.setPadding(0, 0, 0, 0); setSelection(getCount()); if (mOnPullDownRefresh != null) { mOnPullDownRefresh.onLoadingMore(); } } } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } /** * 是否启用下拉刷新 * @param isEnable */ public void setEnabledPullDownRefresh(boolean isEnable) { isEnabledPullDownRefresh = isEnable; } /** * 是否启用加载更多 * @param isEnable */ public void setEnabledLoadMore(boolean isEnable) { isEnabledLoadMore = isEnable; } }