-
Notifications
You must be signed in to change notification settings - Fork 265
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
Conversation
event.putDouble("startOffset", blankOffsetTop / pixelDensity) | ||
event.putDouble("endOffset", blankOffsetBottom / pixelDensity) | ||
event.putDouble("blankArea", blankArea / pixelDensity) | ||
event.putDouble("listSize", listSize / pixelDensity) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
- Include RNRecyclerFlatList's test target
- Include RNRecyclerFlatList's test target
bd06b35
to
dea898b
Compare
ios/Sources/BlankAreaView.swift
Outdated
return (blankOffsetTop, blankOffsetBottom, blankArea) | ||
} | ||
|
||
func scrollViewContains( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
ios/Sources/BlankAreaView.swift
Outdated
let cells = scrollView.subviews.first(where: { $0 is RCTScrollContentView })?.subviews ?? [] | ||
guard !cells.isEmpty else { return (0, 0, 0) } |
There was a problem hiding this comment.
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
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) } |
There was a problem hiding this comment.
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.
!didSet, | ||
let scrollView = scrollView | ||
else { return } | ||
observation = scrollView.observe(\.contentOffset, changeHandler: { [weak self] scrollView, _ in |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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 |
I have added a computation of blank area for
FlatList
on both iOS and Android.The usage is the following:
BlankAreaView
, a native component, will then find the relevant views in its subviews (children on Android) and do similar computations as we do inAutoLayoutView
. To report the blank area, we then use the same event as we do forRecyclerFlatList
- this means the blank area is also already reported to the Flipper plugin.