1 public class RefreshListView extends ListView implements OnScrollListener { 2 3 private View mHeaderView;//头布局 4 private View mFooterView;//脚布局 5 private ImageView mArrowView;//刷新箭头 6 private ProgressBar mProgressBar;//进度条 7 private TextView mTitle;//显示的刷新状态标题 8 private TextView mLastRefreshTime;//上一次刷新的时间 9 private RotateAnimation mRotateUpAnim;//向上旋转的动画 10 private RotateAnimation mRotateDownAnim;//向下旋转的动画 11 private float startY;//按下的起始y坐标 12 private int mFirstVisiblePos;//记录第一个可见的item位置 13 private int mHeaderViewHeight;//头布局测量所得的高度 14 private int mFooterViewHeight;//脚布局测量所得的高度 15 private static final int PULL_TO_REFRESH = 0;//下拉刷新 16 private static final int RELEASE_TO_REFRESH = 1;//释放刷新 17 private static final int RELEASING = 2;//正在刷新 18 private int mCurrentState = PULL_TO_REFRESH;//记录当前的刷新状态 19 private boolean isLoadingMore;//记录上拉加载的状态 20 private OnRefreshListener mOnRefreshListener;//下拉刷新接口 21 private OnLoadMoreListener mOnLoadMoreListener;//上拉加载接口 22 23 public RefreshListView(Context context) { 24 super(context); 25 init(); 26 27 } 28 29 public RefreshListView(Context context, AttributeSet attrs) { 30 super(context, attrs); 31 init(); 32 } 33 34 public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) { 35 super(context, attrs, defStyleAttr); 36 init(); 37 } 38 39 private void initHeaderView() { 40 //初始化相关布局控件 41 mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null); 42 mArrowView = (ImageView) mHeaderView.findViewById(R.id.iv_arrow); 43 mProgressBar = (ProgressBar) mHeaderView.findViewById(R.id.pb); 44 mTitle = (TextView) mHeaderView.findViewById(R.id.tv_title); 45 mLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_desc_last_refresh); 46 mLastRefreshTime.setText(getLastRefreshTime()); 47 //提前手动测量,获取mHeaderView的实际高度 48 mHeaderView.measure(0, 0); 49 //获取测量的高度 50 mHeaderViewHeight = mHeaderView.getMeasuredHeight(); 51 //通过设置padding隐藏头布局 52 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); 53 //将头布局增加到ListView中,注意这里必须在setAdapter之前 54 this.addHeaderView(mHeaderView); 55 } 56 57 /** 58 * 初始化操作 59 */ 60 private void init() { 61 initHeaderView(); 62 initFooterView(); 63 initAnimation(); 64 //设置滑动监听 65 this.setOnScrollListener(this); 66 } 67 68 private void initFooterView() { 69 //原理和上面添加头布局一样,不做解释了 70 mFooterView = View.inflate(getContext(), R.layout.layout_footer_list, null); 71 mFooterView.measure(0, 0); 72 mFooterViewHeight = mFooterView.getMeasuredHeight(); 73 mFooterView.setPadding(0, -mFooterViewHeight, 0, 0); 74 this.addFooterView(mFooterView); 75 } 76 77 /** 78 * 初始化下拉刷新的时候,左边箭头的执行动画 79 */ 80 private void initAnimation() { 81 // 向上旋转,围绕自己的中心逆时针旋转180度 82 mRotateUpAnim = new RotateAnimation(0f, -180f, Animation.RELATIVE_TO_SELF, 0.5f, 83 Animation.RELATIVE_TO_SELF, 0.5f); 84 mRotateUpAnim.setDuration(3000);//设置动画持续的时间 85 mRotateUpAnim.setFillAfter(true);//设置动画结束停留在结束的位置 86 // 向下旋转,围绕自己的中心逆时针旋转180度 87 mRotateDownAnim = new RotateAnimation(-180f, -360f, Animation.RELATIVE_TO_SELF, 88 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); 89 mRotateDownAnim.setDuration(3000); 90 mRotateDownAnim.setFillAfter(true); 91 92 } 93 94 /** 95 * 重写ListView的onTouchEvent方法,处理我们的滑动事件 96 */ 97 @Override 98 public boolean onTouchEvent(MotionEvent ev) { 99 switch (ev.getAction()) {100 case MotionEvent.ACTION_DOWN:101 //记录下x坐标102 startY = ev.getY();103 break;104 case MotionEvent.ACTION_MOVE:105 //记录下滑动时的y坐标106 float endY = ev.getY();107 //如果当前已经是正在刷新的状态或者正在加载更多的状态,不做处理108 if (mCurrentState == RELEASING || isLoadingMore) {109 return super.onTouchEvent(ev);//执行父类的逻辑,我们这边不进行处理110 }111 //滑动的距离112 float dy = endY - startY;113 //第一个可见item的position是0.且滑动距离大于0,慢慢显示头布局114 if (dy > 0 && mFirstVisiblePos == 0) {115 //更新头布局的padding。 topPadding=(-自身高度+滑动的距离)116 int paddingTop = (int) (dy - mHeaderViewHeight);117 mHeaderView.setPadding(0, paddingTop, 0, 0);118 //头布局已经完全显示,并且当前的状态不是释放刷新,切换119 if (paddingTop > 0 && mCurrentState != RELEASE_TO_REFRESH) {120 mCurrentState = RELEASE_TO_REFRESH;121 updateHeaderView();122 } else if (paddingTop < 0 && mCurrentState != PULL_TO_REFRESH) {123 //头布局没有完全显示,并且现在不是下拉刷新的状态124 mCurrentState = PULL_TO_REFRESH;125 updateHeaderView();126 127 128 }129 return true;//事件已经被我们消费处理130 }131 132 break;133 case MotionEvent.ACTION_UP:134 //抬起,根据当前的状态135 if (mCurrentState == PULL_TO_REFRESH) {136 //头布局没有完全显示,则恢复原样137 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);138 } else if (mCurrentState == RELEASE_TO_REFRESH) {139 //释放刷新,头布局完全显示了140 mHeaderView.setPadding(0, 0, 0, 0);141 mCurrentState = RELEASING;//正在刷新中142 updateHeaderView();143 }144 break;145 default:146 break;147 }148 return super.onTouchEvent(ev);149 }150 151 /**152 * 更新头布局.根据状态值来切换153 */154 private void updateHeaderView() {155 switch (mCurrentState) {156 case PULL_TO_REFRESH://切换成下拉刷新157 mTitle.setText("下拉刷新");158 mArrowView.startAnimation(mRotateDownAnim);159 break;160 case RELEASE_TO_REFRESH://切换成释放刷新161 mTitle.setText("释放刷新");162 mArrowView.startAnimation(mRotateUpAnim);163 break;164 case RELEASING://切换成正在刷新165 mArrowView.clearAnimation();//这里要清除动画166 mArrowView.setVisibility(INVISIBLE);167 mProgressBar.setVisibility(VISIBLE);168 mTitle.setText("正在刷新中");169 //回调刷新接口方法进行刷新170 if (mOnRefreshListener != null) {171 mOnRefreshListener.onRefresh();172 }173 break;174 default:175 break;176 }177 }178 179 /**180 * 刷新完成181 */182 public void onRefreshComplete() {183 //还原最初的状态184 mCurrentState = PULL_TO_REFRESH;185 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);186 mArrowView.setVisibility(VISIBLE);187 mProgressBar.setVisibility(INVISIBLE);188 String lastRefreshTime = getLastRefreshTime();189 mLastRefreshTime.setText(lastRefreshTime);190 }191 192 /**193 * 上拉加载完成194 */195 public void onLoadMoreComplete() {196 //还原最初的状态197 isLoadingMore = false;198 mFooterView.setPadding(0, 0, 0, 0);199 }200 201 /**202 * 获取上一次刷新的时间203 */204 private String getLastRefreshTime() {205 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");206 return format.format(new Date());207 }208 209 210 /**211 * 对外暴露设置刷新接口的方法212 */213 public void setOnRefreshListener(OnRefreshListener mOnRefreshListener) {214 this.mOnRefreshListener = mOnRefreshListener;215 }216 217 public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {218 this.mOnLoadMoreListener = mOnLoadMoreListener;219 }220 221 /**222 * 滑动状态改变223 */224 @Override225 public void onScrollStateChanged(AbsListView view, int scrollState) {226 if (isLoadingMore || mCurrentState == RELEASING) {227 //正在加载更多,或者刷新状态。不做处理228 return;229 }230 if (scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() == getCount() - 1) {231 //空闲状态且到了最后一个item,执行上拉加载232 isLoadingMore = true;233 mFooterView.setPadding(0, 0, 0, 0);235 setSelection(getCount() - 1);//跳转到最后一条使其显示加载更多236 if (mOnLoadMoreListener != null) {237 mOnLoadMoreListener.onLoadMore();238 }239 }240 241 }242 243 /**244 * 滑动过程245 */246 @Override247 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,248 int totalItemCount) {249 //记录第一个可见item的position250 mFirstVisiblePos = firstVisibleItem;252 253 }254 255 /**256 * 刷新接口257 */258 public interface OnRefreshListener {259 260 void onRefresh();261 }262 263 /**264 * 加载更多接口265 */266 public interface OnLoadMoreListener {267 268 void onLoadMore();269 }270 }