Android中Listview下拉刷新和上拉加载更多的多种实现方案
listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明。
方案一:添加头布局和脚布局
android系统为listview提供了addfootview和addheadview两个API。这样可以直接自定义一个View,以添加视图的形式实现下来刷新和上拉加载。
实现步骤
1、创建一个类继承ListView:classPullToRefreshListViewextendsListView;
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()。加载完成后通知控件加载完成。
具体实现:
importcom.itheima.pulltorefreshlistview.R; importandroid.content.Context; importandroid.util.AttributeSet; importandroid.view.MotionEvent; importandroid.view.View; importandroid.view.animation.RotateAnimation; importandroid.widget.AbsListView; importandroid.widget.ImageView; importandroid.widget.ListView; importandroid.widget.ProgressBar; importandroid.widget.TextView; publicclassPullToRefreshListViewextendsListView{ privateViewheaderView; privatefloatdownY; privateintheaderViewHeight; /**状态:下拉刷新*/ privatestaticfinalintSTATE_PULL_TO_REFRESH=0; /**状态:松开刷新*/ privatestaticfinalintSTATE_RELEASE_REFRESH=1; /**状态:正在刷新*/ privatestaticfinalintSTATE_REFRESHING=2; /**当前状态*/ privateintcurrentState=STATE_PULL_TO_REFRESH;//默认是下拉刷新状态 privateImageViewiv_arrow; privateProgressBarprogress_bar; privateTextViewtv_state; privateRotateAnimationupAnim; privateRotateAnimationdownAnim; privateOnRefreshingListenermOnRefreshingListener; privateViewfooterView; privateintfooterViewHeight; /**正在加载更多*/ privatebooleanloadingMore; publicPullToRefreshListView(Contextcontext,AttributeSetattrs){ super(context,attrs); initHeaderView(); initFooterView(); } privatevoidinitHeaderView(){ 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); } privatevoidinitFooterView(){ footerView=View.inflate(getContext(),R.layout.footer_view,null); footerView.measure(0,0);//主动触发测量,mesure内部会调用onMeasure footerViewHeight=footerView.getMeasuredHeight(); hideFooterView(); super.addFooterView(footerView); super.setOnScrollListener(newOnScrollListener(){ //当ListView滚动的状态发生改变的时候会调用这个方法 @Override publicvoidonScrollStateChanged(AbsListViewview,intscrollState){ 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 publicvoidonScroll(AbsListViewview,intfirstVisibleItem,intvisibleItemCount,inttotalItemCount){ } }); } privatevoidhideFooterView(){ intpaddingTop=-footerViewHeight; setFooterViewPaddingTop(paddingTop); } privatevoidshowFooterView(){ intpaddingTop=0; setFooterViewPaddingTop(paddingTop); } privatevoidsetFooterViewPaddingTop(intpaddingTop){ footerView.setPadding(0,paddingTop,0,0); } /** *设置显示进度的圈圈 *@paramshowProgressBar如果是true,则显示ProgressBar,否则的话显示箭头 */ privatevoidshowRefreshingProgressBar(booleanshowProgressBar){ progress_bar.setVisibility(showProgressBar?View.VISIBLE:View.GONE); iv_arrow.setVisibility(!showProgressBar?View.VISIBLE:View.GONE); if(showProgressBar){ iv_arrow.clearAnimation();//有动画的View要清除动画才能真正的隐藏 } } /** *创建旋转动画 *@paramfromDegrees从哪个角度开始转 *@paramtoDegrees转到哪个角度 *@return */ privateRotateAnimationcreateRotateAnim(floatfromDegrees,floattoDegrees){ intpivotXType=RotateAnimation.RELATIVE_TO_SELF;//旋转点的参照物 intpivotYType=RotateAnimation.RELATIVE_TO_SELF;//旋转点的参照物 floatpivotXValue=0.5f;//旋转点x方向的位置 floatpivotYValue=0.5f;//旋转点y方向的位置 RotateAnimationra=newRotateAnimation(fromDegrees,toDegrees,pivotXType,pivotXValue,pivotYType,pivotYValue); ra.setDuration(300); ra.setFillAfter(true);//让动画停留在结束位置 returnra; } /**隐藏HeaderView*/ privatevoidhideHeaderView(){ intpaddingTop=-headerViewHeight; setHeaderViewPaddingTop(paddingTop); } /**显示HeaderView*/ privatevoidshowHeaderView(){ intpaddingTop=0; setHeaderViewPaddingTop(paddingTop); } /** *设置HeaderView的paddingTop *@parampaddingTop */ privatevoidsetHeaderViewPaddingTop(intpaddingTop){ headerView.setPadding(0,paddingTop,0,0); } @Override publicbooleanonTouchEvent(MotionEventev){ switch(ev.getAction()){ caseMotionEvent.ACTION_DOWN: downY=ev.getY(); break; caseMotionEvent.ACTION_MOVE: if(currentState==STATE_REFRESHING){ //如果当前已经是“正在刷新“的状态了,则不用去处理下拉刷新了 returnsuper.onTouchEvent(ev); } intfingerMoveDistanceY=(int)(ev.getY()-downY);//手指移动的距离 //如果是向下滑动,并且界面上可见的第一条item是ListView的索引为0的item时我们才处理下拉刷新的操作 if(fingerMoveDistanceY>0&&getFirstVisiblePosition()==0){ intpaddingTop=-headerViewHeight+fingerMoveDistanceY; setHeaderViewPaddingTop(paddingTop); if(paddingTop<0&¤tState!=STATE_PULL_TO_REFRESH){ //如果paddingTop小于0,说明HeaderView没有完全显示出来,则进入下拉刷新的状态 currentState=STATE_PULL_TO_REFRESH; tv_state.setText("下拉刷新"); iv_arrow.startAnimation(downAnim); showRefreshingProgressBar(false); //让箭头转一下 }elseif(paddingTop>=0&¤tState!=STATE_RELEASE_REFRESH){ //如果paddingTop>=0,说明HeaderView已经完全显示出来,则进入松开刷新的状态 currentState=STATE_RELEASE_REFRESH; tv_state.setText("松开刷新"); iv_arrow.startAnimation(upAnim); showRefreshingProgressBar(false); } returntrue; } break; caseMotionEvent.ACTION_UP: if(currentState==STATE_RELEASE_REFRESH){ //如果当前状态是松开刷新,并且抬起了手,则进入正在刷新状态 currentState=STATE_REFRESHING; tv_state.setText("正在刷新"); showRefreshingProgressBar(true); showHeaderView(); if(mOnRefreshingListener!=null){ mOnRefreshingListener.onRefreshing(); } }elseif(currentState==STATE_PULL_TO_REFRESH){ //如果抬起手时是下拉刷新状态,则把HeaderView完成隐藏 hideHeaderView(); } break; } returnsuper.onTouchEvent(ev); } publicvoidsetOnRefreshingListener(OnRefreshingListenermOnRefreshingListener){ this.mOnRefreshingListener=mOnRefreshingListener; } /**ListView刷新的监听器*/ publicinterfaceOnRefreshingListener{ /**当ListView可以刷新数据的时候会调用这个方法*/ voidonRefreshing(); /**当ListView可以加载更多的时候会调用这个方法*/ voidonLoadMore(); } /**联网刷新数据的操作已经完成了*/ publicvoidonRefreshComplete(){ hideHeaderView(); currentState=STATE_PULL_TO_REFRESH; showRefreshingProgressBar(false); } /**加载更多新数据的操作已经完成了*/ publicvoidonLoadmoreComplete(){ hideFooterView(); loadingMore=false; } }
方案二:listview的多种样式显示
设置listview的适配器的时候可以实现两个方法:getViewTypeCount()和getItemViewType(),前者指定条目的种类,后者返回具体的类型,这样可以根据不同的类型设计相关的样式,包括上拉加载更多,和下拉刷新,两者类似,因此这里仅仅给出加载更多的写法。具体实现如下:
1、重写getViewTypeCount()和getItemViewType(),这里包括普通的item条目和加载更多的条目,所以getViewTypeCount()返回值为2;
@Override publicintgetViewTypeCount(){ returnsuper.getViewTypeCount()+1; } @Override publicintgetItemViewType(intposition){ if(position==getCount()-1){ return0; }else{ returnaddViewType(position);//构造一个方法出来,方便子类修改,添加更多的样式 } } publicintaddViewType(intposition){ return1; }
2、在getview()中针对不同的类型添加布局:
@Override publicViewgetView(intposition,ViewconvertView,ViewGroupparent){ BaseHoldleholdle; 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{//普通视图视图,请求网络获取数据 Tdata=(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(newOvershootInterpolator(4)).start(); returnmHoldleView; }
3、具体的加载更多视图的实现
privateBaseHoldlegetLoadmoreHoldle(){ if(mLoadmoreHoldle==null){ mLoadmoreHoldle=newLoadmoreHoldle(); } returnmLoadmoreHoldle; } publicclassLoadmoreHoldleextendsBaseHoldle{ @Bind(R.id.item_loadmore_container_loading) LinearLayoutitemloadmorecontainerloading; @Bind(R.id.item_loadmore_container_retry) LinearLayoutitemloadmorecontainerretry; @Bind(R.id.item_loadmore_tv_retry) TextViewitem_loadmore_tv_retry; publicstaticfinalintLOADMORE_LODING=0; publicstaticfinalintLOADMORE_ERROR=1; publicstaticfinalintLOADMORE_NONE=2; privateintmCurretState; @Override publicvoidrefreshHoldleView(Objectdata){ itemloadmorecontainerloading.setVisibility(View.GONE); itemloadmorecontainerretry.setVisibility(View.GONE); mCurretState=(int)data; switch(mCurretState){ caseLOADMORE_LODING: itemloadmorecontainerloading.setVisibility(View.VISIBLE); break; caseLOADMORE_ERROR: itemloadmorecontainerretry.setVisibility(View.VISIBLE); break; caseLOADMORE_NONE: break; } } @Override publicViewininViewHoldle(){ Viewview=View.inflate(UiUtils.getContext(),R.layout.itemloadmore,null); ButterKnife.bind(this,view); returnview; } } //holder基类,提取公共的方法 publicabstractclassBaseHoldle<T>{ publicViewmHoldleView; publicTmdata; publicBaseHoldle(){ mHoldleView=ininViewHoldle(); mHoldleView.setTag(this); } publicvoidsetDataAndRefreshHoldleView(Tmdata){ this.mdata=mdata; refreshHoldleView(mdata); } publicabstractvoidrefreshHoldleView(Tdata); publicabstractViewininViewHoldle(); }
方案三: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 publicvoidonRefresh(){ refreshLayout.postDelayed(newRunnable(){ @Override publicvoidrun(){ //请求网络,获取数据 refreshLayout.setRefreshing(false); } },3000); }
以上所述是小编给大家介绍的Android中Listview下拉刷新和上拉加载更多的多种实现方案,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!