Android中使用ListView实现下拉刷新和上拉加载功能


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 @Override 225 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 @Override 247 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 248 int totalItemCount) { 249 //记录第一个可见item的position 250 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 }

 

posted @ 2017-06-19 13:59  一名Android小生  阅读(368)  评论(0编辑  收藏  举报