背景知识
ListView使用非常广泛,对于使用ListView的应用来说,下拉刷新是必不可少要实现的功能。
我们常用的微博、网易新闻,搜狐新闻都使用了这一功能,如下图所示。
微博
搜狐新闻
具体学习:
首先分析下拉刷新的具体操作过程:
用户手指在ListView上按下并往下拉----->出现一个提示的View在ListView顶部----->ListView内容更新,顶部的提示View消失
具体实现步骤:
1.创建继承自ListView的RefreshListView,并添加顶部提示View
public class RefreshListView extends ListView { View header;// 顶部提示View public RefreshListView(Context context) { super(context); initView(context); } public RefreshListView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } private void initView(Context context) { // LayoutInflater作用是加载布局 LayoutInflater inflater = LayoutInflater.from(context); header = inflater.inflate(R.layout.header_layout, null); this.addHeaderView(header); }}
顶部提示View布局文件 header_layout.xml:
1 26 7 36 3713 21 22 28 29 34 35 44 45 53 54
运行效果图:
2.由上图可以知道,默认加载header是显示出来的。默认我们应该隐藏顶部提示布局header。
1 private void initView(Context context){ 2 //LayoutInflater作用是加载布局 3 LayoutInflater inflater = LayoutInflater.from(context); 4 header = inflater.inflate(R.layout.header_layout, null); 5 measureView(header); 6 headerHeight = header.getMeasuredHeight(); 7 topPadding(-headerHeight); 8 this.addHeaderView(header); 9 }10 /**11 * 设置顶部布局的上边距12 * @param topPadding13 */14 private void topPadding(int topPadding){15 //设置顶部提示的边距16 //除了顶部用参数值topPadding外,其他三个用header默认的值17 header.setPadding(header.getPaddingLeft(), topPadding, 18 header.getPaddingRight(), header.getPaddingBottom());19 //使header无效,将来调用onDraw()重绘View20 header.invalidate();21 }22 /**23 * 通知父布局,占用的宽和高24 */25 private void measureView(View view){26 //LayoutParams are used by views to tell their parents 27 //how they want to be laid out.28 //LayoutParams被view用来告诉它们的父布局它们应该被怎样安排29 ViewGroup.LayoutParams p = view.getLayoutParams();30 if(p==null){31 //两个参数:width,height32 p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,33 ViewGroup.LayoutParams.WRAP_CONTENT);34 }35 //getChildMeasureSpec:获取子View的widthMeasureSpec、heightMeasureSpec值36 int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);37 int height;38 int tempHeight = p.height;39 if(tempHeight>0){40 height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);41 }else{42 height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);43 }44 view.measure(width, height);45 }
运行效果图:
3.监听用户滑动屏幕操作
1.实现OnScrollListener接口
2.根据用户按下、移动、抬起手势来设置不同的响应事件
3.顶部提示View--header有四种状态:
1. 正常状态(NONE)
2.提示用户下拉可以刷新(PULL)
3.提示用户释放可以刷新(RELEASE)
4.提示用户正在刷新状态(REFREASH)
1 public class RefreshListView extends ListView implements OnScrollListener{ 2 View header;//下拉刷新时出现的顶部布局View 3 int headerHeight;//header的高度 4 int firstVisibleItem;//当前界面第一个可见的item的位置 5 6 boolean isFlag;//标志,是在当前显示的listView是在listView最顶端时按下额 7 int startY;//用户按下的Y值 8 9 10 int state;//当前状态11 final int NONE = 0;//正常状态12 final int PULL = 1;//提示下拉状态13 final int RELEASE = 2;//提示释放状态14 final int REFRESH = 3;//提示正在刷新状态15 16 17 @Override18 public void onScrollStateChanged(AbsListView view, int scrollState) {19 // TODO Auto-generated method stub20 this.scrollState = scrollState;21 }22 @Override23 public void onScroll(AbsListView view, int firstVisibleItem,24 int visibleItemCount, int totalItemCount) {25 // TODO Auto-generated method stub26 this.firstVisibleItem = firstVisibleItem;27 }28 @Override29 public boolean onTouchEvent(MotionEvent ev) {30 // TODO Auto-generated method stub31 switch (ev.getAction()) {32 case MotionEvent.ACTION_DOWN:33 if(firstVisibleItem == 0){34 isFlag = true;//ListView最顶端按下,标志值设为真35 startY = (int)ev.getY();36 }37 break;38 case MotionEvent.ACTION_MOVE:39 onMove(ev);40 break;41 case MotionEvent.ACTION_UP:42 if(state == RELEASE){43 state = REFRESH;44 //加载数据45 refreshViewByState();46 iRefreshlistener.onRefresh();47 }else if(state == PULL){48 state = NONE;49 isFlag = false;50 refreshViewByState();51 }52 break;53 }54 return super.onTouchEvent(ev);55
onMove()方法----->根据用户下拉距离的不同设置不同的响应方法
1 private void onMove(MotionEvent ev){ 2 //如果不是最顶端按下,则直接返回 3 if(!isFlag){ 4 return; 5 } 6 int currentY = (int)ev.getY();//当前的Y值 7 int space = currentY - startY;//用户向下拉的距离 8 int topPadding = space - headerHeight;//顶部提示View距顶部的距离值 9 switch (state) {10 //正常状态11 case NONE:12 if(space>0){13 state = PULL;//下拉的距离大于0,则将状态改为PULL(提示下拉更新)14 refreshViewByState();//根据状态的不同更新View15 }16 break;17 case PULL:18 topPadding(topPadding);19 if(space>headerHeight+30//下拉的距离大于header的高度加30且用户滚动屏幕,手指仍在屏幕上20 &&scrollState == SCROLL_STATE_TOUCH_SCROLL ){21 state = RELEASE;//将状态改为RELEASE(提示松开更新)22 refreshViewByState();23 }24 break;25 case RELEASE:26 topPadding(topPadding);27 if(space
根据不同状态,改变用户界面的显示
1 /** 2 * 根据当前状态state,改变界面显示 3 * state: 4 * NONE:无操作 5 * PULL:下拉可以刷新 6 * RELEASE:松开可以刷新 7 * REFREASH:正在刷新 8 */ 9 private void refreshViewByState(){10 //提示11 TextView tips = (TextView)header.findViewById(R.id.tips);12 //箭头13 ImageView arrow = (ImageView)header.findViewById(R.id.arrow);14 //进度条15 ProgressBar progress = (ProgressBar)header.findViewById(R.id.progress);16 //箭头的动画效果1,由0度转向180度,即箭头由朝下转为朝上17 RotateAnimation animation1 = new RotateAnimation(0, 180,18 RotateAnimation.RELATIVE_TO_SELF,0.5f,19 RotateAnimation.RELATIVE_TO_SELF,0.5f);20 animation1.setDuration(500);21 animation1.setFillAfter(true);22 //箭头的动画效果2,由180度转向0度,即箭头由朝上转为朝下23 RotateAnimation animation2 = new RotateAnimation(180, 0,24 RotateAnimation.RELATIVE_TO_SELF,0.5f,25 RotateAnimation.RELATIVE_TO_SELF,0.5f);26 animation2.setDuration(500);27 animation2.setFillAfter(true);28 29 switch (state) {30 case NONE: //正常状态31 arrow.clearAnimation(); //清除箭头动画效果32 topPadding(-headerHeight); //设置header距离顶部的距离33 break;34 35 case PULL: //下拉状态36 arrow.setVisibility(View.VISIBLE); //箭头设为可见37 progress.setVisibility(View.GONE); //进度条设为不可见38 tips.setText("下拉可以刷新"); //提示文字设为"下拉可以刷新"39 arrow.clearAnimation(); //清除之前的动画效果,并将其设置为动画效果240 arrow.setAnimation(animation2);41 break;42 43 case RELEASE: //下拉状态44 arrow.setVisibility(View.VISIBLE); //箭头设为可见45 progress.setVisibility(View.GONE); //进度条设为不可见46 tips.setText("松开可以刷新"); //提示文字设为"松开可以刷新"47 arrow.clearAnimation(); //清除之前的动画效果,并将其设置为动画效果248 arrow.setAnimation(animation1);49 break;50 51 case REFRESH: //更新状态52 topPadding(50); //距离顶部的距离设置为5053 arrow.setVisibility(View.GONE); //箭头设为不可见54 progress.setVisibility(View.VISIBLE); //进度条设为可见55 tips.setText("正在刷新..."); //提示文字设为""正在刷新..."56 arrow.clearAnimation(); //清除动画效果57 break;58 59 }60 }
4.更新数据 由于在RefreshListView中不能直接更新数据,必须设置回调接口来实现更新数据这一功能。
IRefreshListener iRefreshlistener;//刷新数据的接口 ... public void setInterface(IRefreshListener listener){ this.iRefreshlistener = listener; } /** * 刷新数据接口 * @author lenovo * */ public interface IRefreshListener{ public void onRefresh(); }
在MainActivity中实现IRefreshListener接口并实现onRefresh()方法
public class MainActivity extends Activity implements IRefreshListener{ ...... @Override public void onRefresh() { // TODO Auto-generated method stub //handler设置刷新延时效果 Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub //获取最新数据 getRefreshData(); //通知界面显示 adapter.notifyDataSetChanged(); //通知listView刷新数据完毕 listView.refreshComplete(); } }, 2000);}
刷新完成的操作:
1 public void refreshComplete(){ 2 state = NONE; //状态设为正常状态 3 isFlag = false; //标志设为false 4 refreshViewByState(); 5 //设置提示更新时间间隔 6 Time t = new Time(); 7 t.setToNow(); 8 time = t.hour*60+t.minute-updateTime; 9 updateTime = t.hour*60+t.minute;10 TextView lastUpdateTime = (TextView)findViewById(R.id.time);11 lastUpdateTime.setText(time+"分钟前更新");12 }
Demo运行效果图
想及时获取最新最有用的Android开发干货,请关注我
转载请注明网址:
如果觉得本文对你的学习工作有所帮助,不妨在右下方点推荐一下,谢谢。
联系我:JohnTsai.Work@gmail.com