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下拉刷新和上拉加载更多的多种实现方案,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!