Android中Listview下拉刷新和上拉加载更多的多种实现方案
listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明。
方案一:添加头布局和脚布局
android系统为listview提供了addfootview和addheadview两个api。这样可以直接自定义一个view,以添加视图的形式实现下来刷新和上拉加载。
实现步骤
1、创建一个类继承listview:class pulltorefreshlistview extends listview;
2、在构造方法中添加headview:addheaderview(headview);
3、获取headview的高。测量控件的高可以有两方法getmeasuredheight和getheight,getmeasuredheight()在onmeasure方法执行之后才能获取到;getheight() 在onlayout方法执行之后才能获取到值;
4、显示和隐藏headview,通过setpadding实现,当向下滑,且第一条可见item是第0条的时候才需要设置headview的paddingtop来显示headview。
显示:headview.setpadding(0,0,0,0);
隐藏:headview.setpadding(0,-headviewheight,0,0);
5、下拉刷新三种状态的判断,移动的时候,当paddingtop < 0 的时候,说明headview没有完全显示出来,进入下拉刷新状态;移动的时候,当paddingtop >= 0 的时候, 说明headview已经完全显示出来了,进入松开以新状态;手指抬起的时候,且当前状态是松开刷新状态的时候,进入正在刷新状态; 当已经是“正在刷新”状态时, 则不允许再做”下拉刷新”和”松开刷新”的操作了,在move事件中加入判断,如果已经是正在刷新状态了,则不处理下拉的操作了。
6、下拉箭头的转动。下拉刷新是向下,松开刷新时向上。旋转动画通过属性动画实现。隐藏箭头的时候要清除动画:iv_arrow.clearanimation(); 如果不隐藏动画效果,设置view.gone之后还是看得见的。
7、headview显示时,当手指松开时的处理,松开时如果是“正在刷新”状态,则把headvie完全显示;松开时如果是“下拉刷新”状态,则把headview完全隐藏
8、增加footerview:addfooterview(footerview)。当listview处于空闲状态,并且最后一条可见item是listview中的最后一条数据时显示footview, footerview显示出来后,listview不会自动上滑把footerview显示出来的,所以需要手动设置:setselection(getcount() - 1);即选中最后一条。
9、增加回调监听器。当listview处于刷新状态的时候会调用onrefreshing()方法;当listview处于加载更多的时候会调用onloadmore()。加载完成后通知控件加载完成。
具体实现:
import com.itheima.pulltorefreshlistview.r; import android.content.context; import android.util.attributeset; import android.view.motionevent; import android.view.view; import android.view.animation.rotateanimation; import android.widget.abslistview; import android.widget.imageview; import android.widget.listview; import android.widget.progressbar; import android.widget.textview; public class pulltorefreshlistview extends listview { private view headerview; private float downy; private int headerviewheight; /** 状态:下拉刷新 */ private static final int state_pull_to_refresh = 0; /** 状态:松开刷新 */ private static final int state_release_refresh = 1; /** 状态:正在刷新 */ private static final int state_refreshing = 2; /** 当前状态 */ private int currentstate = state_pull_to_refresh; // 默认是下拉刷新状态 private imageview iv_arrow; private progressbar progress_bar; private textview tv_state; private rotateanimation upanim; private rotateanimation downanim; private onrefreshinglistener monrefreshinglistener; private view footerview; private int footerviewheight; /** 正在加载更多 */ private boolean loadingmore; public pulltorefreshlistview(context context, attributeset attrs) { super(context, attrs); initheaderview(); initfooterview(); } private void initheaderview() { headerview = view.inflate(getcontext(), r.layout.header_view, null); iv_arrow = (imageview) headerview.findviewbyid(r.id.iv_arrow); progress_bar = (progressbar) headerview.findviewbyid(r.id.progress_bar); showrefreshingprogressbar(false); tv_state = (textview) headerview.findviewbyid(r.id.tv_state); headerview.measure(0, 0); // 主动触发测量,mesure内部会调用onmeasure headerviewheight = headerview.getmeasuredheight(); hideheaderview(); super.addheaderview(headerview); upanim = createrotateanim(0f, -180f); downanim = createrotateanim(-180f, -360f); } private void initfooterview() { footerview = view.inflate(getcontext(), r.layout.footer_view, null); footerview.measure(0, 0);// 主动触发测量,mesure内部会调用onmeasure footerviewheight = footerview.getmeasuredheight(); hidefooterview(); super.addfooterview(footerview); super.setonscrolllistener(new onscrolllistener() { // 当listview滚动的状态发生改变的时候会调用这个方法 @override public void onscrollstatechanged(abslistview view, int scrollstate) { if (scrollstate == onscrolllistener.scroll_state_idle // listview处于空闲状态 && getlastvisibleposition() == getcount() - 1 // 界面上可见的最后一条item是listview中最后的一条item && loadingmore == false // 如果当前没有去做正在加载更多的事情 ) { loadingmore = true; showfooterview(); setselection(getcount() - 1); if (monrefreshinglistener != null) { monrefreshinglistener.onloadmore(); } } } // 当listview滚动的时候会调用这个方法 @override public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) { } }); } private void hidefooterview() { int paddingtop = -footerviewheight; setfooterviewpaddingtop(paddingtop); } private void showfooterview() { int paddingtop = 0; setfooterviewpaddingtop(paddingtop); } private void setfooterviewpaddingtop(int paddingtop) { footerview.setpadding(0, paddingtop, 0, 0); } /** * 设置显示进度的圈圈 * @param showprogressbar 如果是true,则显示progressbar,否则的话显示箭头 */ private void showrefreshingprogressbar(boolean showprogressbar) { progress_bar.setvisibility(showprogressbar ? view.visible : view.gone); iv_arrow.setvisibility(!showprogressbar ? view.visible : view.gone); if (showprogressbar) { iv_arrow.clearanimation(); // 有动画的view要清除动画才能真正的隐藏 } } /** * 创建旋转动画 * @param fromdegrees 从哪个角度开始转 * @param todegrees 转到哪个角度 * @return */ private rotateanimation createrotateanim(float fromdegrees, float todegrees) { int pivotxtype = rotateanimation.relative_to_self; // 旋转点的参照物 int pivotytype = rotateanimation.relative_to_self; // 旋转点的参照物 float pivotxvalue = 0.5f; // 旋转点x方向的位置 float pivotyvalue = 0.5f; // 旋转点y方向的位置 rotateanimation ra = new rotateanimation(fromdegrees, todegrees, pivotxtype, pivotxvalue, pivotytype, pivotyvalue); ra.setduration(300); ra.setfillafter(true); // 让动画停留在结束位置 return ra; } /** 隐藏headerview */ private void hideheaderview() { int paddingtop = -headerviewheight; setheaderviewpaddingtop(paddingtop); } /** 显示headerview */ private void showheaderview() { int paddingtop = 0; setheaderviewpaddingtop(paddingtop); } /** * 设置headerview的paddingtop * @param paddingtop */ private void setheaderviewpaddingtop(int paddingtop) { headerview.setpadding(0, paddingtop, 0, 0); } @override public boolean ontouchevent(motionevent ev) { switch (ev.getaction()) { case motionevent.action_down: downy = ev.gety(); break; case motionevent.action_move: if (currentstate == state_refreshing) { // 如果当前已经是“正在刷新“的状态了,则不用去处理下拉刷新了 return super.ontouchevent(ev); } int fingermovedistancey = (int) (ev.gety() - downy); // 手指移动的距离 // 如果是向下滑动,并且界面上可见的第一条item是listview的索引为0的item时我们才处理下拉刷新的操作 if (fingermovedistancey > 0 && getfirstvisibleposition() == 0) { int paddingtop = -headerviewheight + fingermovedistancey; setheaderviewpaddingtop(paddingtop); if (paddingtop < 0 && currentstate != state_pull_to_refresh) { // 如果paddingtop小于0,说明headerview没有完全显示出来,则进入下拉刷新的状态 currentstate = state_pull_to_refresh; tv_state.settext("下拉刷新"); iv_arrow.startanimation(downanim); showrefreshingprogressbar(false); // 让箭头转一下 } else if (paddingtop >= 0 && currentstate != state_release_refresh) { // 如果paddingtop>=0,说明headerview已经完全显示出来,则进入松开刷新的状态 currentstate = state_release_refresh; tv_state.settext("松开刷新"); iv_arrow.startanimation(upanim); showrefreshingprogressbar(false); } return true; } break; case motionevent.action_up: if (currentstate == state_release_refresh) { // 如果当前状态是松开刷新,并且抬起了手,则进入正在刷新状态 currentstate = state_refreshing; tv_state.settext("正在刷新"); showrefreshingprogressbar(true); showheaderview(); if (monrefreshinglistener != null) { monrefreshinglistener.onrefreshing(); } } else if (currentstate == state_pull_to_refresh) { // 如果抬起手时是下拉刷新状态,则把headerview完成隐藏 hideheaderview(); } break; } return super.ontouchevent(ev); } public void setonrefreshinglistener(onrefreshinglistener monrefreshinglistener) { this.monrefreshinglistener = monrefreshinglistener; } /** listview刷新的监听器 */ public interface onrefreshinglistener { /** 当listview可以刷新数据的时候会调用这个方法 */ void onrefreshing(); /** 当listview可以加载更多 的时候会调用这个方法 */ void onloadmore(); } /** 联网刷新数据的操作已经完成了 */ public void onrefreshcomplete() { hideheaderview(); currentstate = state_pull_to_refresh; showrefreshingprogressbar(false); } /** 加载更多新数据的操作已经完成了 */ public void onloadmorecomplete() { hidefooterview(); loadingmore = false; } }
方案二: listview的多种样式显示
设置listview的适配器的时候可以实现两个方法: getviewtypecount()和getitemviewtype(),前者指定条目的种类,后者返回具体的类型,这样可以根据不同的类型设计相关的样式,包括上拉加载更多,和下拉刷新,两者类似,因此这里仅仅给出加载更多的写法。具体实现如下:
1、重写getviewtypecount()和getitemviewtype(),这里包括普通的item条目和加载更多的条目,所以getviewtypecount()返回值为2;
@override public int getviewtypecount() { return super.getviewtypecount() + 1; } @override public int getitemviewtype(int position) { if (position == getcount() - 1) { return 0; } else { return addviewtype(position); //构造一个方法出来,方便子类修改,添加更多的样式 } } public int addviewtype(int position) { return 1; }
2、在getview()中针对不同的类型添加布局:
@override public view getview(int position, view convertview, viewgroup parent) { baseholdle holdle; if (convertview == null) { if (getitemviewtype(position) == 0) { //type为0 表示应该加载加载更多的视图 holdle = getloadmoreholdle(); } else { //否则为普通视图 holdle = getspecialbaseholdle(position); } } else { holdle = (baseholdle) convertview.gettag(); } if (getitemviewtype(position) == 0) { //加载更多视图,请求网络获取数据 if (havemore()) { holdle.setdataandrefreshholdleview(loadmoreholdle.loadmore_loding); triggleloadmoredata(); } else { holdle.setdataandrefreshholdleview(loadmoreholdle.loadmore_none); } } else { //普通视图视图,请求网络获取数据 t data = (t) mdata.get(position); holdle.setdataandrefreshholdleview(data); } mholdleview = holdle.mholdleview; mholdleview.setscalex(0.6f); mholdleview.setscaley(0.6f); viewcompat.animate(mholdleview).scalex(1).scaley(1).setduration(400).setinterpolator(new overshootinterpolator(4)).start(); return mholdleview; }
3、具体的加载更多视图的实现
private baseholdle getloadmoreholdle() { if (mloadmoreholdle == null) { mloadmoreholdle = new loadmoreholdle(); } return mloadmoreholdle; } public class loadmoreholdle extends baseholdle { @bind(r.id.item_loadmore_container_loading) linearlayout itemloadmorecontainerloading; @bind(r.id.item_loadmore_container_retry) linearlayout itemloadmorecontainerretry; @bind(r.id.item_loadmore_tv_retry) textview item_loadmore_tv_retry; public static final int loadmore_loding = 0; public static final int loadmore_error = 1; public static final int loadmore_none = 2; private int mcurretstate; @override public void refreshholdleview(object data) { itemloadmorecontainerloading.setvisibility(view.gone); itemloadmorecontainerretry.setvisibility(view.gone); mcurretstate = (int) data; switch (mcurretstate) { case loadmore_loding: itemloadmorecontainerloading.setvisibility(view.visible); break; case loadmore_error: itemloadmorecontainerretry.setvisibility(view.visible); break; case loadmore_none: break; } } @override public view ininviewholdle() { view view = view.inflate(uiutils.getcontext(), r.layout.itemloadmore, null); butterknife.bind(this, view); return view; } } //holder基类,提取公共的方法 public abstract class baseholdle<t> { public view mholdleview; public t mdata; public baseholdle() { mholdleview = ininviewholdle(); mholdleview.settag(this); } public void setdataandrefreshholdleview(t mdata) { this.mdata = mdata; refreshholdleview(mdata); } public abstract void refreshholdleview(t data); public abstract view ininviewholdle(); }
方案三: swiperefreshlayout实现下来刷新
swiperefreshlayout对下不兼容,且只有下拉刷新功能没有上拉加载更多的功能。当时作为andriod5.0之后的新特性,使用起来方便,可以直接调用系统的api。使用方法也较为简单。具体实现如下:
首先声明控件,设置颜色:
refreshlayout = (swiperefreshlayout) findviewbyid(r.id.refresh); refreshlayout.setonrefreshlistener(this); refreshlayout.setcolorschemeresources(android.r.color.holo_blue_bright, android.r.color.holo_green_light,android.r.color.holo_orange_light, android.r.color.holo_red_light); refreshlayout.setprogressbackgroundcolor(r.color.refresh_bg); refreshlayout.setprogressbackgroundcolor(r.color.refresh_bg);
写一个类实现swiperefreshlayout.onrefreshlistener,重写onrefresh()方法:
@override public void onrefresh() { refreshlayout.postdelayed(new runnable() { @override public void run() { //请求网络,获取数据 refreshlayout.setrefreshing(false); } },3000); }
以上所述是小编给大家介绍的android中listview下拉刷新和上拉加载更多的多种实现方案,希望对大家有所帮助
下一篇: JSP之plugin的使用