在介绍下拉刷新的实现之前,先看看效果图,可以看到,除了丑了一点,效果还是不错的,下面就来正式开始介绍实现。
总体介绍
首先我们思考下,要实现这个效果,需要些什么
- 给ListView添加下拉显示出来的视图
- 给ListView添加滑动的监听(OnTouch)
- 将滑动与下拉视图相结合,根据滑动的位置来更新视图
给ListView添加下拉的视图
- 定义下拉布局
我们可以看到,下拉布局里面包括一个下拉箭头(ImageView),一个提示的文字(TextView),一个刷新时的进度条(ProgressBar),当然,有的人还多用一个TextView用于显示上次刷新时间,不过原理都是一样的,下面是下拉布局的xml文件,其中的进度条一开始是隐藏的
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="match_parent" android:layout_width="match_parent"> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="@string/updata" android:textSize="23sp" /> <ImageView android:id="@+id/arrow" android:layout_width="40dp" android:layout_height="40dp" android:layout_toLeftOf="@id/text" android:src="@drawable/arrow" /> <ProgressBar android:id="@+id/progress" style="?android:attr/progressBarStyle" android:layout_width="40dp" android:layout_height="40dp" android:layout_toLeftOf="@id/text" android:visibility="gone" /> </RelativeLayout> </LinearLayout>
- 将下拉布局添加在ListView里面
ListView有一个成员函数addHeaderView(View v)用于添加一个view到ListView的头部,通过这个函数我们就可以把上面定义的布局文件通过View.inflate()加载出来,添加到ListView的头部
- 将头部下拉布局给隐藏
通过上面介绍的一个函数,我们就可以添加一个布局到ListView的头部了,可是这样并不是我们想要的样子,我们还需要把他给隐藏,而且希望在滑动的时候这个视图慢慢的出来或者消失,这个效果,我们可以通过setPadding()来设置头部布局的内边距,当顶部的内边距为负的总高度时,这个视图就隐藏了。当手指下拉的时候,根据滑动的距离,设置内部顶部边距,就可以实现顶部视图随手指滑动而移动的效果了
给ListView添加滑动监听
我选择的是直接覆写OnTouch方法实现点击监听,当手指移动的时候,判断ListView的第一项是否是在最顶部,在最顶部则记录当前手指的位置,继续移动,这判断从上次开始移动到目前的距离,用于设置内部上边距
/** * 实现点击监听 */ @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: break; } return super.onTouchEvent(ev); }
判断当前ListView当前是不是最顶部也很简单,实现OnScrollListener接口,覆写如下方法。
@Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { }
此方法最重要的是后面几个参数
firstVisibleItem:第一个可见的item的编号,从0开始
visibleItemCount:屏幕可见的item总数
totalItemCount:ListView全部item数量
这里我们主要使用的是firstVisibleItem,当firstVisibleItem为0的时候,说明ListView已经到了最顶部,如果继续拉下,则表示需要显示下拉视图了,这时记录手指的位置,然后根据继续下拉的距离,就可以慢慢显示下拉视图了,当松开手以后,根据已经下拉的距离,如果大于一定值就开始刷新,如果达到刷新的距离那就直接复原(重新隐藏下拉视图就行)。
总结一下,在下拉过程中发生的事:
用户点击屏幕开始下拉⇒距离达到一定程度(例子中是当下拉视图全部显示时),改变提示文字“下拉刷新”为"松开手指刷新"⇒释放手指,根据已经下拉的距离判断是否刷新或者直接隐藏下拉框
刷新的时候,视图的变化我封装成一个函数updateViewByState(),根据状态更新视图,不过为了突出重点,大家可以在源码里面看,不过大致的思路就是这样了
关于刷新
关于数据刷新的问题,我使用了接口回调的方式,在类中定义了一个接口,然后对外提供设置接口的方法,当外部需要调用刷新的时候,就可以实现这个接口,然后通过我提供的setOnUpdateListener方法传递进来一个接口的实例,类似于控件的setOnClick()方法
/** * 接口变量,用于保存调用者传过来的对象 */ private UpdateView listener = null; /** * 回调接口,用于给调用者刷新 */ public interface UpdateView { public void updateListView(); } /** * 类似于button.setOnClieckListener() * 用此方法给调用者设置刷新响应 */ public void setOnUpdateListener(UpdateView listener) { this.listener = listener; }
源码下载:360云盘 访问密码 8cf8