• ViewPager 异常状态之 无法切换、循环切换


            网上关于 ViewPager 的用法、源码解析已经讲的很多了。但生产环境中,我们可能会遇到各种奇怪的问题。这篇文章将会聊聊自己遇到的比较奇怪的异常情况,并讲述分析思路与源码解析。

    循环切换

    viewpager 异常

            从视频中可以看到,当切换到 4 的时候,继续向右切换却变成了1。用户就会感觉“鬼打墙了”永远在这几个数据里面循环起来。

            从复现的路径上可以看出,当切换到 4 的时候,下方的Navigation切换到了 0,但 viewPager 本身没有什么变化。

    进一步的思考,排查setCurrentItemInternal的调用位置

    1. setAdapter

    2. dataSetChanged

    3. onResotreInstanceState

    4. onTouchEvent

    5. endFakeDrag

            1 是初始化 adapter 的时候,调用的,很明显不是这里的问题;3 是恢复的时候。5 很明显也不是;出问题的地方只有 2 或者 4。4 是拖动的时候触发的,这里看起来是切换过去以后才出问题。先不查它,后面再说。

            那么 dataSetChanged 的嫌疑最大。

    1. class VerticalViewPager {
    2. void dataSetChanged() {
    3. boolean isUpdating = false;
    4. for (int i = 0; i < mItems.size(); i++) {
    5. final ItemInfo ii = mItems.get(i);
    6. final int newPos = mAdapter.getItemPosition(ii.object);
    7. if (ii.position != newPos) {
    8. if (ii.position == mCurItem) {
    9. // Our current item changed position. Follow it.
    10. newCurrItem = newPos;
    11. }
    12. ii.position = newPos;
    13. needPopulate = true;
    14. ...
    15. }
    16. if (needPopulate) {
    17. // Reset our known page widths; populate will recompute them.
    18. ...
    19. if (mSuspendOnePopulate) {
    20. // do nothing
    21. } else {
    22. setCurrentItemInternal(newCurrItem, false, true);
    23. }
    24. requestLayout();
    25. }
    26. }
    27. }
    28. class xMAdapter {
    29. override fun getItemPosition(any: Any): Int {
    30. items.forEachIndexed { index, view ->
    31. if (view == (`object` as? View)?.tag) {
    32. return index
    33. }
    34. }
    35. return POSITION_NONE
    36. }
    37. }

            上述代码中,删除了无关的代码。可以发现,这里有一个非常可能导致问题的地方,就是  final int newPos = mAdapter.getItemPosition(ii.object);,可以看到,源码中使用 tag 去mAdapter 中去寻找 position。这就导致了一个问题,当列表中存在多个相同 tag, 且下标不一样的时候,会存在 position 查找错误的可能。举例说明。

    当数据是(【】表示 items 中的数据)

    0 1 2 3 4 5 6 【7 8 9】 的时候。当前视频是 8

    当从 8 -> 9以后。

    0 1 2 3 4 5 6 7 【8 9 10】 此时新来了一批数据,触发了 loadMoreResult,接着触发 dataSetChanged。

    从代码中可以看到,此时会遍历items,然后从 mAdapter 种获取 newPos 的位置。问题来了,此时下标 1、9 的tag是一个,这时候,遍历 mAdapter 会先返回前者,也就是返回了 1。明明在 9 位置,却返回了 1。接着会执行

    setCurrentItemInternal->populate->addNewItem。从这里就全错了,数据变成了

    【0 1 2】 3 4 5 6 8 9 10。继而循环了。

    所以出现该问题的路径可能有一下两种情况

    1. 服务端同一刷下发了两个相同的视频

    2. 有人在 mAdapter 中插入或者 替换了之前已经存在的视频

    解决思路

    1.  禁止 mAdapter 中出现重复的数据,禁止 index 返回出现问题
    2. 修改 getItemPosition 的逻辑,反向遍历,优先拿后者
      1. 用户反向滑动的时候,可能会出现问题,又业务逻辑来判断是否采取这种方式解决。

    无法切换

    其实原理一样,不想写了。

  • 相关阅读:
    关于个体工商户的15个财税要点,你清楚吗?
    第四节——Vue属性绑定
    【LeetCode】Day137-寻找消失&重复数
    一步步撸脚本监控
    html5 主体标签
    微信图文如何替换成自己的二维码?
    【日志分析】Android 运营商名称显示优先级(AlphaTag/SPN)
    Win11找不到DNS地址怎么办?Win11找不到DNS无法访问网页解决方法
    Java shp 转 GeoJson
    Android App内存泄漏原理、检测及修改方案
  • 原文地址:https://blog.csdn.net/qq_37660755/article/details/133268013