e7005f7f54
Summary: public I was looking into the missing panels at the bottom of the <ListView> - Grid Layout example, and found that it was caused by several problems, some in the example and some in ListView itself. The first problem seemed to be a bug in the `_getDistanceFromEnd()` method, which calculates whether the ListView needs to load more content based on the distance of the visible content from the bottom of the scrollview. This was previously using the function Math.max(scrollProperties.contentLength, scrollProperties.visibleLength) - scrollProperties.visibleLength - scrollProperties.offset to calculate the amount the user could scroll before they run out of content. This sort-of works in most cases because `scrollProperties.contentLength` is usually longer than `scrollProperties.visibleLength`, so this would generally evaluate to scrollProperties.contentLength - scrollProperties.visibleLength - scrollProperties.offset which meant that it would be positive as long as there was content still to be displayed offscreen, and negative when you reached the end of the content. This logic breaks down if `contentLength` is less than `visibleLength`, however. For example, if you have 300pts of content loaded, and your scrollView is 500pts tall, and your scroll position is zero, this evaluates to Math.max(300, 500) - 500 - 0 = 0 In other words, the algorithm is saying that you have zero pts of scroll content remaining before you need to reload. But actually, the bottom 200pts of the screen are empty, so you're really 200pts in debt, and need to load extra rows to fill that space. The correct algorithm is simply to get rid of the `Math.max` and just use scrollProperties.contentLength - scrollProperties.visibleLength - scrollProperties.offset I originally thought that this was the cause of the gap, but it isn't, because ListView has `DEFAULT_SCROLL_RENDER_AHEAD = 1000`, which means that it tries to load at least 1000pts more content than is currently visible, to avoid gaps. This masked the bug, so in practice it wasn't causing an issue. The next problem I found was that there is an implict assumption in ListView that the first page of content you load is sufficient to cover the screen, or rather, that the first _ second page is sufficient. The constants `DEFAULT_INITIAL_ROWS = 10` and `DEFAULT_PAGE_SIZE = 1`, mean that when the ListView first loads, the following happens: 1. It loads 10 rows of content. 2. It checks if `_getDistanceFromEnd() < DEFAULT_SCROLL_RENDER_AHEAD` (1000). 3. If it is, it loads another `DEFAULT_PAGE_SIZE` rows of content, then stops. In the case of the ListView Grid Layout example, this meant that it first loaded 10 cells, then loaded another 1, for a total of 11. The problem was that going from 10 to 11 cells isn't sufficient to fill the visible scroll area, and it doesn't change the `contentSize` (since the cells wrap onto the same line), and since ListView doesn't try to load any more until the `contentSize` or `scrollOffset ` changes, it stops loading new rows at that point. I tried fixing this by calling `_renderMoreRowsIfNeeded()` after `_pageInNewRows()` so that it will continue to fetch new rows until the `_getDistanceFromEnd()` is less than the threshold, rather than stopping after the first page and waiting until the `contentSize` or `scrollOffset` change, but although this solves the problem for the Grid Layout example, it leads to over-fetching in the more common case of a standard row-based ListView. In the end, I just increased the `pageSize` to 3 for the Grid Layout example, which makes more sense anyway since loading a page that is not a multiple of the number of cells per row confuses the `_renderMoreRowsIfNeeded` algorithm, and leads to gaps at the bottom of the view. This solved the problem, however there was still a "pop-in" effect, where the additional rows were paged in after the ListView appeared. This was simply a misconfiguration in the example itself: The default of 10 rows was insufficient to fill the screen, so I changed the `initialListSize` prop to `20`. Reviewed By: javache Differential Revision: D2911690 fb-gh-sync-id: 8d6bd78843335fb091e7e24f7c2e6a416b0321d3 shipit-source-id: 8d6bd78843335fb091e7e24f7c2e6a416b0321d3 |
||
---|---|---|
.. | ||
AnimatedGratuitousApp | ||
NavigationExperimental | ||
Navigator | ||
Thumbnails | ||
UIExplorer | ||
UIExplorer.xcodeproj | ||
UIExplorerIntegrationTests | ||
UIExplorerUnitTests | ||
android/app | ||
AccessibilityAndroidExample.android.js | ||
AccessibilityIOSExample.js | ||
ActionSheetIOSExample.js | ||
ActivityIndicatorIOSExample.js | ||
AdSupportIOSExample.js | ||
AlertExample.js | ||
AlertIOSExample.js | ||
AnimatedExample.js | ||
AppStateExample.js | ||
AppStateIOSExample.js | ||
AssetScaledImageExample.js | ||
AsyncStorageExample.js | ||
BorderExample.js | ||
BoxShadowExample.js | ||
CameraRollExample.js | ||
CameraRollView.js | ||
ClipboardExample.js | ||
DatePickerAndroidExample.js | ||
DatePickerIOSExample.js | ||
ExampleTypes.js | ||
GeolocationExample.js | ||
ImageCapInsetsExample.js | ||
ImageEditingExample.js | ||
ImageExample.js | ||
IntentAndroidExample.android.js | ||
LayoutEventsExample.js | ||
LayoutExample.js | ||
ListViewExample.js | ||
ListViewGridLayoutExample.js | ||
ListViewPagingExample.js | ||
MapViewExample.js | ||
ModalExample.js | ||
NavigatorIOSColorsExample.js | ||
NavigatorIOSExample.js | ||
NetInfoExample.js | ||
PanResponderExample.js | ||
PickerAndroidExample.js | ||
PickerIOSExample.js | ||
PointerEventsExample.js | ||
ProgressBarAndroidExample.android.js | ||
ProgressViewIOSExample.js | ||
PullToRefreshViewAndroidExample.android.js | ||
PushNotificationIOSExample.js | ||
RCTRootViewIOSExample.js | ||
README.md | ||
RefreshControlExample.js | ||
ScrollViewExample.js | ||
ScrollViewSimpleExample.js | ||
SegmentedControlIOSExample.js | ||
SetPropertiesExampleApp.js | ||
SliderIOSExample.js | ||
StatusBarExample.js | ||
StatusBarIOSExample.js | ||
SwitchExample.js | ||
TabBarIOSExample.js | ||
TextExample.android.js | ||
TextExample.ios.js | ||
TextInputExample.android.js | ||
TextInputExample.ios.js | ||
TimePickerAndroidExample.js | ||
TimerExample.js | ||
ToastAndroidExample.android.js | ||
ToolbarAndroidExample.android.js | ||
TouchableExample.js | ||
TransformExample.js | ||
TransparentHitTestExample.js | ||
UIExplorerApp.android.js | ||
UIExplorerApp.ios.js | ||
UIExplorerBlock.js | ||
UIExplorerButton.js | ||
UIExplorerList.android.js | ||
UIExplorerList.ios.js | ||
UIExplorerListBase.js | ||
UIExplorerPage.js | ||
UIExplorerTitle.js | ||
VibrationIOSExample.js | ||
ViewExample.js | ||
ViewPagerAndroidExample.android.js | ||
WebViewExample.js | ||
XHRExample.android.js | ||
XHRExample.ios.js | ||
XHRExampleCookies.js | ||
XHRExampleFetch.js | ||
XHRExampleHeaders.js | ||
bunny.png | ||
createExamplePage.js | ||
flux@3x.png | ||
hawk.png | ||
helloworld.html | ||
slider-left.png | ||
slider-left@2x.png | ||
slider-right.png | ||
slider-right@2x.png | ||
slider.png | ||
slider@2x.png | ||
uie_comment_highlighted@2x.png | ||
uie_comment_normal@2x.png | ||
uie_thumb_big.png | ||
uie_thumb_normal@2x.png | ||
uie_thumb_selected@2x.png |
README.md
UIExplorer
The UIExplorer is a sample app that showcases React Native views and modules.
Running this app
Before running the app, make sure you ran:
git clone https://github.com/facebook/react-native.git
cd react-native
npm install
Running on iOS
Mac OS and Xcode are required.
- Open
Examples/UIExplorer/UIExplorer.xcodeproj
in Xcode - Hit the Run button
See Running on device if you want to use a physical device.
Running on Android
You'll need to have all the prerequisites (SDK, NDK) for Building React Native installed.
Start an Android emulator (Genymotion is recommended).
cd react-native
./gradlew :Examples:UIExplorer:android:app:installDebug
./packager/packager.sh
Note: Building for the first time can take a while.
Open the UIExplorer app in your emulator.
See Running on Device in case you want to use a physical device.
Built from source
Building the app on both iOS and Android means building the React Native framework from source. This way you're running the latest native and JS code the way you see it in your clone of the github repo.
This is different from apps created using react-native init
which have a dependency on a specific version of React Native JS and native code, declared in a package.json
file (and build.gradle
for Android apps).