├── demo.gif
├── demo_android.gif
├── .gitignore
├── android
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── zyu
│ │ ├── ReactNativeWheelPickerPackage.java
│ │ ├── ReactWheelCurvedPickerManager.java
│ │ └── ReactWheelCurvedPicker.java
└── build.gradle
├── WheelCurvedPicker.ios.js
├── index.js
├── package.json
├── WheelCurvedPicker.android.js
└── README.md
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lesliesam/react-native-wheel-picker/HEAD/demo.gif
--------------------------------------------------------------------------------
/demo_android.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lesliesam/react-native-wheel-picker/HEAD/demo_android.gif
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | android/build
2 | *.iml
3 | android/.idea/workspace.xml
4 | android/.idea/copyright/profiles_settings.xml
5 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/WheelCurvedPicker.ios.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import {
5 | View,
6 | } from 'react-native';
7 |
8 | module.exports = View;
9 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 |
5 | import {
6 | PickerIOS,
7 | Platform,
8 | } from 'react-native';
9 |
10 | import WheelCurvedPicker from './WheelCurvedPicker'
11 |
12 | module.exports = (Platform.OS === 'ios' ? PickerIOS : WheelCurvedPicker)
13 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.1"
6 | defaultConfig {
7 | minSdkVersion 16
8 | targetSdkVersion 22
9 | versionCode 1
10 | versionName "1.0"
11 | ndk {
12 | abiFilters "armeabi-v7a", "x86"
13 | }
14 | }
15 | }
16 |
17 | dependencies {
18 | compile fileTree(dir: 'libs', include: ['*.jar'])
19 | compile "cn.aigestudio.wheelpicker:WheelPicker:1.0.3"
20 | compile 'com.facebook.react:react-native:+'
21 | }
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-wheel-picker",
3 | "version": "1.2.0",
4 | "description": "React native cross platform picker.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/lesliesam/react-native-wheel-picker.git"
12 | },
13 | "keywords": [
14 | "react-native",
15 | "picker",
16 | "wheel"
17 | ],
18 | "author": "Yu Zheng",
19 | "license": "ISC",
20 | "bugs": {
21 | "url": "https://github.com/lesliesam/react-native-wheel-picker/issues"
22 | },
23 | "homepage": "https://github.com/lesliesam/react-native-wheel-picker#readme",
24 | "dependencies": {}
25 | }
26 |
--------------------------------------------------------------------------------
/android/src/main/java/com/zyu/ReactNativeWheelPickerPackage.java:
--------------------------------------------------------------------------------
1 | package com.zyu;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 |
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | /**
14 | * @author Sam Yu
15 | */
16 | public class ReactNativeWheelPickerPackage implements ReactPackage {
17 | @Override
18 | public List createNativeModules(ReactApplicationContext reactContext) {
19 | return Collections.emptyList();
20 | }
21 |
22 | public List> createJSModules() {
23 | return Collections.emptyList();
24 | }
25 |
26 | @Override
27 | public List createViewManagers(ReactApplicationContext reactContext) {
28 | return Arrays.asList(
29 | new ReactWheelCurvedPickerManager()
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/WheelCurvedPicker.android.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React from 'react';
4 | import PropTypes from 'prop-types'
5 | import {
6 | View,
7 | ColorPropType,
8 | requireNativeComponent,
9 | } from 'react-native';
10 |
11 | const defaultItemStyle = { color: 'white', fontSize: 26 };
12 |
13 | const WheelCurvedPickerNativeInterface = {
14 | name: 'WheelCurvedPicker',
15 | propTypes: {
16 | ...View.propTypes,
17 | data:PropTypes.array,
18 | textColor: ColorPropType,
19 | textSize: PropTypes.number,
20 | itemStyle: PropTypes.object,
21 | itemSpace: PropTypes.number,
22 | onValueChange: PropTypes.func,
23 | selectedValue: PropTypes.any,
24 | selectedIndex: PropTypes.number,
25 | }
26 | }
27 |
28 | const WheelCurvedPickerNative = requireNativeComponent('WheelCurvedPicker', WheelCurvedPickerNativeInterface);
29 |
30 | class WheelCurvedPicker extends React.Component {
31 |
32 | propTypes: {
33 | ...View.propTypes,
34 |
35 | data: PropTypes.array,
36 |
37 | textColor: ColorPropType,
38 |
39 | textSize: PropTypes.number,
40 |
41 | itemStyle: PropTypes.object,
42 |
43 | itemSpace: PropTypes.number,
44 |
45 | onValueChange: PropTypes.func,
46 |
47 | selectedValue: PropTypes.any,
48 |
49 | selectedIndex: PropTypes.number,
50 | }
51 |
52 | constructor(props){
53 | super(props)
54 | this.state = this._stateFromProps(props)
55 | }
56 |
57 | static defaultProps = {
58 | itemStyle : {color:"white", fontSize:26},
59 | itemSpace: 20
60 | }
61 |
62 | componentWillReceiveProps (props) {
63 | this.setState(this._stateFromProps(props));
64 | }
65 |
66 | _stateFromProps (props) {
67 | var selectedIndex = 0;
68 | var items = [];
69 | React.Children.forEach(props.children, function (child, index) {
70 | if (child.props.value === props.selectedValue) {
71 | selectedIndex = index;
72 | }
73 | items.push({value: child.props.value, label: child.props.label});
74 | });
75 |
76 | var textSize = props.itemStyle.fontSize
77 | var textColor = props.itemStyle.color
78 |
79 | return {selectedIndex, items, textSize, textColor};
80 | }
81 |
82 | _onValueChange = (e) => {
83 | if (this.props.onValueChange) {
84 | this.props.onValueChange(e.nativeEvent.data);
85 | }
86 | }
87 |
88 | render() {
89 | return ;
96 | }
97 | }
98 |
99 | class Item extends React.Component {
100 | propTypes: {
101 | value: React.PropTypes.any, // string or integer basically
102 | label: React.PropTypes.string,
103 | }
104 |
105 | render () {
106 | // These items don't get rendered directly.
107 | return null;
108 | }
109 | }
110 |
111 | WheelCurvedPicker.Item = Item;
112 |
113 | module.exports = WheelCurvedPicker;
114 |
--------------------------------------------------------------------------------
/android/src/main/java/com/zyu/ReactWheelCurvedPickerManager.java:
--------------------------------------------------------------------------------
1 | package com.zyu;
2 |
3 | import android.graphics.Color;
4 |
5 | import com.aigestudio.wheelpicker.core.AbstractWheelPicker;
6 | import com.facebook.react.bridge.ReadableArray;
7 | import com.facebook.react.bridge.ReadableMap;
8 | import com.facebook.react.common.MapBuilder;
9 | import com.facebook.react.uimanager.PixelUtil;
10 | import com.facebook.react.uimanager.SimpleViewManager;
11 | import com.facebook.react.uimanager.ThemedReactContext;
12 | import com.facebook.react.uimanager.annotations.ReactProp;
13 |
14 | import java.util.ArrayList;
15 | import java.util.Map;
16 |
17 | /**
18 | * @author Sam Yu
19 | */
20 | public class ReactWheelCurvedPickerManager extends SimpleViewManager {
21 |
22 | private static final String REACT_CLASS = "WheelCurvedPicker";
23 |
24 | private static final int DEFAULT_TEXT_SIZE = 25 * 2;
25 | private static final int DEFAULT_ITEM_SPACE = 14 * 2;
26 |
27 | @Override
28 | protected ReactWheelCurvedPicker createViewInstance(ThemedReactContext reactContext) {
29 | ReactWheelCurvedPicker picker = new ReactWheelCurvedPicker(reactContext);
30 | picker.setTextColor(Color.LTGRAY);
31 | picker.setCurrentTextColor(Color.WHITE);
32 | picker.setTextSize(DEFAULT_TEXT_SIZE);
33 | picker.setItemSpace(DEFAULT_ITEM_SPACE);
34 |
35 | return picker;
36 | }
37 |
38 | @Override
39 | public Map getExportedCustomDirectEventTypeConstants() {
40 | return MapBuilder.of(
41 | ItemSelectedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onValueChange")
42 | );
43 | }
44 |
45 | @ReactProp(name="data")
46 | public void setData(ReactWheelCurvedPicker picker, ReadableArray items) {
47 | if (picker != null) {
48 | ArrayList valueData = new ArrayList<>();
49 | ArrayList labelData = new ArrayList<>();
50 | for (int i = 0; i < items.size(); i ++) {
51 | ReadableMap itemMap = items.getMap(i);
52 | valueData.add(itemMap.getInt("value"));
53 | labelData.add(itemMap.getString("label"));
54 | }
55 | picker.setValueData(valueData);
56 | picker.setData(labelData);
57 | }
58 | }
59 |
60 | @ReactProp(name="selectedIndex")
61 | public void setSelectedIndex(ReactWheelCurvedPicker picker, int index) {
62 | if (picker != null && picker.getState() == AbstractWheelPicker.SCROLL_STATE_IDLE) {
63 | picker.setItemIndex(index);
64 | picker.invalidate();
65 | }
66 | }
67 |
68 | @ReactProp(name="textColor", customType = "Color")
69 | public void setTextColor(ReactWheelCurvedPicker picker, Integer color) {
70 | if (picker != null) {
71 | picker.setCurrentTextColor(color);
72 | picker.setTextColor(color);
73 | }
74 | }
75 |
76 | @ReactProp(name="textSize")
77 | public void setTextSize(ReactWheelCurvedPicker picker, int size) {
78 | if (picker != null) {
79 | picker.setTextSize((int) PixelUtil.toPixelFromDIP(size));
80 | }
81 | }
82 |
83 | @ReactProp(name="itemSpace")
84 | public void setItemSpace(ReactWheelCurvedPicker picker, int space) {
85 | if (picker != null) {
86 | picker.setItemSpace((int) PixelUtil.toPixelFromDIP(space));
87 | }
88 | }
89 |
90 | @Override
91 | public String getName() {
92 | return REACT_CLASS;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java:
--------------------------------------------------------------------------------
1 | package com.zyu;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.LinearGradient;
6 | import android.graphics.Paint;
7 | import android.graphics.Shader;
8 | import android.os.SystemClock;
9 | import android.util.AttributeSet;
10 |
11 | import com.aigestudio.wheelpicker.core.AbstractWheelPicker;
12 | import com.aigestudio.wheelpicker.view.WheelCurvedPicker;
13 | import com.facebook.react.bridge.Arguments;
14 | import com.facebook.react.bridge.ReactContext;
15 | import com.facebook.react.bridge.WritableMap;
16 | import com.facebook.react.uimanager.UIManagerModule;
17 | import com.facebook.react.uimanager.events.Event;
18 | import com.facebook.react.uimanager.events.EventDispatcher;
19 | import com.facebook.react.uimanager.events.RCTEventEmitter;
20 |
21 | import java.util.List;
22 |
23 | /**
24 | * @author Sam Yu
25 | */
26 | public class ReactWheelCurvedPicker extends WheelCurvedPicker {
27 |
28 | private final EventDispatcher mEventDispatcher;
29 | private List mValueData;
30 |
31 | public ReactWheelCurvedPicker(ReactContext reactContext) {
32 | super(reactContext);
33 | mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
34 | setOnWheelChangeListener(new OnWheelChangeListener() {
35 | @Override
36 | public void onWheelScrolling(float deltaX, float deltaY) {
37 | }
38 |
39 | @Override
40 | public void onWheelSelected(int index, String data) {
41 | if (mValueData != null && index < mValueData.size()) {
42 | mEventDispatcher.dispatchEvent(
43 | new ItemSelectedEvent(getId(), mValueData.get(index)));
44 | }
45 | }
46 |
47 | @Override
48 | public void onWheelScrollStateChanged(int state) {
49 | }
50 | });
51 | }
52 |
53 | @Override
54 | protected void drawForeground(Canvas canvas) {
55 | super.drawForeground(canvas);
56 |
57 | Paint paint = new Paint();
58 | paint.setColor(Color.WHITE);
59 | int colorFrom = 0x00FFFFFF;//Color.BLACK;
60 | int colorTo = Color.WHITE;
61 | LinearGradient linearGradientShader = new LinearGradient(rectCurItem.left, rectCurItem.top, rectCurItem.right/2, rectCurItem.top, colorFrom, colorTo, Shader.TileMode.MIRROR);
62 | paint.setShader(linearGradientShader);
63 | canvas.drawLine(rectCurItem.left, rectCurItem.top, rectCurItem.right, rectCurItem.top, paint);
64 | canvas.drawLine(rectCurItem.left, rectCurItem.bottom, rectCurItem.right, rectCurItem.bottom, paint);
65 | }
66 |
67 | @Override
68 | public void setItemIndex(int index) {
69 | super.setItemIndex(index);
70 | unitDeltaTotal = 0;
71 | mHandler.post(this);
72 | }
73 |
74 | public void setValueData(List data) {
75 | mValueData = data;
76 | }
77 |
78 | public int getState() {
79 | return state;
80 | }
81 | }
82 |
83 | class ItemSelectedEvent extends Event {
84 |
85 | public static final String EVENT_NAME = "wheelCurvedPickerPageSelected";
86 |
87 | private final int mValue;
88 |
89 | protected ItemSelectedEvent(int viewTag, int value) {
90 | super(viewTag);
91 | mValue = value;
92 | }
93 |
94 | @Override
95 | public String getEventName() {
96 | return EVENT_NAME;
97 | }
98 |
99 | @Override
100 | public void dispatch(RCTEventEmitter rctEventEmitter) {
101 | rctEventEmitter.receiveEvent(getViewTag(), getEventName(), serializeEventData());
102 | }
103 |
104 | private WritableMap serializeEventData() {
105 | WritableMap eventData = Arguments.createMap();
106 | eventData.putInt("data", mValue);
107 | return eventData;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-wheel-picker
2 | [](https://npmjs.org/package/react-native-wheel-picker "View this project on npm")
3 | [](https://npmjs.org/package/react-native-wheel-picker "View this project on npm")
4 |
5 | ## Introduction
6 | Cross platform Picker component based on React-native.
7 |
8 | Since picker is originally supported by ios while Android only supports a ugly Spinner component. If you want to have the same user behaviour, you can use this.
9 |
10 | The android component is based on https://github.com/AigeStudio/WheelPicker which runs super fast and smoothly. It also supports curved effect which make it exactly the same looking and feel as the ios picker.
11 | 
12 | 
13 |
14 | ## How to use
15 |
16 | Run command
17 |
18 | For apps using RN 0.40 or higher, please run
19 | ```
20 | npm i react-native-wheel-picker --save
21 | ```
22 | For apps using RN 0.39 or less, please run
23 | ```
24 | npm install --save --save-exact react-native-wheel-picker@1.1.2
25 | ```
26 | Add in settings.gradle
27 | ```
28 | include ':react-native-wheel-picker'
29 | project(':react-native-wheel-picker').projectDir = new File(settingsDir, '../node_modules/react-native-wheel-picker/android')
30 | ```
31 | Add in app/build.gradle
32 | ```
33 | compile project(':react-native-wheel-picker')
34 | ```
35 | Modify MainApplication
36 | ```
37 | import com.zyu.ReactNativeWheelPickerPackage;
38 | ......
39 |
40 | protected List getPackages() {
41 | return Arrays.asList(
42 | new MainReactPackage(), new ReactNativeWheelPickerPackage()
43 | );
44 | }
45 | ```
46 |
47 | ## Example code
48 | ```
49 | import React, { Component } from 'react';
50 | import {
51 | Platform,
52 | StyleSheet,
53 | Text,
54 | View,
55 | } from 'react-native';
56 |
57 |
58 | import Picker from 'react-native-wheel-picker'
59 | var PickerItem = Picker.Item;
60 |
61 | export default class App extends Component<{}> {
62 |
63 | constructor(props) {
64 | super(props);
65 | this.state = {
66 | selectedItem : 2,
67 | itemList: ['刘备', '张飞', '关羽', '赵云', '黄忠', '马超', '魏延', '诸葛亮']
68 | };
69 | }
70 |
71 | onPickerSelect (index) {
72 | this.setState({
73 | selectedItem: index,
74 | })
75 | }
76 |
77 | onAddItem = () => {
78 | var name = '司马懿'
79 | if (this.state.itemList.indexOf(name) == -1) {
80 | this.state.itemList.push(name)
81 | }
82 | this.setState({
83 | selectedItem: this.state.itemList.indexOf(name),
84 | })
85 | }
86 |
87 | render () {
88 | return (
89 |
90 |
91 | Welcome to React Native!
92 |
93 | this.onPickerSelect(index)}>
97 | {this.state.itemList.map((value, i) => (
98 |
99 | ))}
100 |
101 |
102 | 你最喜欢的是:{this.state.itemList[this.state.selectedItem]}
103 |
104 |
105 |
107 | 怎么没有司马懿?
108 |
109 |
110 | );
111 | }
112 | }
113 |
114 | const styles = StyleSheet.create({
115 | container: {
116 | flex: 1,
117 | justifyContent: 'center',
118 | alignItems: 'center',
119 | backgroundColor: '#1962dd',
120 | },
121 | welcome: {
122 | fontSize: 20,
123 | textAlign: 'center',
124 | margin: 10,
125 | color: '#ffffff',
126 | },
127 | instructions: {
128 | textAlign: 'center',
129 | color: '#333333',
130 | marginBottom: 5,
131 | },
132 | });
133 | ```
134 |
--------------------------------------------------------------------------------