自定义的ViewPagerIndicator
项目里有个地方需要用到ViewPagerIndicator,不想因为这一点小功能引入第三方库。于是就自己写了一个简单的,支持设置圆形、圆角矩形和随滑动伸缩。
代码还有很多可以优化的地方,就贴一点关键代码吧。
整体的思路是持有和ViewPager的itemCount相同数量的rectF和一个当前位置的rectF,通过给ViewPager添加一个滑动监听,在onPageScrolled()方法中更改当前位置的rectF的left和right值来实现伸缩效果。
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (!flexible) return;
if (currentItem >= dotsCount)
return;
//十分丑陋但是能实现伸缩效果的算法QAQ
if (currentItem == position) {//左划,滑动完毕会显示+1页
float v = positionOffset * 2f;//放大进度,超过一半即可以到下一页
if (v > 1) {
v = 1;
}
rectF.right = rectFs[currentItem].left + dotWidth + (dotWidth + dotMargin) * v;
if (rectF.left != rectFs[currentItem].left) {
rectF.left = rectF.left - (rectF.left - rectFs[currentItem].left) * (1 - positionOffset);
}
} else {//右划,滑动完成会显示position页
float v = 1 - positionOffset;
v = v * 2f;
if (v > 1)
v = 1;
rectF.left = rectFs[currentItem].right - dotWidth - (dotWidth + dotMargin) * v;
if (rectF.right != rectFs[currentItem].right) {
rectF.right = rectF.right + (rectFs[currentItem].right - rectF.right) * positionOffset;
}
}
invalidate();
}
问题记录
但是使用这个indicator的页面在多次刷新ViewPager数据之后出现了ANR,将日志文件导出之后发现是设置ViewPagerAdapter的时候每次绑定ViewPager都给adapter注册了一个匿名datasetObserver对象,而这个observer调用时又会给adapter添加另一个匿名的datasetObserver对象,于是就出现了以下运行逻辑:
- 初次绑定,添加了一个匿名的datasetObserver对象;
- ViewPager刷新,再次添加一个匿名的datasetObserver对象;
- ViewPager再此刷新,前两次添加的datasetObserver对象分别给adapter添加一个datasetObserver对象,此时adapter中有四个datasetObserver对象;
- 以此类推,viewpager刷新n次,adapter中的datasetObserver对象数量会指数级增长 -> ANR
虽然解决的方式也很简单,将datasetObserver对象作为成员变量保存,需要重新注册之前解除注册一次即可。往往是这种细节问题会导致一些很让人头疼的bug,特此记录一下。