指令汇B新闻客户端开发(三) 下拉刷新
现在我们继续这个新闻客户端的开发,今天分享的是下拉刷新的实现,我们都知道下拉刷新是一个应用很常见也很实用的功能。我这个应用是通过拉ListView来实现刷新的,先看一张刷新的原理图
从图中可知,手指移动的距离就是dy。
刷新分为三种状态:下拉刷新、正在刷新、松开刷新;
定义这三种状态为:
private static final int STATE_PULL_REFRESH = 0;// 下拉刷新
private static final int STATE_RELEASE_REFRESH = 1;// 松开刷新
private static final int STATE_REFRESHING = 2;// 正在刷新
接下来开始写我们的代码了
初始化头布局:
private void initHeaderView() { mHeaderView = View.inflate(getContext(), R.layout.refresh_header, null); this.addHeaderView(mHeaderView); tvTitle = (TextView) mHeaderView.findViewById(R.id.tv_title); tvTime = (TextView) mHeaderView.findViewById(R.id.tv_time); ivArrow = (ImageView) mHeaderView.findViewById(R.id.iv_arr); pbProgress = (ProgressBar) mHeaderView.findViewById(R.id.pb_progress); mHeaderView.measure(0, 0); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏头布局 initArrowAnim(); tvTime.setText("最后刷新时间:" + getCurrentTime()); }初始化脚布局
* 初始化脚布局 */ private void initFooterView() { mFooterView = View.inflate(getContext(), R.layout.refresh_listview_footer, null); this.addFooterView(mFooterView); mFooterView.measure(0, 0); mFooterViewHeight = mFooterView.getMeasuredHeight(); mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏 this.setOnScrollListener(this); }
初始化箭头的动画:
/** * 初始化箭头动画 */ private void initArrowAnim() { // 箭头向上动画 animUp = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animUp.setDuration(200); animUp.setFillAfter(true); // 箭头向下动画 animDown = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animDown.setDuration(200); animDown.setFillAfter(true); }
接下来是实现触摸的方法及偏移量的计算:
@Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: startY = (int) ev.getRawY(); break; case MotionEvent.ACTION_MOVE: if (startY == -1) {// 确保startY有效 startY = (int) ev.getRawY(); } if (mCurrrentState == STATE_REFRESHING) {// 正在刷新时不做处理 break; } int endY = (int) ev.getRawY(); int dy = endY - startY;// 移动偏移量 if (dy > 0 && getFirstVisiblePosition() == 0) {// 只有下拉并且当前是第一个item,才允许下拉 int padding = dy - mHeaderViewHeight;// 计算padding mHeaderView.setPadding(0, padding, 0, 0);// 设置当前padding if (padding > 0 && mCurrrentState != STATE_RELEASE_REFRESH) {// 状态改为松开刷新 mCurrrentState = STATE_RELEASE_REFRESH; refreshState(); } else if (padding < 0 && mCurrrentState != STATE_PULL_REFRESH) {// 改为下拉刷新状态 mCurrrentState = STATE_PULL_REFRESH; refreshState(); } return true; } break; case MotionEvent.ACTION_UP: startY = -1;// 重置 if (mCurrrentState == STATE_RELEASE_REFRESH) { mCurrrentState = STATE_REFRESHING;// 正在刷新 mHeaderView.setPadding(0, 0, 0, 0);// 显示 refreshState(); } else if (mCurrrentState == STATE_PULL_REFRESH) { mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏 } break; default: break; } return super.onTouchEvent(ev); }
刷新下拉控件的布局:
private void refreshState() { switch (mCurrrentState) { case STATE_PULL_REFRESH: tvTitle.setText("下拉刷新"); ivArrow.setVisibility(View.VISIBLE); pbProgress.setVisibility(View.INVISIBLE); ivArrow.startAnimation(animDown); break; case STATE_RELEASE_REFRESH: tvTitle.setText("松开刷新"); ivArrow.setVisibility(View.VISIBLE); pbProgress.setVisibility(View.INVISIBLE); ivArrow.startAnimation(animUp); break; case STATE_REFRESHING: tvTitle.setText("正在刷新..."); ivArrow.clearAnimation();// 必须先清除动画,才能隐藏 ivArrow.setVisibility(View.INVISIBLE); pbProgress.setVisibility(View.VISIBLE); if (mListener != null) { mListener.onRefresh(); } break; default: break; } }
刷新完成之后我们需要收起这个下拉刷新的按钮,额,好吧,总的来说比较复杂了:
/* * 收起下拉刷新的控件 */ public void onRefreshComplete(boolean success) { if (isLoadingMore) {// 正在加载更多... mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏脚布局 isLoadingMore = false; } else { mCurrrentState = STATE_PULL_REFRESH; tvTitle.setText("下拉刷新"); ivArrow.setVisibility(View.VISIBLE); pbProgress.setVisibility(View.INVISIBLE); mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏 if (success) { tvTime.setText("最后刷新时间:" + getCurrentTime()); } } }
我们还可以看到上面有一个实时的时间,获取当前系统时间:
/** * 获取当前时间 */ public String getCurrentTime() { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return format.format(new Date()); } private boolean isLoadingMore; @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) { if (getLastVisiblePosition() == getCount() - 1 && !isLoadingMore) {// 滑动到最后 // System.out.println("到底了....."); mFooterView.setPadding(0, 0, 0, 0);// 显示 setSelection(getCount() - 1);// 改变listview显示位置 isLoadingMore = true; if (mListener != null) { mListener.onLoadMore(); } } } }
在新闻详情页里面要设置一下下拉刷新监听:
// 设置下拉刷新监听 lvList.setOnRefreshListener(new OnRefreshListener() { @Override public void onRefresh() { getDataFromServer(); } @Override public void onLoadMore() { if (mMoreUrl != null) { getMoreDataFromServer(); } else { Toast.makeText(mActivity, "最后一页了", Toast.LENGTH_SHORT) .show(); lvList.onRefreshComplete(false);// 收起加载更多的布局 } } });
这里刷新功能基本上实现了,这里只写出了关键代码。