ListView实现下拉刷新(三)实现下拉刷新
2015-10-10 15:36
204 查看
该准备的东西都已经准备好了。在这篇文章里,我们就开始实现下拉刷新功能吧。
一、大体的逻辑分析
我们来简单分析一下需要做的逻辑吧。首先分析头布局有几种状态。不下拉时,为正常状态,此时头布局隐藏。下拉到一定高度,提示信息变为“下拉刷新”,箭头朝下,此为下拉状态。再往下拉,提示信息变为“松开刷新”,箭头朝上,此为提示刷新状态。而此时松开手指,则执行刷新操作,头布局变为进度条显示,箭头消失,此为正在刷新状态。相反的,其他状态下松开手指,都不执行刷新操作,应该将头布局恢复到正常状态。因为可确定头布局的状态有四种。
我们根据这四种状态,确定我们要做的事情。要监听ListView的滚动,故要实现OnScrollListener接口。还要监听手指触摸事件,根据手指的下拉移动来改变头布局的显示效果,根据手指的抬起来判断是否进行刷新操作,因为要实现onTouchEvent方法。也就是说,头布局状态的改变应该随着手指的移动而改变,因此在onTouchEvent里面我们要实现上面分析的四种状态的改变。当然,状态改变就意味着头布局显示效果的改变,这里可以嵌套在onTouchEvent方法里面。但考虑到避免方法臃肿,以及其他地方可能也需要改变头布局界面,比如数据加载完成后等情况,因此专门将头布局界面的改变抽取出来,凝聚为一个方法。
然后就是数据刷新,刷新操作要在MyListView里执行,但是数据要在MainActivity中获取。老规矩,用接口回调即可。
好了,基本上大体的逻辑就这么多了。下面我们将上面的分析转化为代码。
二、代码编写
废话我就不多说了,上面的分析很清楚了。继续完善MyListView即可。代码如下:
接下来就是MainActivity中的代码了。如下:
好了,快快运行下程序,体验下拉刷新的效果吧。至此,ListView实现下拉刷新,我们讲解完毕了。
一、大体的逻辑分析
我们来简单分析一下需要做的逻辑吧。首先分析头布局有几种状态。不下拉时,为正常状态,此时头布局隐藏。下拉到一定高度,提示信息变为“下拉刷新”,箭头朝下,此为下拉状态。再往下拉,提示信息变为“松开刷新”,箭头朝上,此为提示刷新状态。而此时松开手指,则执行刷新操作,头布局变为进度条显示,箭头消失,此为正在刷新状态。相反的,其他状态下松开手指,都不执行刷新操作,应该将头布局恢复到正常状态。因为可确定头布局的状态有四种。
我们根据这四种状态,确定我们要做的事情。要监听ListView的滚动,故要实现OnScrollListener接口。还要监听手指触摸事件,根据手指的下拉移动来改变头布局的显示效果,根据手指的抬起来判断是否进行刷新操作,因为要实现onTouchEvent方法。也就是说,头布局状态的改变应该随着手指的移动而改变,因此在onTouchEvent里面我们要实现上面分析的四种状态的改变。当然,状态改变就意味着头布局显示效果的改变,这里可以嵌套在onTouchEvent方法里面。但考虑到避免方法臃肿,以及其他地方可能也需要改变头布局界面,比如数据加载完成后等情况,因此专门将头布局界面的改变抽取出来,凝聚为一个方法。
然后就是数据刷新,刷新操作要在MyListView里执行,但是数据要在MainActivity中获取。老规矩,用接口回调即可。
好了,基本上大体的逻辑就这么多了。下面我们将上面的分析转化为代码。
二、代码编写
废话我就不多说了,上面的分析很清楚了。继续完善MyListView即可。代码如下:
package com.fuly.load; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.ImageView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; public class MyListView extends ListView implements OnScrollListener{ private View header;//头布局 private int headerHeight;//头布局自身的高度 private int scrollState;//当前滚动状态 private int firstVisibleItem;//当前可见的第一个item private int startY;//刚开始触摸屏幕时的Y值 private int curState = 0;//当前header状态,默认为0 private final int NORMAL = 0;//正常状态 private final int PULL = 1;//状态下拉 private final int RELEASE = 2;//提示刷新状态 private final int RELEASING = 3;//状态正在刷新 private boolean canPull = false;//是否可以执行下拉操作 private refresfListener mListener;//回调接口 //三个构造方法都要重写 public MyListView(Context context) { super(context); initView( context); } public MyListView(Context context, AttributeSet attrs) { super(context, attrs); initView( context); } public MyListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView( context); } //定义回调接口 public interface refresfListener{ void refresh(); } public void setOnRefreshListener(refresfListener listener){ this.mListener = listener; } public void initView(Context context){ header = LayoutInflater.from(context).inflate(R.layout.header, null); notifyView(header); headerHeight = header.getMeasuredHeight();//获取header的高度 // headerHeight = header.getHeight(); paddingTop(-headerHeight); //将头布局加进去 this.addHeaderView(header); this.setOnScrollListener(this); } /** * 该方法为通知父布局,子布局view的宽度和高度 * @param view:子布局 */ private void notifyView(View view){ ViewGroup.LayoutParams p = view.getLayoutParams(); if(p == null){ p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } //spec表示当前子view左右边距,padding表示子view的左右内边距 //childDimension:子view的宽度 int width = ViewGroup.getChildMeasureSpec(0, 0, p.width); int height; int tempHeight = p.height; if(tempHeight>0){ //子布局高度不为空,需要填充这个布局 height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY); }else{ //高度为0,则不需要填充 height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } //然后告诉父布局,子布局的高度和宽度 view.measure(width, height); } //该方法设定header的paddingTop private void paddingTop(int pt){ header.setPadding(header.getPaddingLeft(), pt, header.getPaddingRight(), header.getPaddingBottom()); header.invalidate(); } /*** * 监听当前滚动状态 * scrollState:当前滚动状态 */ public void onScrollStateChanged(AbsListView view, int scrollState) { //记录当前的滚动状态 this.scrollState = scrollState; } /*** * 监听当前滚动的item * firstVisibleItem:当前可见的第一个item * visibleItemCount:当前共有多少个item可见 * totalItemCount:总共有多少个item * */ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { this.firstVisibleItem = firstVisibleItem; } //触屏事件 public boolean onTouchEvent(MotionEvent ev) { switch(ev.getAction()){ //手指落到屏幕上时 case MotionEvent.ACTION_DOWN: //如果当前可见的第一个item为第0号,说明ListView位于顶端,可以执行下拉刷新 if(firstVisibleItem == 0){ canPull = true; startY = (int) ev.getY(); } break; //手指在屏幕上拖动时 case MotionEvent.ACTION_MOVE: if(canPull){ touchMove(ev); } break; //手指离开屏幕时 case MotionEvent.ACTION_UP: canPull = false; if(curState == RELEASE){ curState = RELEASING; refreshHeaderByState(); //这里添加刷新数据的逻辑 mListener.refresh(); }else{ curState = NORMAL; refreshHeaderByState(); paddingTop(-headerHeight); } break; } return super.onTouchEvent(ev); } /** * 该方法根据触摸屏幕滑动来改变STATE,即改变当前状态 * @param ev */ private void touchMove(MotionEvent ev) { int tempY = (int) ev.getY(); int space = tempY -startY;//移动的距离 int topdding = space-headerHeight; paddingTop(topdding);//即时设定头布局的隐藏高度 if(space>headerHeight&&space<headerHeight+50&&scrollState == SCROLL_STATE_TOUCH_SCROLL){ curState = PULL;//设定为下拉状态 refreshHeaderByState(); } if(space>headerHeight+50){ curState = RELEASE;//设定为提示刷新状态 refreshHeaderByState(); } if(space<headerHeight){ curState = NORMAL;//设定为正常状态 refreshHeaderByState(); } } /** * 根据当前状态更改header的显示界面 * */ private void refreshHeaderByState( ){ ProgressBar pb = (ProgressBar) header.findViewById(R.id.progress_bar); ImageView img = (ImageView) header.findViewById(R.id.img_arrow); TextView tv = (TextView) header.findViewById(R.id.textinfo); switch(curState){ case NORMAL: pb.setVisibility(View.GONE); img.setVisibility(View.VISIBLE); img.setImageResource(R.drawable.down_arrow); tv.setText("下拉刷新"); break; case PULL: pb.setVisibility(View.GONE); img.setVisibility(View.VISIBLE); img.setImageResource(R.drawable.down_arrow); tv.setText("下拉刷新"); break; case RELEASE: pb.setVisibility(View.GONE); img.setVisibility(View.VISIBLE); img.setImageResource(R.drawable.up_arrow); tv.setText("松开刷新"); break; case RELEASING: pb.setVisibility(View.VISIBLE); img.setVisibility(View.GONE); tv.setText("正在刷新"); break; } } //数据刷新完成后的操作 public void refreshFinish(){ curState = NORMAL; paddingTop(-headerHeight); refreshHeaderByState(); } }
接下来就是MainActivity中的代码了。如下:
package com.fuly.load; import java.util.ArrayList; import java.util.List; import com.fuly.load.MyListView.refresfListener; import android.os.Bundle; import android.os.Handler; import android.app.Activity; public class MainActivity extends Activity implements refresfListener{ private MyListView lv; private List<MyData> mDatas = new ArrayList<MyData>(); private MyAdapter mAdapter; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData();//该方法初始化数据 lv = (MyListView) findViewById(R.id.list_view); lv.setOnRefreshListener(this);//设定回调接口 mAdapter = new MyAdapter(this, mDatas); lv.setAdapter(mAdapter); } /** * 该方法初始化数据,即提供初始的素材 */ private void initData() { for(int i = 0;i<12;i++){ MyData md = new MyData("你好,我是提前设定的"); mDatas.add(md); } } /** * 提供刷新数据 */ private void getRefreshData() { for(int i = 0;i<3;i++){ MyData md = new MyData("你好,我是刷新进来的"); mDatas.add(i, md); } } //重写回调方法 public void refresh() { //在这里之所以使用Handler,是想让操作延迟,这样子效果看起来更 //清晰,实际项目中,是 不需要的 Handler mHandler = new Handler(); mHandler.postDelayed(new Runnable(){ @Override public void run() { //获得刷新数据 getRefreshData(); //刷新ListView mAdapter.notifyDataSetChanged(); //lv.setSelection(mDatas.size()-1); //刷新后 lv.refreshFinish(); } }, 5000 ); } }
好了,快快运行下程序,体验下拉刷新的效果吧。至此,ListView实现下拉刷新,我们讲解完毕了。
相关文章推荐
- sqlServer 存储过程执行遇到的问题及解决方案
- Lamp最新源码包一般安装过程及常见问题
- spring与servlet集成开发对象管理问题解决方法
- 谈谈iOS的layoutSubviews机制
- 王立平-NGUI
- Servlet声明周期
- hdu 4815 Little Tiger vs. Deep Monkey(01背包)
- [算法练习] 反转链表
- 【OpenCV入门指南】第一篇 安装OpenCV
- 布局Panel TGridPanel TFlowPanel
- hdu5289 Assignment
- 电脑怎么安装安卓(Android)x86 不使用U盘安装Android x86 4.4详细教程
- 数据结构--栈的构造、插入、删除、得到栈顶元素操作
- ztree系列之:如何动态更新节点数据(名称、图标、字体、颜色等)
- ios开发之IBOutlet和IBAction的区别
- 当代与古代
- [微信]微信网页授权用户基本信息接口文档
- bootstrap modal垂直居中
- GSM蜂窝基站定位基本原理浅析
- 堆和栈的总结