android下拉刷新ListView的介绍和实现代码
大致上,我们发现,下拉刷新的列表和一般列表的区别是,当滚动条在顶端的时候,再往下拉动就会把整个列表拉下来,显示出松开刷新的提示。由此可以看出,在构建这个下拉刷新的组件的时候,只用继承listview,然后重写ontouchevent就能实现。还有就是要能在xml布局文件中引用,还需要一个参数为context,attributeset的构造函数。
表面上的功能大概就这些了。另一方面,刷新的行为似乎还没有定义,在刷新前做什么,刷新时要做什么,刷新完成后要做什么,这些行为写入一个接口中,然后让组件去实现。
在整个组件的实现中,主体部分自然是ontouchevent的部分。这里需要做一些说明,在listview中,数据的滚动和listview.scrollto的行为是不一样的。数据的滚动是大概适配器的事。所以在不满足下拉整个列表的条件下,ontouchevent 应该返回super.ontouchevent(ev),让listview组件原本的ontouchevent去处理。
考虑到组件的id和表头的布局需要事先定义,同时我想把这个组件应用于多个项目里,所以就把这个组件作为一个library去实现。
下面就是具体的实现代码。
首先来看一下表头的布局文件chenzong_push_refresh_header.xml:
<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dip"
>
<imageview
android:layout_width="30dip"
android:layout_height="40dip"
android:background="@drawable/arrow_down"
android:layout_alignparentleft="true"
android:id="@+id/push_refresh_header_img"
android:layout_marginleft="10dip"
/>
<progressbar
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_alignparentleft="true"
android:layout_marginleft="10dip"
android:id="@+id/push_refresh_header_pb"
style="@android:style/widget.progressbar.inverse"
android:visibility="gone"/>
<linearlayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerinparent="true"
android:orientation="vertical"
>
<textview
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="最近一次更新在:"
android:textcolor="#000000"
/>
<textview
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/push_refresh_header_date"
android:textcolor="#000000"
android:text="2013-03-04 08:03:38"/>
</linearlayout>
</relativelayout>
箭头、processbar和最近的一次刷新时间,表头文件就这三个元素。
刷新的行为接口refreshoperation的代码:
public interface refreshoperation {
public void onrefreshstart();
public void onrefreshing();
public void onrefreshend();
}
列表拉下来时,箭头翻转的动画arrow_rotate.xml:
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator"
android:fromdegrees="0"
android:todegrees="180"
android:duration="300"
android:pivotx="50%"
android:pivoty="50%"
android:fillafter="true"
android:repeatcount="0">
</rotate>
这些文件和一些资源文件备齐了之后,接下来就是下拉刷新列表pushrefreshlist的具体实现:
package com.chenzong;
import java.util.calendar;
import com.doall.pushrefreshlist.r;
import android.content.context;
import android.os.handler;
import android.os.message;
import android.util.attributeset;
import android.view.layoutinflater;
import android.view.motionevent;
import android.view.view;
import android.view.animation.animation;
import android.view.animation.animationutils;
import android.widget.listview;
import android.widget.textview;
public class pushrefreshlist extends listview implements refreshoperation{
private int header_layout=r.layout.chenzong_push_refresh_header;
//表头文件
private int arrow_down=r.drawable.arrow_down;
//箭头往下的资源
private int arrow_up=r.drawable.arrow_up;
//箭头往上的资源
private int img=r.id.push_refresh_header_img;
//显示箭头的控件id
private int pb=r.id.push_refresh_header_pb;
//刷新时的进度条
private int startpoint=0;
//触摸的起始点
private refreshoperation refresh;
//刷新行为的对象
private animation animation=null;
private context context;
private view headerview;
private int minpushheight;
private final string tag="pushrefresh";
public pushrefreshlist(context cotext, attributeset attrs) {
super(context, attrs);
view empty=new view(context);
//判断是否到列表的顶端,通常要用到this.getfirstvisibleposition(),这里创建一个高度的为零view,加到headerview和数据之间
this.addheaderview(empty);
layoutinflater inflater=layoutinflater.from(context);
headerview=inflater.inflate(header_layout, null);
this.addheaderview(headerview);
this.setrefreshoperation(this);
this.context=context;
}
@override
protected void onlayout(boolean changed, int l, int t, int r, int b) {
this.minpushheight=headerview.getmeasuredheight();
//获取下拉刷新的触发高度
super.onlayout(changed, l, t, r, b);
}
private boolean canhandleevent(int dy)
{
return (dy<0&&this.getfirstvisibleposition()==0&&!ispbvisible());
}
@override
public boolean ontouchevent(motionevent ev) {
int action=ev.getaction();
switch(action)
{
case motionevent.action_down:
startpoint=(int)ev.gety();
break;
case motionevent.action_move:
int dy=startpoint-(int)ev.gety();
if(canhandleevent(dy))
{
if(animation==null)
{
if(math.abs(this.getscrolly())>=this.minpushheight)
{
animation=animationutils.loadanimation(context, r.anim.arrow_rotate);
view mview=headerview.findviewbyid(img);
mview.startanimation(animation);
this.setscrollbarfadingenabled(true);
}
}
this.scrollto(0,dy/2);
return true;
}
break;
case motionevent.action_up:
this.setscrollbarfadingenabled(false);
if(animation!=null)
{
setimgbackgroundup();
switchcompent(view.invisible,view.visible);
this.scrollto(0,-minpushheight);
pushrefreshlist.this.refresh.onrefreshstart();
new thread(mrunnable).start();
animation=null;
}
else
this.scrollto(0,0);
break;
}
return super.ontouchevent(ev);
}
private runnable mrunnable=new runnable()
{
@override
public void run() {
pushrefreshlist.this.refresh.onrefreshing();
mhandler.obtainmessage().sendtotarget();
}
};
private handler mhandler=new handler()
{
@override
public void handlemessage(message msg) {
pushrefreshlist.this.refresh.onrefreshend();
pushrefreshlist.this.scrollto(0, 0);
pushrefreshlist.this.setimgbackgrounddown();
pushrefreshlist.this.switchcompent(view.visible, view.gone);
textview tv=(textview)headerview.findviewbyid(r.id.push_refresh_header_date);
tv.settext(this.getdatestr());
}
private string getdatestr()
{
calendar ca=calendar.getinstance();
int year=ca.get(calendar.year);
int month=ca.get(calendar.month);
int date=ca.get(calendar.date);
int hour=ca.get(calendar.hour);
int mintes=ca.get(calendar.minute);
int second=ca.get(calendar.second);
return year+"-"+(month+1)+"-"+date+" "+hour+":"+mintes+":"+second;
}
};
private void switchcompent(int imgstatus,int pbstatus)
{
view img=headerview.findviewbyid(r.id.push_refresh_header_img);
img.clearanimation();
//执行了动画的控件如果不调用clearanimation,setvisibility(view.gone)会失效
img.setvisibility(imgstatus);
headerview.findviewbyid(r.id.push_refresh_header_pb).setvisibility(pbstatus);
}
private boolean ispbvisible()
{
return view.visible==headerview.findviewbyid(r.id.push_refresh_header_pb).getvisibility();
}
private void setimgbackgroundup()
{
view mview=headerview.findviewbyid(this.img);
mview.setbackgroundresource(arrow_up);
}
private void setimgbackgrounddown()
{
view mview=headerview.findviewbyid(this.img);
mview.setbackgroundresource(arrow_down);
}
public void setrefreshoperation(refreshoperation refresh)
{
this.refresh=refresh;
}
@override
public void onrefreshstart() {
}
@override
public void onrefreshing() {
}
@override
public void onrefreshend() {
}