最近在做项目的时候,使用xrecyclerview,测试报了一个bug,说在有的手机上能用上拉加载更多,有的不能加载更多,本来打算立刻打开谷歌找一找有没有人碰到过相似的问题,但是突然想到,以往出现bug,都是去谷歌,每次虽然能解决问题,但是也不过是多知道了一个api,多了解了某个控件的特性,对自己的能力并没有很好地提升,最好还是自己思考想办法解决这个问题,下面就是关于解决这个bug的一些思考。
一、确定问题
首先确定一下问题到底是什么,测试说xrecyclerview(一个github上的三方库,不知道的可以去github上找找,有细节上的bug整体来说很好用)的上拉加载更多功能在有的手机上可以加载更多,有的手机上不能加载更多,这说明这个功能是没有问题的,因为有手机可以实现,难道是手机的问题?拿来测试说的不能上拉加载更多的手机,连上adb,查看一下日志,没有很多信息,就是onLoadMore()接口方法没有被调用,打开crecyclerview源码,找到哪里调用了这个接口,如下:
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
if (state == RecyclerView.SCROLL_STATE_IDLE && mLoadingListener != null && !isLoadingData && loadingMoreEnabled) {
LayoutManager layoutManager = getLayoutManager();
int lastVisibleItemPosition;
if (layoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);
lastVisibleItemPosition = findMax(into);
} else {
lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
}
if (layoutManager.getChildCount() > 0
&& lastVisibleItemPosition >= layoutManager.getItemCount() - 1 && layoutManager.getItemCount() > layoutManager.getChildCount() && !isNoMore && mRefreshHeader.getState() < ArrowRefreshHeader.STATE_REFRESHING) {
isLoadingData = true;
if (mFootView instanceof LoadingMoreFooter) {
((LoadingMoreFooter) mFootView).setState(LoadingMoreFooter.STATE_LOADING);
} else {
mFootView.setVisibility(View.VISIBLE);
}
mLoadingListener.onLoadMore();
}
}
}
通过源码可以看出来,那个一长串的条件就是影响上拉加载更多接口onLoadMore能不能被调用的关键。
那么现在确定了影响条件的关键,下一步就是想办法筛选这些条件,看看到底是谁影响了代码。
二、确定影响条件
因为是使用依赖下载的三方库,所以并不能直接修改源码,或者打印断点调试,从github上下载源码运行编译的时间又比较长,所以我选择了一个比较懒的方法,写一个TestXRecyclerview继承xrecyclerview,并重写onScrollStateChanged(int state)方法,让他运行我自己的方法,这里需要注意的是,一般情况下,还是用下载源码打断点调试的方法比较好,但是这个问题涉及到的类和方法只有一个,所以用了这样的方式。
具体TestXRecyclerview类代码如下:
public class TestXRecyclerView extends XRecyclerView {
public TestXRecyclerView(Context context) {
super(context);
}
public TestXRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TestXRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onScrollStateChanged(int state) {
// super.onScrollStateChanged(state);
if (state == RecyclerView.SCROLL_STATE_IDLE) {
Log.i("log" , "==是否调用加载更多方法1==");
LayoutManager layoutManager = getLayoutManager();
int lastVisibleItemPosition;
if (layoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);
lastVisibleItemPosition = 20;
} else {
lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
}
if (layoutManager.getChildCount() > 0
&& lastVisibleItemPosition >= layoutManager.getItemCount() - 1 && layoutManager.getItemCount() > layoutManager.getChildCount()) {
Log.i("log" , "==是否调用加载更多方法2==");
}
}
}
}
首先是把super.onScrollStateChanged(state);方法注释掉,然后复制源码里面的onScrollStateChanged(int state) 方法出来,把一些无关的条件去掉(这里需要自己判断是否有关),最终剩下以上代码,运行,并没有调用自己写的log:方法2,说明layoutManager.getChildCount() > 0 , lastVisibleItemPosition >= layoutManager.getItemCount() - 1 , layoutManager.getItemCount() > layoutManager.getChildCount() 这三个条件中有一个或者几个返回了false。这里先看layoutManager.getChildCount() > 0,这个条件肯定不是false,因为数据都显示出来了,那就是剩下的两个条件,其实到这里我已经突然想到了,有可能是item的总高度不够导致上拉加载更多不能触发,因为我们分页机制是一页返回十条,现在一页里面已经显示了九条多的数据,不同手机的高度不一样,这就是为什么有的手机可以有的手机不可以,不过既然已经到了这里,还是要完整的解决问题才行
想解决上面的问题,就要明白layoutManager.getItemCount()和 layoutManager.getChildCount()是什么意思,顺手开始谷歌,感觉不对,说好的看源码呢?于是回去,ctrl点开getChildCount()方法源码
/**
* Return the current number of child views attached to the parent RecyclerView.
* This does not include child views that were temporarily detached and/or scrapped.
*
* @return Number of attached children
*/
public int getChildCount() {
return mChildHelper != null ? mChildHelper.getChildCount() : 0;
}
嗯。。。再点开mChildHelper.getChildCount()
int getChildCount() {
return mCallback.getChildCount() - mHiddenViews.size();
}
看英文意思,应该是所有item的数量,减去没有显示在页面上的item的数量,那getChildCount()就是显示在页面上的item的数量咯。那不出意外,layoutManager.getItemCount()应该就是所有item的数量,很好,我已经提起兴趣了,点开源码:
public int getItemCount() {
final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
return a != null ? a.getItemCount() : 0;
}
猜对啦,现在基本上能明白,lastVisibleItemPosition >= layoutManager.getItemCount() - 1,layoutManager.getItemCount() > layoutManager.getChildCount() ,这两句话都是同一个意思能看到的item数量,要比所有的item的数量大,不然就不触发上拉加载更多,想一想,分页返回十条数据,一页就显示了九条,在不同手机上因为分辨率的不同,有的能看到九条,有的能看到十条,找到了原因所在,问题就很好解决了,给item增加一些高度,或者让后台每条数据返回多一些就可以啦。
三、总结
一般情况出现bug,都会去谷歌或者stackoverflow,这次自己找,才发现,很多bug靠自己就能解决,并且这种明白原理,不断探索着寻找问题所在的感觉真的很爽,在这个过程中,有以下几点收获:
1、有的源码比较长,可以根据代码块分割出功能,剔除没影响的功能
2、不想下载源码,可以通过继承重写的方法,打印代码的数据,但是这种只适合不太复杂的三方,当然你可以继续发散,比如看到源码里面比较好的方法,抽取出来,用在自己的代码里面。
3、先思考,再动手
以上,bug解决,新技能get,感觉有点上瘾呢。
网友评论