@@ -698,17 +698,78 @@ void performIncrementalMountForVisibleBoundsChange() {
698
698
// Per ComponentTree visible area. Because BaseMountingViews can be nested and mounted
699
699
// not in "depth order", this variable cannot be static.
700
700
final Rect currentVisibleArea = new Rect ();
701
- final boolean hasNonEmptyVisibleRect = getLocalVisibleRect (currentVisibleArea );
701
+ final boolean areBoundsVisible = getLocalVisibleRect (currentVisibleArea );
702
702
703
- if (hasNonEmptyVisibleRect
703
+ if (areBoundsVisible
704
704
|| hasComponentsExcludedFromIncrementalMount (getCurrentLayoutState ())
705
705
// It might not be yet visible but animating from 0 height/width in which case we still
706
706
// need to mount them to trigger animation.
707
- || animatingRootBoundsFromZero (currentVisibleArea )) {
707
+ || animatingRootBoundsFromZero (currentVisibleArea )
708
+ || hasBecomeInvisible ()) {
708
709
mountComponent (currentVisibleArea , true );
709
710
}
710
711
}
711
712
713
+ /**
714
+ * This is used to detect an edge case of using Litho in a nested scenario. You can imagine a a
715
+ * host XML, which takes a LithoView on top.
716
+ *
717
+ * <p>As we scroll the LithoView out of the screen, we will process incremental mount correctly
718
+ * until the last visible pixel.
719
+ *
720
+ * <pre>
721
+ * |________________________| top: 0
722
+ * || ||
723
+ * || Litho View ||
724
+ * ||______________________|| bottom: 156
725
+ * | |
726
+ * | |
727
+ * | Remaining Host |
728
+ * | |
729
+ * |_______________________ |
730
+ * </pre>
731
+ *
732
+ * However, once the LithoView goes off the screen but the remaining host is still visible, the
733
+ * LithoView rect coordinates become negative:
734
+ *
735
+ * <pre>
736
+ * |________________________| top: -156
737
+ * || ||
738
+ * || Litho View ||
739
+ * ||______________________|| bottom: 0
740
+ *
741
+ * invisible
742
+ * - - - - - - - - - - - - - - - - - - - - - -
743
+ * visible
744
+ * |_______________________ |
745
+ * | |
746
+ * | Remaining Host |
747
+ * | |
748
+ * |_______________________ |
749
+ * </pre>
750
+ *
751
+ * Therefore, the rect is considered not visible, and in normal conditions we wouldn't process an
752
+ * extra pass of incremental mount.
753
+ *
754
+ * <p>We use this check to understand if in the last IM pass, the rect was visible, and that now
755
+ * is not. If that is the case, we will do an extra pass of IM to guarantee that the visibility
756
+ * outputs are processed and any onInvisible callback is delivered.
757
+ */
758
+ private boolean hasBecomeInvisible () {
759
+ ComponentsConfiguration configuration = getConfiguration ();
760
+ boolean shouldNotifyVisibleBoundsChangeWhenNestedLithoViewBecomesInvisible =
761
+ configuration != null
762
+ && configuration .shouldNotifyVisibleBoundsChangeWhenNestedLithoViewBecomesInvisible ;
763
+
764
+ return shouldNotifyVisibleBoundsChangeWhenNestedLithoViewBecomesInvisible
765
+ && mPreviousMountVisibleRectBounds .bottom >= 0
766
+ && mPreviousMountVisibleRectBounds .top >= 0
767
+ && mPreviousMountVisibleRectBounds .height () > 0
768
+ && mPreviousMountVisibleRectBounds .left >= 0
769
+ && mPreviousMountVisibleRectBounds .right >= 0
770
+ && mPreviousMountVisibleRectBounds .width () > 0 ;
771
+ }
772
+
712
773
private boolean animatingRootBoundsFromZero (Rect currentVisibleArea ) {
713
774
final TreeMountInfo mountInfo = getMountInfo ();
714
775
final boolean hasMounted = mountInfo != null && mountInfo .hasMounted ;
0 commit comments