Android代码实现AdapterViews和RecyclerView无限滚动
应用的一个共同的特点就是当用户欢动时自动加载更多的内容,这是通过用户滑动触发一定的阈值时发送数据请求实现的。
相同的是:信息实现滑动的效果需要定义在列表中最后一个可见项,和某些类型的阈值以便于开始在最后一项到达之前开始抓取数据,实现无限的滚动。
实现无限滚动的现象的重要之处就在于在用户滑动到最低端之前就行数据的获取,所以需要加上一个阈值来帮助实现获取数据的预期。
使用ListView和GridView实现
每个AdapterView例如ListView和GridView当用户开始进行滚动操作时候都会触发OnScrollListener.使用这个系统我们就可以定义一个基本的EndlessScrollListener,通过创造继承OnScrollListener的类来支持大多数情况下的使用。
packagecom.codepath.customadapter;
importandroid.widget.AbsListView;
/**
*CreatedbyAdministratoron2016/7/11.
*/
publicabstractclassEndlessScrollListenerimplementsAbsListView.OnScrollListener{
//在你滑动项下最少为多少时开始加载数据
privateintvisibleThreshold=5;
//已经加载数据的当前页码
privateintcurrentPage=0;
//上一次加载数据后数据库的数据量
privateintpreviousTotalItemCount=0;
//我们是否在等待最后一组数据的加载
privatebooleanloading=true;
//设置开始页的下标
privateintstartingPageIndex=0;
publicEndlessScrollListener(){
}
publicEndlessScrollListener(intvisibleThreshold){
this.visibleThreshold=visibleThreshold;
}
publicEndlessScrollListener(intvisibleThreshold,intstartingPageIndex){
this.visibleThreshold=visibleThreshold;
this.startingPageIndex=startingPageIndex;
}
//这个方法可能会在滑动调用很多次,所以在设计时要保持谨慎
//我们需要一些有用的参数来帮助我们,当我们需要加载更多数据的时候
//但是我们首先要检查是否我们在等待先前的加载结束
//onScroll()当列表或网格视图被滚动后将会调用,参数一:报告状态的视图参数二:第一个可以看见的项的下标,参数三:可见项的数量参数四:listAdapter中所有的项数
@Override
publicvoidonScroll(AbsListViewview,intfirstVisibleItem,intvisibleItemCount,inttotalItemCount){
//如果总项数为0,而且先前没有项,那么这个列表是无效的应该被设定为初始状态
if(totalItemCount<previousTotalItemCount){
this.currentPage=this.startingPageIndex;
this.previousTotalItemCount=totalItemCount;
if(totalItemCount==0){this.loading=true;}
}
//如果仍在加载中我们可以检查一下数据集合是否改变了,如果改变的话那就是已经完成了loading需要更新当前
//页数和数据总量
if(loading&&(totalItemCount>previousTotalItemCount)){
loading=false;
previousTotalItemCount=totalItemCount;
currentPage++;
}
//如果当前没有加载,我们需要检查当前是否达到了阈值,如果是的话我们需要
//加载更多的数据,执行onLoadMore
if(!loading&&(firstVisibleItem+visibleItemCount+visibleThreshold)>=totalItemCount){
loading=onLoadMore(currentPage+1,totalItemCount);
}
}
//定义实际加载数据的过程,如果数据加载完成返回false,如果正在加载返回true;
publicabstractbooleanonLoadMore(intpage,inttotalItemCount);
@Override
publicvoidonScrollStateChanged(AbsListViewview,intscrollState){
//不采取动作
}
}
要注意的是这是一个抽象的类,为了要使用这些,必须继承这个基本的类并且实现onLoadMore()方法实际的获取数据,我们在一个活动中定义一个匿名的类来继承EndlessScrollListener然后将其连接AdapterView上
publicclassMainActivityextendsActivity{
@Override
protectedvoidonCreate(BundlesavedInstance){
//向平常一样
ListViewlvItems=(ListView)findViewById(R.id.lvItens);
//将监听器绑定到上面
lvItems.setOnScrollListener(newEndlessScrollListener(){
@Override
publicbooleanonLoadMore(intpage,inttotalItemsCount){
//当新数据需要绑定到列表上的时候触发
//加载数据需要的代码AddwhatevercodeisneededtoappendnewitemstoyourAdapterView
customLoadMoreDataFromApi(page);
//orcustomLoadMoreDataFromApi(totalItemsCount);
returntrue;//数据加载中为true,不然为false;ONLYifmoredataisactuallybeingloaded;falseotherwise.
}
});
}
//加载更多的数据
publicvoidcustomLoadMoreDataFromApi(intoffset){
//这个方法通常会发起一些网络请求,然后向适配器添加更多的数据
//将偏移量数据作为参数附在请求里来获得一个数据的分页
//解析API返回的值并且获得新的对象构建适配器
}
}
现在当用户滑动并且触发阈值时会自动触发onloadMore()方法,而且监听器给予了对于页数和数据总量的访问权限。
实现RecyclerView的无限滑动
我们可以使用相同的方法来定义一个接口EndlessRecyclerViewScrollListener然后定义一个onLoadMore()的方法来进行实现。由于LayoutManager负责项的生成和滑动的管理,我们需要一个LayoutManage的实例来收集必要的信息。
实现必要的分页需要这样的步骤:
1).直接复制EndlessRecyclerViewScrollListener.java
2).调用addOnScrollListener(...)在RecyclerView中来实现无限的分页,传递EndlessRecyclerViewScrollListener的实例来实现onLoadMore方法来决定什么时候来加载新的数据
3).在onLoadMore方法中通过发送网络请求或者从源数据加载来获得更多item。
protectedvoidonCreate(BundlesavedInstanceState){
//ConfiguretheRecyclerView获得RecylerView的实例
RecyclerViewrvItems=(RecyclerView)findViewById(R.id.rvContacts);
LinearLayoutManagerlinearLayoutManager=newLinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
//Addthescrolllistener
rvItems.addOnScrollListener(newEndlessRecyclerViewScrollListener(linearLayoutManager){
@Override
publicvoidonLoadMore(intpage,inttotalItemsCount){
//Triggeredonlywhennewdataneedstobeappendedtothelist
//Addwhatevercodeisneededtoappendnewitemstothebottomofthelist
customLoadMoreDataFromApi(page);
}
});
}
//Appendmoredataintotheadapter
//Thismethodprobablysendsoutanetworkrequestandappendsnewdataitemstoyouradapter.
publicvoidcustomLoadMoreDataFromApi(intpage){
//SendanAPIrequesttoretrieveappropriatedatausingtheoffsetvalueasaparameter.
//-->DeserializeAPIresponseandthenconstructnewobjectstoappendtotheadapter
//-->Notifytheadapterofthechanges
}
}
EndlessRecyclerView
publicabstractclassEndlessRecyclerViewScrollListenerextendsRecyclerView.OnScrollListener{
//Theminimumamountofitemstohavebelowyourcurrentscrollposition
//beforeloadingmore.
privateintvisibleThreshold=5;
//Thecurrentoffsetindexofdatayouhaveloaded
privateintcurrentPage=0;
//Thetotalnumberofitemsinthedatasetafterthelastload
privateintpreviousTotalItemCount=0;
//Trueifwearestillwaitingforthelastsetofdatatoload.
privatebooleanloading=true;
//Setsthestartingpageindex
privateintstartingPageIndex=0;
RecyclerView.LayoutManagermLayoutManager;
publicEndlessRecyclerViewScrollListener(LinearLayoutManagerlayoutManager){
this.mLayoutManager=layoutManager;
}
publicEndlessRecyclerViewScrollListener(GridLayoutManagerlayoutManager){
this.mLayoutManager=layoutManager;
visibleThreshold=visibleThreshold*layoutManager.getSpanCount();
}
publicEndlessRecyclerViewScrollListener(StaggeredGridLayoutManagerlayoutManager){
this.mLayoutManager=layoutManager;
visibleThreshold=visibleThreshold*layoutManager.getSpanCount();
}
publicintgetLastVisibleItem(int[]lastVisibleItemPositions){
intmaxSize=0;
for(inti=0;i<lastVisibleItemPositions.length;i++){
if(i==0){
maxSize=lastVisibleItemPositions[i];
}
elseif(lastVisibleItemPositions[i]>maxSize){
maxSize=lastVisibleItemPositions[i];
}
}
returnmaxSize;
}
//Thishappensmanytimesasecondduringascroll,sobewaryofthecodeyouplacehere.
//Wearegivenafewusefulparameterstohelpusworkoutifweneedtoloadsomemoredata,
//butfirstwecheckifwearewaitingforthepreviousloadtofinish.
@Override
publicvoidonScrolled(RecyclerViewview,intdx,intdy){
intlastVisibleItemPosition=0;
inttotalItemCount=mLayoutManager.getItemCount();
if(mLayoutManagerinstanceofStaggeredGridLayoutManager){
int[]lastVisibleItemPositions=((StaggeredGridLayoutManager)mLayoutManager).findLastVisibleItemPositions(null);
//getmaximumelementwithinthelist
lastVisibleItemPosition=getLastVisibleItem(lastVisibleItemPositions);
}elseif(mLayoutManagerinstanceofLinearLayoutManager){
lastVisibleItemPosition=((LinearLayoutManager)mLayoutManager).findLastVisibleItemPosition();
}elseif(mLayoutManagerinstanceofGridLayoutManager){
lastVisibleItemPosition=((GridLayoutManager)mLayoutManager).findLastVisibleItemPosition();
}
//Ifthetotalitemcountiszeroandthepreviousisn't,assumethe
//listisinvalidatedandshouldberesetbacktoinitialstate
if(totalItemCount<previousTotalItemCount){
this.currentPage=this.startingPageIndex;
this.previousTotalItemCount=totalItemCount;
if(totalItemCount==0){
this.loading=true;
}
}
//Ifit'sstillloading,wechecktoseeifthedatasetcounthas
//changed,ifsoweconcludeithasfinishedloadingandupdatethecurrentpage
//numberandtotalitemcount.
if(loading&&(totalItemCount>previousTotalItemCount)){
loading=false;
previousTotalItemCount=totalItemCount;
}
//Ifitisn'tcurrentlyloading,wechecktoseeifwehavebreached
//thevisibleThresholdandneedtoreloadmoredata.
//Ifwedoneedtoreloadsomemoredata,weexecuteonLoadMoretofetchthedata.
//thresholdshouldreflecthowmanytotalcolumnstherearetoo
if(!loading&&(lastVisibleItemPosition+visibleThreshold)>totalItemCount){
currentPage++;
onLoadMore(currentPage,totalItemCount);
loading=true;
}
}
//Definestheprocessforactuallyloadingmoredatabasedonpage
publicabstractvoidonLoadMore(intpage,inttotalItemsCount);
}
注意问题:
1.对于ListView,确定将绑定监听器的步骤放在onCreate()的方法中
2.为可加可靠的进行分页,需要确定在向列表中添加新数据的时候先清理适配器中的数据
对于RecyclerView来说在通知适配器时推荐更细致的更新。
3.对于RecyclerView来说确保在清除列表中的数据的时候迅速的通知适配器内容更新了,以便于可以触发新的onScroll事件,重置自己
展示进度条
为了在底部展示进度条证明ListView正在加载。我们可以在Adapter中进行设置,我们可以定义两类,可以是进度条类型或者是文本表明达到了最底行,参考:http://guides.codepath.com/android/Endless-Scrolling-with-AdapterViews-and-RecyclerView
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。