如果不能科学上网,某些链接可能打不开,请移步中文README: React Native 仿淘宝安卓客户端首页.
- Prepare your environment: Requirements and Android Setup
- Clone this repo, and goto the project root directory
- Run
npm install
- Run
react-native run-android
- Enjoy
Note: For convenience, the demo enables bundling js & assets in debug mode, so there's no need to run react-native start
for js packager startup. If you need to modify codes and reload the demo for effect, please modify build.gradle, change bundleInDebug
from true
to false
, run react-native start
to start js packager, then connect your device to it. See Running On Device.
Currently there's only Android implementation. So index.ios.js & ./ios are absent. React version of this demo is 15.4.1. ReactNative version of this demo is 0.41.2.
Sadly, all pictures and text are placed in the demo so far. Still searching for free Taobao API.
ReactNative provides ViewPagerAndroid as a component to fullfill similar function of native ViewPager
. In fact it just wraps ReactViewPager
. The drawback of simply using ViewPagerAndroid
is lack of flexibility: e.g., it's impossible to slide the content in vertical direction. Inspired by race604/react-native-viewpager, the viewpager in this demo enhances it, adds more flexibility such as control of slide direction, pager indicator style & position, etc. In this demo, there are both horizontal & vertical sliding viewpagers.
Related code: CustomViewPager.js.
At first the demo tries to implement a pull-to-refresh-scrollview in pure js, using ScrollView, PanResponder and Animated. Basic idea is to concatenate a loading layout and a ScrollView
: when loading layout is hidden, ScrollView
is receiving gestures. When loading layout is pulled out to show, panresponder is receiving gestures, so scrollview receives no gesture, and loading layout is pulled out using Animation
. However, this idea encounters a gesture processing problem : react-native/issues/1046, which indicates:
Ideally, once something gets the responder, it can prevent others from responding by setting onPanResponderTerminationRequest: () => false,
However, we often have troubles while interacting with touch interactions happening on the main thread, like the iOS slider, or any scroll views. The problem is that javascript cannot immediately reject the responsiveness of the native event, because the main thread must synchronously decide if JS or the scroll view should become responder, and JS can only respond asynchronously.
This is a difficult problem to solve, and is something we have been thinking about for a while. cc @vjeux @sahrens @jordwalke @lelandrichardson. This could be solved in a similar way as @kmagiera is moving animations to the UI thread. Basically we construct and serialize some logic such that these immediate operations can be resolved without waiting for the JS thread.
Due to this issue, and the fact that ReactNative ScrollView
wraps a native ReactScrollView
, PanResponder
can not totally block gestures from passing to the native scrollview. As a result, sometimes when we try to pull down to refresh, gestures are absorbed by native scrollview to show an overscroll effect, not the pulldown of loading layout.
To solve this problem, the demo processes gestures in native code, then export the native view to ReactNative's js realm. Based on widely-known chrisbanes/Android-PullToRefresh, the demo simplifies its code, deleting unnecessary mode & attribute, retaining just the gesture processing part: RCTPullToRefreshScrollView.java. Then the demo exports it to js realm by RCTPullToRefreshScrollViewManager.java, the js component is PullToRefreshScrollView.js.
Another problem is how to define loading layout in js, not in java. This offers flexibility to hot reload the UI of loading layout. Intuitively, the demo tries to pass js element to native view by Props. But this doesn't work since Native UI Components only imports props of primitive types, or simple data structure such as ReadableArray
& ReadableMap
.
To hack this, the demo defines a native RCTPullToRefreshLoadingLayout.java, exports it to js by RCTPullToRefreshLoadingLayoutManager.java, the js component is PullToRefreshLoadingLayout.js. When the js component of this view, <RCTPullToRefreshLoadingLayout>
is added as a child of <RCTPullToRefreshScrollView>
, on the java side, RCTPullToRefreshLoadingLayout
is also added as a child of RCTPullToRefreshScrollView
. Override the addView
method of RCTPullToRefreshScrollView
to search for instance of RCTPullToRefreshLoadingLayout
. Once found, the demo gets the reference to it, i.e. the loading layout defined in js. With this reference, further manipulation with the loading layout is possible on java side.
Notably, to support the negative padding necessary for the pull down effect, RCTPullToRefreshScrollView
should handle its own layout, rather than leaving it to js. This is made possible by:
@Override
public boolean needsCustomLayoutForChildren() {
return true;
}
in RCTPullToRefreshScrollViewManager
.
The circle progress bar in loading layout, is drawn by react-native-svg and d3-shape. The idea comes from fdnhkj/react-native-conical-gradient. Related code: CircularProgressBarSvg.js.
The demo also provides a ART
version of circle progress bar. Sadly ART
has NO official documentation: react-native/issues/4789, and it frequently crashes (reason still not known). Related code: CircularProgressBarART.js.
TabNavigation is supported by react-native-tab-navigator. One thing to mention is that the SceneContainer
of this lib uses a style of position: absolute
, making the scene content actually takes up the whole space of its parent view. For this demo, the SceneContainer
is set to fixed height, which is the screen height minus tabBar height.