Method for obtaining sliding distance by Recyclerview when item height is different

  • 2021-10-24 23:37:02
  • OfStack

Preface

Recently, there was a need to calculate the RecyclerView slip distance, and the value obtained by the provided computeVerticalScrollOffset () method was not very accurate. It is based on the average height of item. If the height of item is 1 in the list, this method can be used. The problem is that my application scenario is that the height of each item is not 1, so I can only find another way.

Method 1:

Looking for the method on the net, with a variable to statistics, every time the slide of the accumulation of y axis offset. When inserting\ moving\ deleting item, you need to update totalDy manually, otherwise it will go wrong.


private int totalDy = 0;
mRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
  @Override
  public void onScrolled(RecyclerView recyclerview, int dx, int dy) {
    totalDy -= dy;
  }
}

Method 2:

Method 1 is more troublesome and has more pits. So consider overriding the computeVerticalScrollOffset () method of LinearLayoutManager. Since the native method is calculated at average height, overriding the calculation logic can achieve the desired effect.

1. Statistic the height of item shown in the list. When the layout is completed, record the height of view corresponding to item at positon position with one map.


private Map<Integer, Integer> heightMap = new HashMap<>();
int count = getChildCount();
for (int i = 0; i < count; i++) {
  View view = getChildAt(i);
  heightMap.put(i, view.getHeight());
}

2. Rewrite computeVerticalScrollOffset (), find position of the first visible item on the current screen, accumulate 0 to item height of positon through heightMap loop, and add the height of the invisible part of the first visible item. Finally, the sliding offset of the whole list is obtained.


@Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
  if (getChildCount() == 0) {
    return 0;
  }
  int firstVisiablePosition = findFirstVisibleItemPosition();
  View firstVisiableView = findViewByPosition(firstVisiablePosition);
  int offsetY = -(int) (firstVisiableView.getY());
  for (int i = 0; i < firstVisiablePosition; i++) {
    offsetY += heightMap.get(i) == null ? 0 : heightMap.get(i);
  }
  return offsetY;
}

3. Final code


public class OffsetLinearLayoutManager extends LinearLayoutManager {

  public OffsetLinearLayoutManager(Context context) {
    super(context);
  }

  private Map<Integer, Integer> heightMap = new HashMap<>();

  @Override
  public void onLayoutCompleted(RecyclerView.State state) {
    super.onLayoutCompleted(state);
    int count = getChildCount();
    for (int i = 0; i < count ; i++) {
      View view = getChildAt(i);
      heightMap.put(i, view.getHeight());
    }
  }

  @Override
  public int computeVerticalScrollOffset(RecyclerView.State state) {
    if (getChildCount() == 0) {
      return 0;
    }
    try {
      int firstVisiablePosition = findFirstVisibleItemPosition();
      View firstVisiableView = findViewByPosition(firstVisiablePosition);
      int offsetY = -(int) (firstVisiableView.getY());
      for (int i = 0; i < firstVisiablePosition; i++) {
        offsetY += heightMap.get(i) == null ? 0 : heightMap.get(i);
      }
      return offsetY;
    } catch (Exception e) {
      return 0;
    }
  }
}


mRecycler.setLayoutManager(new OffsetLinearLayoutManager(mContext));

Related articles: