Skip to content

Commit 0258d5d

Browse files
brentvatneide
authored andcommitted
Add setPageWithoutAnimation
Summary: In some cases it's desirable to set the page in the ViewPager without animating it -- we have this in ScrollView with `scrollWithoutAnimationTo`. This PR adds `setPageWithoutAnimation` on ViewPager. cc ide kmagiera Closes facebook#3621 Reviewed By: svcscm Differential Revision: D2652056 Pulled By: mkonicek fb-gh-sync-id: 6f1f38558c41ffdd863c0ebb2f046c75b5c0392c
1 parent 75b6d2b commit 0258d5d

File tree

4 files changed

+121
-45
lines changed

4 files changed

+121
-45
lines changed

Examples/UIExplorer/ViewPagerAndroidExample.android.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,23 +96,39 @@ var ViewPagerAndroidExample = React.createClass({
9696
description: 'Container that allows to flip left and right between child views.'
9797
},
9898
getInitialState: function() {
99-
return {page: 0, progress: {position: 0, offset: 0}};
99+
return {
100+
page: 0,
101+
animationsAreEnabled: true,
102+
progress: {
103+
position: 0,
104+
offset: 0,
105+
},
106+
};
100107
},
108+
101109
onPageSelected: function(e) {
102110
this.setState({page: e.nativeEvent.position});
103111
},
112+
104113
onPageScroll: function(e) {
105114
this.setState({progress: e.nativeEvent});
106115
},
116+
107117
move: function(delta) {
108118
var page = this.state.page + delta;
109-
this.viewPager && this.viewPager.setPage(page);
110-
this.setState({page});
119+
this.go(page);
111120
},
121+
112122
go: function(page) {
113-
this.viewPager && this.viewPager.setPage(page);
123+
if (this.state.animationsAreEnabled) {
124+
this.viewPager.setPage(page);
125+
} else {
126+
this.viewPager.setPageWithoutAnimation(page);
127+
}
128+
114129
this.setState({page});
115130
},
131+
116132
render: function() {
117133
var pages = [];
118134
for (var i = 0; i < PAGES; i++) {
@@ -131,7 +147,7 @@ var ViewPagerAndroidExample = React.createClass({
131147
</View>
132148
);
133149
}
134-
var page = this.state.page;
150+
var { page, animationsAreEnabled } = this.state;
135151
return (
136152
<View style={styles.container}>
137153
<ViewPagerAndroid
@@ -142,6 +158,19 @@ var ViewPagerAndroidExample = React.createClass({
142158
ref={viewPager => { this.viewPager = viewPager; }}>
143159
{pages}
144160
</ViewPagerAndroid>
161+
<View style={styles.buttons}>
162+
{ animationsAreEnabled ?
163+
<Button
164+
text="Turn off animations"
165+
enabled={true}
166+
onPress={() => this.setState({animationsAreEnabled: false})}
167+
/> :
168+
<Button
169+
text="Turn animations back on"
170+
enabled={true}
171+
onPress={() => this.setState({animationsAreEnabled: true})}
172+
/> }
173+
</View>
145174
<View style={styles.buttons}>
146175
<Button text="Start" enabled={page > 0} onPress={() => this.go(0)}/>
147176
<Button text="Prev" enabled={page > 0} onPress={() => this.move(-1)}/>

Libraries/Components/ViewPager/ViewPagerAndroid.android.js

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@
22
* Copyright 2004-present Facebook. All Rights Reserved.
33
*
44
* @providesModule ViewPagerAndroid
5+
* @flow
56
*/
67
'use strict';
78

9+
var NativeModules = require('NativeModules');
810
var NativeMethodsMixin = require('NativeMethodsMixin');
911
var React = require('React');
1012
var ReactElement = require('ReactElement');
1113
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
1214
var ReactPropTypes = require('ReactPropTypes');
1315

16+
var RCTUIManager = NativeModules.UIManager;
17+
1418
var createReactNativeComponentClass = require('createReactNativeComponentClass');
1519
var dismissKeyboard = require('dismissKeyboard');
1620

1721
var VIEWPAGER_REF = 'viewPager';
1822

19-
var ViewPagerValidAttributes = {
20-
selectedPage: true,
21-
};
22-
2323
/**
2424
* Container that allows to flip left and right between child views. Each
2525
* child view of the `ViewPagerAndroid` will be treated as a separate page
@@ -97,17 +97,17 @@ var ViewPagerAndroid = React.createClass({
9797
]),
9898
},
9999

100-
getInitialState: function() {
101-
return {
102-
selectedPage: this.props.initialPage,
103-
};
100+
componentDidMount: function() {
101+
if (this.props.initialPage) {
102+
this.setPageWithoutAnimation(this.props.initialPage);
103+
}
104104
},
105105

106-
getInnerViewNode: function() {
106+
getInnerViewNode: function(): ReactComponent {
107107
return this.refs[VIEWPAGER_REF].getInnerViewNode();
108108
},
109109

110-
_childrenWithOverridenStyle: function() {
110+
_childrenWithOverridenStyle: function(): Array {
111111
// Override styles so that each page will fill the parent. Native component
112112
// will handle positioning of elements, so it's not important to offset
113113
// them correctly.
@@ -134,34 +134,51 @@ var ViewPagerAndroid = React.createClass({
134134
return ReactElement.createElement(child.type, newProps);
135135
});
136136
},
137-
_onPageScroll: function(event) {
137+
138+
_onPageScroll: function(e: Event) {
138139
if (this.props.onPageScroll) {
139-
this.props.onPageScroll(event);
140+
this.props.onPageScroll(e);
140141
}
141142
if (this.props.keyboardDismissMode === 'on-drag') {
142143
dismissKeyboard();
143144
}
144145
},
145-
_onPageSelected: function(event) {
146-
var selectedPage = event.nativeEvent.position;
147-
this.setState({
148-
selectedPage,
149-
});
146+
147+
_onPageSelected: function(e: Event) {
150148
if (this.props.onPageSelected) {
151-
this.props.onPageSelected(event);
149+
this.props.onPageSelected(e);
152150
}
153151
},
154-
setPage: function(selectedPage) {
155-
this.setState({
156-
selectedPage,
157-
});
152+
153+
/**
154+
* A helper function to scroll to a specific page in the ViewPager.
155+
* The transition between pages will be animated.
156+
*/
157+
setPage: function(selectedPage: number) {
158+
RCTUIManager.dispatchViewManagerCommand(
159+
React.findNodeHandle(this),
160+
RCTUIManager.AndroidViewPager.Commands.setPage,
161+
[selectedPage],
162+
);
158163
},
164+
165+
/**
166+
* A helper function to scroll to a specific page in the ViewPager.
167+
* The transition between pages will be *not* be animated.
168+
*/
169+
setPageWithoutAnimation: function(selectedPage: number) {
170+
RCTUIManager.dispatchViewManagerCommand(
171+
React.findNodeHandle(this),
172+
RCTUIManager.AndroidViewPager.Commands.setPageWithoutAnimation,
173+
[selectedPage],
174+
);
175+
},
176+
159177
render: function() {
160178
return (
161179
<NativeAndroidViewPager
162180
ref={VIEWPAGER_REF}
163181
style={this.props.style}
164-
selectedPage={this.state.selectedPage}
165182
onPageScroll={this._onPageScroll}
166183
onPageSelected={this._onPageSelected}
167184
children={this._childrenWithOverridenStyle()}
@@ -171,10 +188,7 @@ var ViewPagerAndroid = React.createClass({
171188
});
172189

173190
var NativeAndroidViewPager = createReactNativeComponentClass({
174-
validAttributes: {
175-
...ReactNativeViewAttributes.UIView,
176-
...ViewPagerValidAttributes,
177-
},
191+
validAttributes: ReactNativeViewAttributes.UIView,
178192
uiViewClassName: 'AndroidViewPager',
179193
});
180194

ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPager.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
133133
return false;
134134
}
135135

136+
public void setCurrentItemFromJs(int item, boolean animated) {
137+
mIsCurrentItemFromJs = true;
138+
setCurrentItem(item, animated);
139+
mIsCurrentItemFromJs = false;
140+
}
141+
136142
/*package*/ void addViewToAdapter(View child, int index) {
137143
getAdapter().addView(child, index);
138144
}
@@ -148,10 +154,4 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
148154
/*package*/ View getViewFromAdapter(int index) {
149155
return getAdapter().getViewAt(index);
150156
}
151-
152-
/*package*/ void setCurrentItemFromJs(int item) {
153-
mIsCurrentItemFromJs = true;
154-
setCurrentItem(item);
155-
mIsCurrentItemFromJs = false;
156-
}
157157
}

ReactAndroid/src/main/java/com/facebook/react/views/viewpager/ReactViewPagerManager.java

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,24 @@
1313

1414
import android.view.View;
1515

16+
import com.facebook.infer.annotation.Assertions;
17+
import com.facebook.react.bridge.ReadableArray;
1618
import com.facebook.react.common.MapBuilder;
17-
import com.facebook.react.uimanager.ReactProp;
1819
import com.facebook.react.uimanager.ThemedReactContext;
1920
import com.facebook.react.uimanager.ViewGroupManager;
2021

22+
import javax.annotation.Nullable;
23+
2124
/**
2225
* Instance of {@link ViewManager} that provides native {@link ViewPager} view.
2326
*/
2427
public class ReactViewPagerManager extends ViewGroupManager<ReactViewPager> {
2528

2629
private static final String REACT_CLASS = "AndroidViewPager";
2730

31+
public static final int COMMAND_SET_PAGE = 1;
32+
public static final int COMMAND_SET_PAGE_WITHOUT_ANIMATION = 2;
33+
2834
@Override
2935
public String getName() {
3036
return REACT_CLASS;
@@ -35,12 +41,6 @@ protected ReactViewPager createViewInstance(ThemedReactContext reactContext) {
3541
return new ReactViewPager(reactContext);
3642
}
3743

38-
@ReactProp(name = "selectedPage")
39-
public void setSelectedPage(ReactViewPager view, int page) {
40-
// TODO(8496821): Handle selectedPage property cleanup correctly, now defaults to 0
41-
view.setCurrentItemFromJs(page);
42-
}
43-
4444
@Override
4545
public boolean needsCustomLayoutForChildren() {
4646
return true;
@@ -54,6 +54,39 @@ public Map getExportedCustomDirectEventTypeConstants() {
5454
);
5555
}
5656

57+
@Override
58+
public Map<String,Integer> getCommandsMap() {
59+
return MapBuilder.of(
60+
"setPage",
61+
COMMAND_SET_PAGE,
62+
"setPageWithoutAnimation",
63+
COMMAND_SET_PAGE_WITHOUT_ANIMATION);
64+
}
65+
66+
@Override
67+
public void receiveCommand(
68+
ReactViewPager viewPager,
69+
int commandType,
70+
@Nullable ReadableArray args) {
71+
Assertions.assertNotNull(viewPager);
72+
Assertions.assertNotNull(args);
73+
switch (commandType) {
74+
case COMMAND_SET_PAGE: {
75+
viewPager.setCurrentItemFromJs(args.getInt(0), true);
76+
return;
77+
}
78+
case COMMAND_SET_PAGE_WITHOUT_ANIMATION: {
79+
viewPager.setCurrentItemFromJs(args.getInt(0), false);
80+
return;
81+
}
82+
default:
83+
throw new IllegalArgumentException(String.format(
84+
"Unsupported command %d received by %s.",
85+
commandType,
86+
getClass().getSimpleName()));
87+
}
88+
}
89+
5790
@Override
5891
public void addView(ReactViewPager parent, View child, int index) {
5992
parent.addViewToAdapter(child, index);

0 commit comments

Comments
 (0)