Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FlatList blank area #66

Merged
merged 39 commits into from
Feb 1, 2022
Merged

FlatList blank area #66

merged 39 commits into from
Feb 1, 2022

Conversation

fortmarek
Copy link
Contributor

I have added a computation of blank area for FlatList on both iOS and Android.

The usage is the following:

<BlankAreaView>
   <FlatList ... />
<BlankAreaView />

BlankAreaView, a native component, will then find the relevant views in its subviews (children on Android) and do similar computations as we do in AutoLayoutView. To report the blank area, we then use the same event as we do for RecyclerFlatList - this means the blank area is also already reported to the Flipper plugin.

@fortmarek fortmarek changed the title Flat list blank area FlatList blank area Jan 26, 2022
event.putDouble("startOffset", blankOffsetTop / pixelDensity)
event.putDouble("endOffset", blankOffsetBottom / pixelDensity)
event.putDouble("blankArea", blankArea / pixelDensity)
event.putDouble("listSize", listSize / pixelDensity)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need list size in this event?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using it in the flipper plugin to trim blank spaces bigger than the listSize which skews the graph too much. We report it on iOS in AutoLayoutView, too, it might be missing there on Android.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can drop blankArea and listSize. For listSize you can set any max value like window height and blank area is just max of other two values.
The event payload size will reduce by half. Will keep the bridge free

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For listSize you can set any max value like window height and blank area is just max of other two values.

This assumes the list is as big as the window height - I'd keep it for now and remove listSize once we move to instance events. I jotted this down here.

I'll get rid of blankArea from the event.

}

fun computeBlankFromGivenOffset(): Triple<Int, Int, Int> {
val cells = ((scrollView as ViewGroup).getChildAt(0) as ViewGroup).getChildren().filter { it != null } .map { it as ViewGroup }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it != null will never be true. View children cannot be null. Correct me if I'm wrong. Both the iterations don't seem to be necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remove this check, I am getting null cannot be cast to non-null type android.view.ViewGroup so I think the the assumption of that view children cannot be null is incorrect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also need to cast the children to ViewGroup as they are by default View which does not have methods like getChildAt

val blankArea = Math.max(blankOffsetTop, blankOffsetBottom)
return Triple(blankOffsetTop, blankOffsetBottom, blankArea)
} catch (e: NoSuchElementException) {
return Triple(listSize, listSize, listSize)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an empty check above already. Can this exception ever be thrown?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible that either cells.first or cells.last will not be able to resolve any element, so yes, this can be potentially thrown, I believe.

return (blankOffsetTop, blankOffsetBottom, blankArea)
}

func scrollViewContains(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is basically the same as the one in AutoLayoutView. We could reuse it and have tests covering this logic for both lists

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's almost the same, but AutoLayoutView has renderAheadOffset. We could pass the renderAheadOffset as zero for BlankAreaView but either way, eventually, AutoLayoutView and BlankAreaView should not be part of the same module, so I don't think reusing a couple of lines of code is worth the hassle.

Comment on lines 41 to 42
let cells = scrollView.subviews.first(where: { $0 is RCTScrollContentView })?.subviews ?? []
guard !cells.isEmpty else { return (0, 0, 0) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, but I think this is easier to read

Suggested change
let cells = scrollView.subviews.first(where: { $0 is RCTScrollContentView })?.subviews ?? []
guard !cells.isEmpty else { return (0, 0, 0) }
let cells = scrollView.subviews.first(where: { $0 is RCTScrollContentView })?.subviews
guard let cells = cells, !cells.isEmpty else { return (0, 0, 0) }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think it's conceptually easier to reason about a variable of type [Element] rather than [Element]?, so I think it's better to keep it as-is.

ios/Sources/BlankAreaView.swift Outdated Show resolved Hide resolved
ios/Sources/BlankAreaView.swift Outdated Show resolved Hide resolved
@ElviraBurchik
Copy link
Contributor

I get multiple blank area offset events with753 on layout. I guess it's screen size, so it means that the screen is fully blank on open?

Screenshot 2022-01-28 at 11 29 49

ios/Sources/BlankAreaView.swift Outdated Show resolved Hide resolved
!didSet,
let scrollView = scrollView
else { return }
observation = scrollView.observe(\.contentOffset, changeHandler: { [weak self] scrollView, _ in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is KVO working consistent in this case? Shouldn't we use ScrollView delegate method instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the scrollView already has a set delegate which is not under our direct control (it's RCTScrollView). This code is asynchronous, so there might be some delays and it'd be better to find synchronous alternative. However, it should not lead to incorrect measurements since we're reading the current scrollView.contentOffset - but if you have an idea how to get a callback for each frame or change in contentOffset synchronously, let me know.

@fortmarek
Copy link
Contributor Author

I get multiple blank area offset events with753 on layout. I guess it's screen size, so it means that the screen is fully blank on open?

That's correct - I think of keeping those blank events and ignore them in the JS layer. This is a good place to compute the final TTI - I'll look into if we can get RecyclerFlatList to report these empty frames, too, as they should be there as well. Additionally, I believe it's more representable of what the user sees as they indeed see a few frames of empty screen and it'd be nice if our measurements reflected this. cc @davebcn87 @naqvitalha

@fortmarek fortmarek merged commit 5b32f90 into main Feb 1, 2022
@fortmarek fortmarek deleted the flat-list-blank-area branch February 1, 2022 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants