in litho-widget/src/main/java/com/facebook/litho/widget/RecyclerBinder.java [2242:2369]
public void measure(
Size outSize,
int widthSpec,
int heightSpec,
@Nullable EventHandler<ReMeasureEvent> reMeasureEventHandler) {
// This is a hack to try to give a signal to applyReadyBatches whether it should even attempt
// to acquire the lock or bail and let measure schedule it as a runnable. This can go away
// once we break up the locking in measure.
// TODO(t37195892): Do not hold lock throughout measure call in RecyclerBinder
final boolean canRemeasure = reMeasureEventHandler != null;
final int scrollDirection = mLayoutInfo.getScrollDirection();
validateMeasureSpecs(widthSpec, heightSpec, canRemeasure, scrollDirection);
final boolean shouldMeasureItemForSize =
shouldMeasureItemForSize(widthSpec, heightSpec, scrollDirection, canRemeasure);
if (mHasManualEstimatedViewportCount && shouldMeasureItemForSize) {
throw new RuntimeException(
"Cannot use manual estimated viewport count when the RecyclerBinder needs an item to determine its size!");
}
mIsInMeasure.set(true);
try {
synchronized (this) {
if (mLastWidthSpec != LayoutManagerOverrideParams.UNINITIALIZED
&& !mRequiresRemeasure.get()
&& !mWrapContent) {
switch (scrollDirection) {
case VERTICAL:
if (mMeasuredSize != null
&& MeasureComparisonUtils.isMeasureSpecCompatible(
mLastWidthSpec, widthSpec, mMeasuredSize.width)) {
outSize.width = mMeasuredSize.width;
outSize.height = mWrapContent ? mMeasuredSize.height : SizeSpec.getSize(heightSpec);
return;
}
break;
default:
if (mMeasuredSize != null
&& MeasureComparisonUtils.isMeasureSpecCompatible(
mLastHeightSpec, heightSpec, mMeasuredSize.height)) {
outSize.width = mWrapContent ? mMeasuredSize.width : SizeSpec.getSize(widthSpec);
outSize.height = mMeasuredSize.height;
return;
}
}
mIsMeasured.set(false);
invalidateLayoutData();
}
// We have never measured before or the measures are not valid so we need to measure now.
mLastWidthSpec = widthSpec;
mLastHeightSpec = heightSpec;
if (!hasComputedRange()) {
final ComponentTreeHolderRangeInfo holderForRangeInfo = getHolderForRangeInfo();
if (holderForRangeInfo != null) {
initRange(
SizeSpec.getSize(widthSpec),
SizeSpec.getSize(heightSpec),
holderForRangeInfo,
scrollDirection);
}
}
final Size initialMeasuredSize =
getInitialMeasuredSize(widthSpec, heightSpec, canRemeasure);
// At this point we might still not have a range. In this situation we should return the
// best size we can detect from the size spec and update it when the first item comes in.
switch (scrollDirection) {
case OrientationHelper.VERTICAL:
if (!shouldMeasureItemForSize || mSizeForMeasure != null) {
mReMeasureEventEventHandler = mWrapContent ? reMeasureEventHandler : null;
} else {
mReMeasureEventEventHandler = reMeasureEventHandler;
mRequiresRemeasure.set(!mWrapContent);
}
break;
case OrientationHelper.HORIZONTAL:
default:
if (!shouldMeasureItemForSize || mSizeForMeasure != null) {
mReMeasureEventEventHandler =
(mHasDynamicItemHeight || mWrapContent) ? reMeasureEventHandler : null;
mRequiresRemeasure.set(mHasDynamicItemHeight);
} else {
mReMeasureEventEventHandler = reMeasureEventHandler;
mRequiresRemeasure.set(!mWrapContent);
}
break;
}
if (mWrapContent) {
final Size wrapSize = new Size();
fillListViewport(initialMeasuredSize.width, initialMeasuredSize.height, wrapSize);
outSize.width = wrapSize.width;
outSize.height = wrapSize.height;
} else {
outSize.width = initialMeasuredSize.width;
outSize.height = initialMeasuredSize.height;
}
mMeasuredSize = new Size(outSize.width, outSize.height);
mIsMeasured.set(true);
if (mComponentWarmer != null) {
mComponentWarmer.setComponentTreeHolderFactory(getComponentTreeHolderPreparer());
}
maybeFillHScrollViewport();
updateAsyncInsertOperations();
if (mEstimatedViewportCount != RecyclerView.NO_POSITION) {
computeRange(mCurrentFirstVisiblePosition, mCurrentLastVisiblePosition);
}
}
} finally {
mIsInMeasure.set(false);
if (mHasAsyncOperations) {
ensureApplyReadyBatches();
}
}
}