平方X 发表于 2017-11-2 12:27:14

RecyclerView Item 设为不可见后无法刷新



无法下拉刷新,想起以前添加 decoration 以后,就可以正常刷新了。
试了一下果然,这一次寻找一下原因,最后发现还是因为隐藏某个 item 后造成的,就不应该隐藏。

调试后发现
```

com.aspsine.swipetoloadlayout.SwipeToLoadLayout#onInterceptTouchEvent
中调用的 onCheckCanRefresh 始终返回 false,也就是不可以刷新。
com.aspsine.swipetoloadlayout.SwipeToLoadLayout#onCheckCanRefresh
com.aspsine.swipetoloadlayout.SwipeToLoadLayout#canChildScrollUp 始终返回 true
com.aspsine.swipetoloadlayout.SwipeToLoadLayout2#canChildScrollUp 重写的
com.aspsine.swipetoloadlayout.SwipeToLoadLayout2#canViewScrollUp
发现在计算 recyclerView 时
//⑥
android.support.v4.view.ViewCompat#canScrollVertically(view,-1) 返回 true
android.view.View#canScrollVertically
    /**
   * Check if this view can be scrolled vertically in a certain direction.
   *
   * @param direction Negative to check scrolling up, positive to check scrolling down.
   * @return true if this view can be scrolled in the specified direction, false otherwise.
   */
    public boolean canScrollVertically(int direction) {
      //④
      final int offset = computeVerticalScrollOffset();
      final int range = computeVerticalScrollRange() - computeVerticalScrollExtent();
      if (range == 0) return false;
      if (direction < 0) {
            //⑤
            return offset > 0;
      } else {
            return offset < range - 1;
      }
    }
看 recyclerView 是如何计算 computeVerticalScrollOffset() 的
android.support.v7.widget.RecyclerView#computeVerticalScrollOffset
转给 LayoutManager
android.support.v7.widget.LinearLayoutManager#computeVerticalScrollOffset
android.support.v7.widget.LinearLayoutManager#computeScrollOffset
    private int computeScrollOffset(RecyclerView.State state) {
      if (getChildCount() == 0) {
            return 0;
      }
      ensureLayoutState();
      return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper,
                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
                this, mSmoothScrollbarEnabled, mShouldReverseLayout);
    }
计算第一个可见 child
android.support.v7.widget.LinearLayoutManager#findFirstVisibleChildClosestToStart
android.support.v7.widget.LinearLayoutManager#findOneVisibleChild

    View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible,
            boolean acceptPartiallyVisible) {
      ensureLayoutState();
      final int start = mOrientationHelper.getStartAfterPadding();
      final int end = mOrientationHelper.getEndAfterPadding();
      final int next = toIndex > fromIndex ? 1 : -1;
      View partiallyVisible = null;
      for (int i = fromIndex; i != toIndex; i+=next) {
            final View child = getChildAt(i);
            final int childStart = mOrientationHelper.getDecoratedStart(child);
            // ①
            final int childEnd = mOrientationHelper.getDecoratedEnd(child);
            if (childStart < end && childEnd > start) {
                if (completelyVisible) {
                  if (childStart >= start && childEnd <= end) {
                        return child;
                  } else if (acceptPartiallyVisible && partiallyVisible == null) {
                        partiallyVisible = child;
                  }
                } else {
                  return child;
                }
            }
      }
      return partiallyVisible;
    }
android.support.v7.widget.OrientationHelper#getDecoratedEnd

android.support.v7.widget.ScrollbarHelper#computeScrollOffset

    static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
            View startChild, View endChild, RecyclerView.LayoutManager lm,
            boolean smoothScrollbarEnabled, boolean reverseLayout) {
      if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
                endChild == null) {
            return 0;
      }
      //②
      final int minPosition = Math.min(lm.getPosition(startChild),
                lm.getPosition(endChild));
      final int maxPosition = Math.max(lm.getPosition(startChild),
                lm.getPosition(endChild));
      //③
      final int itemsBefore = reverseLayout
                ? Math.max(0, state.getItemCount() - maxPosition - 1)
                : Math.max(0, minPosition);
      if (!smoothScrollbarEnabled) {
            return itemsBefore;
      }
      final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild) -
                orientation.getDecoratedStart(startChild));
      final int itemRange = Math.abs(lm.getPosition(startChild) -
                lm.getPosition(endChild)) + 1;
      final float avgSizePerRow = (float) laidOutArea / itemRange;

      return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
                - orientation.getDecoratedStart(startChild)));
    }


设置了 decoration ,使得①中第0个隐藏的 headerView 可见,②③得出的 itemsBefore 为0,返回到④为0,⑤返回 false,到⑥返回 false 可以刷新。
示设置 decoration , 使得①中第0个隐藏的 headerView 不可见,②③得出的 itemsBefore 为1,返回到④有值,⑤返回 true,到⑥返回 false 不可以刷新。
```

到此处,就和之前研初过的 item 设置为 GONE 时仍占用高度的问题连起来了。
要想让 item 设为 GONE,应该在外面包裹一层 ViewGroup,然后手动设置为 GONE
但设置为 GONE,又会导致计算 itemsBefore 时,认为前面还有一个item。




页: [1]
查看完整版本: RecyclerView Item 设为不可见后无法刷新