docs: @Override
      public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {
          // reach the end of this layout
          if (isOutOfRange(layoutState.getCurrentPosition())) {
              return;
          }
  
          boolean isStartLine = false, isEndLine = false;
          boolean isSecondStartLine = false, isSecondEndLine = false;
          final int currentPosition = layoutState.getCurrentPosition();
          GridRangeStyle rangeStyle = mRangeStyle.findRangeStyleByPosition(currentPosition);
  
          final int itemDirection = layoutState.getItemDirection();
          final boolean layingOutInPrimaryDirection =
              itemDirection == LayoutStateWrapper.ITEM_DIRECTION_TAIL;
  
          OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();
  
          final boolean layoutInVertical = helper.getOrientation() == VERTICAL;
  
          if (layoutInVertical) {
              mTotalSize = helper.getContentWidth() - helper.getPaddingRight() - helper.getPaddingLeft() - rangeStyle
                  .getFamilyHorizontalMargin() - rangeStyle.getFamilyHorizontalPadding();
              rangeStyle.mSizePerSpan = (int) ((mTotalSize - (
                  rangeStyle.mSpanCount - 1) * rangeStyle.mHGap) * 1.0f / rangeStyle.mSpanCount + 0.5f);
          } else {
              mTotalSize = helper.getContentHeight() - helper.getPaddingBottom() - helper.getPaddingTop() - rangeStyle
                  .getFamilyVerticalMargin() - rangeStyle.getFamilyVerticalPadding();
              rangeStyle.mSizePerSpan = (int) ((mTotalSize - (
                  rangeStyle.mSpanCount - 1) * rangeStyle.mVGap) * 1.0f / rangeStyle.mSpanCount + 0.5f);
          }
  
          int count = 0;
          int consumedSpanCount = 0;
          int remainingSpan = rangeStyle.mSpanCount;
  
          rangeStyle.ensureSpanCount();
  
          if (!layingOutInPrimaryDirection) {
              // fill the remaining spacing this row
              int itemSpanIndex = getSpanIndex(rangeStyle.mSpanSizeLookup, rangeStyle.mSpanCount, recycler, state, layoutState.getCurrentPosition());
              int itemSpanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, layoutState.getCurrentPosition());
  
  
              remainingSpan = itemSpanIndex + itemSpanSize;
  
              // should find the last element of this row
              if (itemSpanIndex != rangeStyle.mSpanCount - 1) {
                  int index = layoutState.getCurrentPosition();
                  int revRemainingSpan = rangeStyle.mSpanCount - remainingSpan;
                  while (count < rangeStyle.mSpanCount && revRemainingSpan > 0) {
                      // go reverse direction to find views fill current row
                      index -= itemDirection;
                      if (rangeStyle.isOutOfRange(index)) {
                          break;
                      }
                      final int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, index);
                      if (spanSize > rangeStyle.mSpanCount) {
                          throw new IllegalArgumentException("Item at position " + index + " requires " +
                              spanSize + " spans but RangeGridLayoutHelper has only " + rangeStyle.mSpanCount
                              + " spans.");
                      }
  
                      View view = layoutState.retrieve(recycler, index);
                      if (view == null) {
                          break;
                      }
  
                      if (!isStartLine) {
                          isStartLine = helper.getReverseLayout() ? index == mRangeStyle.getRange().getUpper().intValue()
                              : index == mRangeStyle.getRange().getLower().intValue();
                      }
  
  
                      if (!isEndLine) {
                          isEndLine = helper.getReverseLayout() ? index == mRangeStyle.getRange().getLower().intValue()
                              : index == mRangeStyle.getRange().getUpper().intValue();
                      }
  
  
                      revRemainingSpan -= spanSize;
                      if (revRemainingSpan < 0) {
                          break;
                      }
  
  
                      consumedSpanCount += spanSize;
                      rangeStyle.mSet[count] = view;
                      count++;
                  }
  
                  if (count > 0) {
                      // reverse array
                      int s = 0, e = count - 1;
                      while (s < e) {
                          View temp = rangeStyle.mSet[s];
                          rangeStyle.mSet[s] = rangeStyle.mSet[e];
                          rangeStyle.mSet[e] = temp;
                          s++;
                          e--;
                      }
                  }
              }
          }
  
          while (count < rangeStyle.mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
              int pos = layoutState.getCurrentPosition();
              if (rangeStyle.isOutOfRange(pos)) {
                  if (DEBUG) {
                      Log.d(TAG, "pos [" + pos + "] is out of range");
                  }
                  break;
              }
  
              final int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, pos);
              if (spanSize > rangeStyle.mSpanCount) {
                  throw new IllegalArgumentException("Item at position " + pos + " requires " +
                      spanSize + " spans but GridLayoutManager has only " + rangeStyle.mSpanCount
                      + " spans.");
              }
              remainingSpan -= spanSize;
              if (remainingSpan < 0) {
                  break; // item did not fit into this row or column
              }
  
              View view = layoutState.next(recycler);
              if (view == null) {
                  break;
              }
  
              if (!isStartLine) {
                  isStartLine = helper.getReverseLayout() ? pos == mRangeStyle.getRange().getUpper().intValue()
                      : pos == mRangeStyle.getRange().getLower().intValue();
              }
              if (!isSecondStartLine) {
                  if (!rangeStyle.equals(mRangeStyle)) {
                      if (mLayoutWithAnchor) {
                          pos = layoutState.getCurrentPosition();
                      }
                      isSecondStartLine = helper.getReverseLayout() ? pos == rangeStyle.getRange().getUpper()
                          .intValue() : pos == rangeStyle.getRange().getLower().intValue();
                  }
              }
  
              if (!isEndLine) {
                  isEndLine = helper.getReverseLayout() ? pos == mRangeStyle.getRange().getLower().intValue()
                      : pos == mRangeStyle.getRange().getUpper().intValue();
              }
  
              if (!isSecondEndLine) {
                  if (!rangeStyle.equals(mRangeStyle)) {
                      if (mLayoutWithAnchor) {
                          pos = layoutState.getCurrentPosition();
                      }
                      isSecondEndLine = helper.getReverseLayout() ? pos == rangeStyle.getRange().getLower()
                          .intValue() : pos == rangeStyle.getRange().getUpper().intValue();
                  }
              }
  
              consumedSpanCount += spanSize;
              rangeStyle.mSet[count] = view;
              count++;
          }
  
  
          if (count == 0) {
              return;
          }
  
          int maxSize = 0;
  
  
          // we should assign spans before item decor offsets are calculated
          assignSpans(rangeStyle, recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection, helper);
  
          if (remainingSpan > 0 && (count == consumedSpanCount) && rangeStyle.mIsAutoExpand) {
              //autoExpand only support when each cell occupy one span.
              if (layoutInVertical) {
                  rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mHGap) / count;
              } else {
                  rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mVGap) / count;
              }
          } else if (!layingOutInPrimaryDirection && remainingSpan == 0 && (count == consumedSpanCount) && rangeStyle.mIsAutoExpand) {
              //autoExpand only support when each cell occupy one span.
              if (layoutInVertical) {
                  rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mHGap) / count;
              } else {
                  rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mVGap) / count;
              }
          }
  
  
          boolean weighted = false;
          if (rangeStyle.mWeights != null && rangeStyle.mWeights.length > 0) {
              weighted = true;
              int totalSpace;
              if (layoutInVertical) {
                  totalSpace = mTotalSize - (count - 1) * rangeStyle.mHGap;
              } else {
                  totalSpace = mTotalSize - (count - 1) * rangeStyle.mVGap;
              }
  
              // calculate width with weight in percentage
  
              int eqCnt = 0, remainingSpace = totalSpace;
              int colCnt = (remainingSpan > 0 && rangeStyle.mIsAutoExpand) ? count : rangeStyle.mSpanCount;
              for (int i = 0; i < colCnt; i++) {
                  if (i < rangeStyle.mWeights.length && !Float.isNaN(rangeStyle.mWeights[i]) && rangeStyle.mWeights[i] >= 0) {
                      float weight = rangeStyle.mWeights[i];
                      rangeStyle.mSpanCols[i] = (int) (weight * 1.0f / 100 * totalSpace + 0.5f);
                      remainingSpace -= rangeStyle.mSpanCols[i];
                  } else {
                      eqCnt++;
                      rangeStyle.mSpanCols[i] = -1;
                  }
              }
  
              if (eqCnt > 0) {
                  int eqLength = remainingSpace / eqCnt;
                  for (int i = 0; i < colCnt; i++) {
                      if (rangeStyle.mSpanCols[i] < 0) {
                          rangeStyle.mSpanCols[i] = eqLength;
                      }
                  }
              }
          }
  
  
          for (int i = 0; i < count; i++) {
              View view = rangeStyle.mSet[i];
              helper.addChildView(layoutState, view, layingOutInPrimaryDirection ? -1 : 0);
  
              int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, helper.getPosition(view)), spec;
              if (weighted) {
                  final int index = rangeStyle.mSpanIndices[i];
                  int spanLength = 0;
                  for (int j = 0; j < spanSize; j++) {
                      spanLength += rangeStyle.mSpanCols[j + index];
                  }
  
                  spec = View.MeasureSpec.makeMeasureSpec(Math.max(0, spanLength), View.MeasureSpec.EXACTLY);
              } else {
                  spec = View.MeasureSpec.makeMeasureSpec(rangeStyle.mSizePerSpan * spanSize +
                          Math.max(0, spanSize - 1) * (layoutInVertical ? rangeStyle.mHGap : rangeStyle.mVGap),
                      View.MeasureSpec.EXACTLY);
              }
              final LayoutParams lp = (LayoutParams) view.getLayoutParams();
              if (helper.getOrientation() == VERTICAL) {
                  helper.measureChildWithMargins(view, spec, getMainDirSpec(rangeStyle, lp.height, mTotalSize,
                      View.MeasureSpec.getSize(spec), lp.mAspectRatio));
              } else {
                  helper.measureChildWithMargins(view,
                      getMainDirSpec(rangeStyle, lp.width, mTotalSize, View.MeasureSpec.getSize(spec),
                          lp.mAspectRatio), View.MeasureSpec.getSize(spec));
              }
              final int size = orientationHelper.getDecoratedMeasurement(view);
              if (size > maxSize) {
                  maxSize = size;
              }
          }
  
          // views that did not measure the maxSize has to be re-measured
          final int maxMeasureSpec = getMainDirSpec(rangeStyle, maxSize, mTotalSize, 0, Float.NaN);
          for (int i = 0; i < count; i++) {
              final View view = rangeStyle.mSet[i];
              if (orientationHelper.getDecoratedMeasurement(view) != maxSize) {
                  int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, helper.getPosition(view)), spec;
                  if (weighted) {
                      final int index = rangeStyle.mSpanIndices[i];
                      int spanLength = 0;
                      for (int j = 0; j < spanSize; j++) {
                          spanLength += rangeStyle.mSpanCols[j + index];
                      }
  
                      spec = View.MeasureSpec.makeMeasureSpec(Math.max(0, spanLength), View.MeasureSpec.EXACTLY);
                  } else {
                      spec = View.MeasureSpec.makeMeasureSpec(rangeStyle.mSizePerSpan * spanSize +
                              Math.max(0, spanSize - 1) * (layoutInVertical ? rangeStyle.mHGap : rangeStyle.mVGap),
                          View.MeasureSpec.EXACTLY);
                  }
  
                  if (helper.getOrientation() == VERTICAL) {
                      helper.measureChildWithMargins(view, spec, maxMeasureSpec);
                  } else {
                      helper.measureChildWithMargins(view, maxMeasureSpec, spec);
                  }
              }
          }
  
          int startSpace = 0, endSpace = 0;
  
          int secondStartSpace = 0, secondEndSpace = 0;
          boolean isLayoutEnd = layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_END;
          final boolean isOverLapMargin = helper.isEnableMarginOverLap();
  
          if (isStartLine) {
              startSpace = computeStartSpace(helper, layoutInVertical, isLayoutEnd, isOverLapMargin);
          }
          if (isSecondStartLine) {
              secondStartSpace = (layoutInVertical ? rangeStyle.getMarginTop() + rangeStyle.getPaddingTop()
                  : rangeStyle.getMarginLeft() + rangeStyle.getPaddingLeft());
          }
  
          if (isEndLine) {
              endSpace = layoutInVertical ? mRangeStyle.getMarginBottom() + mRangeStyle.getPaddingBottom()
                  : mRangeStyle.getMarginRight() + mRangeStyle.getPaddingRight();
          }
          if (isSecondEndLine) {
              secondEndSpace = (layoutInVertical ? rangeStyle.getMarginBottom() + rangeStyle.getPaddingBottom()
                  : rangeStyle.getMarginRight() + rangeStyle.getPaddingRight());
          }
  
  
          result.mConsumed = maxSize + startSpace + endSpace + secondStartSpace + secondEndSpace;
  
          final boolean layoutStart = layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START;
          int consumedGap = 0;
          if (!mLayoutWithAnchor) {
              if (!layoutStart) {
                  if (!isStartLine) {
                      if (isSecondStartLine) {
                          consumedGap = (layoutInVertical ? rangeStyle.mParent.mVGap : rangeStyle.mParent.mHGap);
                          if (DEBUG) {
                              Log.d(TAG, "⬇ " + currentPosition + " 1 " + consumedGap + " gap");
                          }
                      } else {
                          consumedGap = (layoutInVertical ? rangeStyle.mVGap : rangeStyle.mHGap);
                          if (DEBUG) {
                              Log.d(TAG, "⬇ " + currentPosition + " 2 " + consumedGap + " gap");
                          }
                      }
                  }
              } else {
                  if (!isEndLine) {
                      if (isSecondEndLine) {
                          consumedGap = (layoutInVertical ? rangeStyle.mParent.mVGap : rangeStyle.mParent.mHGap);
                          if (DEBUG) {
                              Log.d(TAG, "⬆ " + currentPosition + " 3 " + consumedGap + " gap");
                          }
                      } else {
                          consumedGap = (layoutInVertical ? rangeStyle.mVGap : rangeStyle.mHGap);
                          if (DEBUG) {
                              Log.d(TAG, "⬆ " + currentPosition + " 4 " + consumedGap + " gap");
                          }
                      }
                  }
              }
          }
          result.mConsumed += consumedGap;
  
          if (result.mConsumed <= 0) {
              result.mConsumed = 0;
          }
  
          int lastUnconsumedSpace = 0;
          /** layoutView() may be triggered by layoutManager's scrollInternalBy() or onFocusSearchFailed() or onLayoutChildren()
           *
           * In case of scrollInternalBy() or onFocusSearchFailed(), layoutState.isRefreshLayout() == false, and layoutState.mOffset = ChildClosestToExpose + alignOffset,
           * see {@link com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx#updateLayoutStateExpose(int, int, boolean, State)},
           * this means last line's layout padding or margin is not really consumed, so considering it before layout new line.
           *
           * In case of onLayoutChildren(), layoutState.isRefreshLayout() == true, and layoutState.mOffset = anchorInfo.mCoordinate = anchorChild.start + alignOffset,
           * see {@link com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx#updateAnchorInfoForLayoutExpose(State, AnchorInfo)},
           * this means last line's layout padding or margin is consumed.
           **/
          if (!layoutState.isRefreshLayout()) {
              if (layoutStart) {
                  int lastLinePosition = currentPosition + 1;
                  if (!isOutOfRange(lastLinePosition)) {
                      RangeStyle<GridRangeStyle> neighbourRange = mRangeStyle.findRangeStyleByPosition(lastLinePosition);
                      if (neighbourRange.isFirstPosition(lastLinePosition)) {
                          lastUnconsumedSpace = layoutInVertical ? neighbourRange.getMarginTop() + neighbourRange.getPaddingTop()
                              : neighbourRange.getMarginLeft() + neighbourRange.getPaddingLeft();
                          if (DEBUG) {
                              Log.d(TAG, "⬆ " + currentPosition + " 1 " + lastUnconsumedSpace + " last");
                          }
                      }
                  }
              } else {
                  int lastLinePosition = currentPosition - 1;
                  if (!isOutOfRange(lastLinePosition)) {
                      RangeStyle<GridRangeStyle> neighbourRange = mRangeStyle.findRangeStyleByPosition(lastLinePosition);
                      if (neighbourRange.isLastPosition(lastLinePosition)) {
                          lastUnconsumedSpace = layoutInVertical ? neighbourRange.getMarginBottom() + neighbourRange.getPaddingBottom()
                              : neighbourRange.getMarginRight() + neighbourRange.getPaddingRight();
                          if (DEBUG) {
                              Log.d(TAG, "⬇ " + currentPosition + " 2 " + lastUnconsumedSpace + " last");
                          }
                      }
                  }
              }
          }
  
          if (DEBUG) {
              Log.d(TAG,
                  (layoutStart ? "⬆ " : "⬇ ") + currentPosition + " consumed " + result.mConsumed + " startSpace " + startSpace + " endSpace "
                      + endSpace + " secondStartSpace " + secondStartSpace + " secondEndSpace " + secondEndSpace + " lastUnconsumedSpace " + lastUnconsumedSpace);
          }
  
          int left = 0, right = 0, top = 0, bottom = 0;
          if (layoutInVertical) {
              if (layoutStart) {
                  bottom = layoutState.getOffset() - endSpace - secondEndSpace - (consumedGap) - lastUnconsumedSpace;
                  top = bottom - maxSize;
              } else {
                  top = layoutState.getOffset() + startSpace + secondStartSpace + (consumedGap) + lastUnconsumedSpace;
                  bottom = top + maxSize;
              }
          } else {
              if (layoutStart) {
                  right = layoutState.getOffset() - endSpace - (consumedGap) - lastUnconsumedSpace;
                  left = right - maxSize;
              } else {
                  left = layoutState.getOffset() + startSpace + (consumedGap) + lastUnconsumedSpace;
                  right = left + maxSize;
              }
          }
  
          for (int i = 0; i < count; i++) {
              View view = rangeStyle.mSet[i];
              final int index = rangeStyle.mSpanIndices[i];
  
              LayoutParams params = (LayoutParams) view.getLayoutParams();
              if (layoutInVertical) {
                  if (weighted) {
                      left = helper.getPaddingLeft() + rangeStyle.getFamilyMarginLeft() + rangeStyle.getFamilyPaddingLeft();
                      for (int j = 0; j < index; j++) {
                          left += rangeStyle.mSpanCols[j] + rangeStyle.mHGap;
                      }
                  } else {
                      left = helper.getPaddingLeft() + rangeStyle.getFamilyMarginLeft() + rangeStyle
                          .getFamilyPaddingLeft() + rangeStyle.mSizePerSpan * index + index * rangeStyle.mHGap;
                  }
  
                  right = left + orientationHelper.getDecoratedMeasurementInOther(view);
              } else {
  
                  if (weighted) {
                      top = helper.getPaddingTop() + rangeStyle.getFamilyMarginTop() + rangeStyle.getFamilyPaddingTop();
                      for (int j = 0; j < index; j++) {
                          top += rangeStyle.mSpanCols[j] + rangeStyle.mVGap;
                      }
                  } else {
                      top = helper.getPaddingTop() + rangeStyle.getFamilyMarginTop() + rangeStyle.getFamilyPaddingTop()
                          + rangeStyle.mSizePerSpan * index + index * rangeStyle.mVGap;
                  }
  
                  bottom = top + orientationHelper.getDecoratedMeasurementInOther(view);
              }
  
              if (DEBUG) {
                  Log.d(TAG, "layout item in position: " + params.getViewPosition() + " with text with SpanIndex: " + index + " into (" +
                      left + ", " + top + ", " + right + ", " + bottom + " )");
              }
  
              // We calculate everything with View's bounding box (which includes decor and margins)
              // To calculate correct layout position, we subtract margins.
              rangeStyle.layoutChild(view, left, top, right, bottom, helper, false);
  
              // Consume the available space if the view is not removed OR changed
              if (params.isItemRemoved() || params.isItemChanged()) {
                  result.mIgnoreConsumed = true;
              }
  
              result.mFocusable |= view.isFocusable();
          }
  
  
          mLayoutWithAnchor = false;
          Arrays.fill(rangeStyle.mSet, null);
          Arrays.fill(rangeStyle.mSpanIndices, 0);
          Arrays.fill(rangeStyle.mSpanCols, 0);
      }