├── .gitignore ├── .npmignore ├── Picker.podspec ├── README.md ├── android ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── beefe │ │ └── picker │ │ ├── PickerViewModule.java │ │ ├── PickerViewPackage.java │ │ ├── util │ │ ├── BuildProperties.java │ │ └── MIUIUtils.java │ │ └── view │ │ ├── InertiaTimerTask.java │ │ ├── LoopView.java │ │ ├── LoopViewGestureListener.java │ │ ├── MessageHandler.java │ │ ├── OnItemSelectedListener.java │ │ ├── OnItemSelectedRunnable.java │ │ ├── OnSelectedListener.java │ │ ├── PickerViewAlone.java │ │ ├── PickerViewLinkage.java │ │ ├── ReturnData.java │ │ └── SmoothScrollTimerTask.java │ └── res │ ├── anim │ ├── picker_enter.xml │ └── picker_exit.xml │ ├── layout │ ├── picker_view.xml │ ├── picker_view_alone.xml │ └── picker_view_linkage.xml │ └── values │ ├── strings.xml │ └── styles.xml ├── doc ├── ui3.jpg └── ui4.jpg ├── example └── PickerTest │ ├── .babelrc │ ├── .buckconfig │ ├── .flowconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .watchmanconfig │ ├── PickerTest.js │ ├── __tests__ │ └── App.js │ ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── pickertest │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystores │ │ ├── BUCK │ │ └── debug.keystore.properties │ └── settings.gradle │ ├── app.json │ ├── area.json │ ├── index.js │ ├── ios │ ├── PickerTest-tvOS │ │ └── Info.plist │ ├── PickerTest-tvOSTests │ │ └── Info.plist │ ├── PickerTest.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── PickerTest-tvOS.xcscheme │ │ │ └── PickerTest.xcscheme │ ├── PickerTest │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ └── PickerTestTests │ │ ├── Info.plist │ │ └── PickerTestTests.m │ └── package.json ├── index.d.ts ├── index.js ├── ios ├── RCTBEEPickerManager.xcodeproj │ └── project.pbxproj └── RCTBEEPickerManager │ ├── BzwPicker.h │ ├── BzwPicker.m │ ├── RCTBEEPickerManager.h │ └── RCTBEEPickerManager.m └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | .idea 28 | .gradle 29 | local.properties 30 | 31 | # node.js 32 | # 33 | node_modules/ 34 | npm-debug.log 35 | 36 | # BUCK 37 | buck-out/ 38 | \.buckd/ 39 | android/app/libs 40 | android/keystores/debug.keystore -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | demo 2 | doc 3 | example 4 | .gitignore 5 | -------------------------------------------------------------------------------- /Picker.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | 7 | s.name = 'Picker' 8 | s.version = package['version'].gsub(/v|-beta/, '') 9 | s.summary = package['description'] 10 | s.author = package['author'] 11 | s.license = package['license'] 12 | s.homepage = package['homepage'] 13 | s.source = { :git => 'https://github.com/beefe/react-native-picker.git', :tag => "v#{s.version}"} 14 | s.platform = :ios, '7.0' 15 | s.preserve_paths = '*.js' 16 | 17 | s.subspec 'Core' do |ss| 18 | ss.dependency 'React' 19 | ss.source_files = 'ios/RCTBEEPickerManager/*.{h,m}' 20 | ss.public_header_files = ['ios/RCTBEEPickerManager/*.h'] 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-picker 2 | 3 | [![npm version](https://img.shields.io/npm/v/react-native-picker.svg?style=flat-square)](https://www.npmjs.com/package/react-native-picker) dependency status 4 | 5 | ![ui3](./doc/ui3.jpg) 6 | ![ui4](./doc/ui4.jpg) 7 | 8 | ### Documentation 9 | 10 | #### Params 11 | 12 | |Key | Type | Default| Support | Description | 13 | | --- | --- | ---- | ------ | ----------- | 14 | |isLoop | Boolean | false | Android | | 15 | |pickerTextEllipsisLen | number | 6 | Android | | 16 | |pickerConfirmBtnText | string | confirm | iOS/Android | | 17 | |pickerCancelBtnText | string | cancel | iOS/Android | | 18 | |pickerTitleText | string | pls select | iOS/Android | | 19 | |pickerConfirmBtnColor | array | [1, 186, 245, 1] | iOS/Android | | 20 | |pickerCancelBtnColor | array | [1, 186, 245, 1] | iOS/Android | | 21 | |pickerTitleColor | array | [20, 20, 20, 1] | iOS/Android | | 22 | |pickerToolBarBg | array | [232, 232, 232, 1] | iOS/Android | | 23 | |pickerBg | array | [196, 199, 206, 1] | iOS/Android | | 24 | |pickerToolBarFontSize | number | 16 | iOS/Android | | 25 | |wheelFlex | array | [1, 1, 1] | iOS/Android | | 26 | |pickerFontSize | number | 16 | iOS/Android | | 27 | |pickerFontColor | array | [31, 31, 31, 1] | iOS/Android | | 28 | |pickerFontFamily | string | | iOS/Android | | 29 | |pickerRowHeight | number | 24 | iOS | | 30 | |pickerData | array | | iOS/Android | | 31 | |selectedValue | array | | iOS/Android | | 32 | |onPickerConfirm | function| | iOS/Android | | 33 | |onPickerCancel | function| | iOS/Android | | 34 | |onPickerSelect | function| | iOS/Android | | 35 | 36 | #### Methods 37 | 38 | |Key | Support | Description | 39 | | --- | ---- | ----------- | 40 | |init | iOS/Android |init and pass parameters to picker | 41 | |toggle | iOS/Android |show or hide picker | 42 | |show | iOS/Android |show picker | 43 | |hide | iOS/Android |hide picker | 44 | |select | iOS/Android |select a row | 45 | |isPickerShow | iOS/Android |get status of picker, return a boolean | 46 | 47 | 48 | ### Usage 49 | 50 | #### Step 1 - install 51 | 52 | ```javascript 53 | npm install react-native-picker --save 54 | ``` 55 | 56 | #### Step 2 - link 57 | 58 | ``` 59 | react-native link 60 | ``` 61 | 62 | #### Step 3 - import and use in project 63 | 64 | ```javascript 65 | import Picker from 'react-native-picker'; 66 | let data = []; 67 | for(var i=0;i<100;i++){ 68 | data.push(i); 69 | } 70 | 71 | Picker.init({ 72 | pickerData: data, 73 | selectedValue: [59], 74 | onPickerConfirm: data => { 75 | console.log(data); 76 | }, 77 | onPickerCancel: data => { 78 | console.log(data); 79 | }, 80 | onPickerSelect: data => { 81 | console.log(data); 82 | } 83 | }); 84 | Picker.show(); 85 | 86 | ``` 87 | 88 | ### Integration With Existing Apps (`iOS`) 89 | The `Podfile` will like below: 90 | ``` ruby 91 | platform :ios, '8.0' 92 | target 'YourTarget' do 93 | pod 'React', :path => '../YOUR_REACT_NATIVE_PROJECT/node_modules/react-native', :subspecs => [ 94 | 'Core', 95 | ... 96 | ] 97 | pod 'Picker', :path => '../YOUR_REACT_NATIVE_PROJECT/node_modules/react-native-picker' 98 | end 99 | ``` 100 | After you have updated the `Podfile` of the existing app, you can install `react-native-picker` like below: 101 | ``` bash 102 | $ pod install 103 | ``` 104 | 105 | ### Notice 106 | 107 | #### support two modes: 108 | 109 | 1. parallel: such as time picker, wheels have no connection with each other 110 | 111 | 2. cascade: such as date picker, address picker .etc, when front wheel changed, the behind wheels will all be reset 112 | 113 | #### parallel: 114 | 115 | - single wheel: 116 | 117 | ```javascript 118 | pickerData = [1,2,3,4]; 119 | selectedValue = 3; 120 | ``` 121 | 122 | - two or more wheel: 123 | 124 | ```javascript 125 | pickerData = [ 126 | [1,2,3,4], 127 | [5,6,7,8], 128 | ... 129 | ]; 130 | selectedValue = [1, 5]; 131 | ``` 132 | 133 | #### cascade: 134 | 135 | - two wheel 136 | 137 | ```javascript 138 | pickerData = [ 139 | { 140 | a: [1, 2, 3, 4] 141 | }, 142 | { 143 | b: [5, 6, 7, 8] 144 | }, 145 | ... 146 | ]; 147 | selectedValue = ['a', 2]; 148 | ``` 149 | 150 | - three wheel 151 | 152 | ```javascript 153 | pickerData = [ 154 | { 155 | a: [ 156 | { 157 | a1: [1, 2, 3, 4] 158 | }, 159 | { 160 | a2: [5, 6, 7, 8] 161 | }, 162 | { 163 | a3: [9, 10, 11, 12] 164 | } 165 | ] 166 | }, 167 | { 168 | b: [ 169 | { 170 | b1: [11, 22, 33, 44] 171 | }, 172 | { 173 | b2: [55, 66, 77, 88] 174 | }, 175 | { 176 | b3: [99, 1010, 1111, 1212] 177 | } 178 | ] 179 | }, 180 | { 181 | c: [ 182 | { 183 | c1: ['a', 'b', 'c'] 184 | }, 185 | { 186 | c2: ['aa', 'bb', 'cc'] 187 | }, 188 | { 189 | c3: ['aaa', 'bbb', 'ccc'] 190 | } 191 | ] 192 | }, 193 | ... 194 | ] 195 | ``` 196 | 197 | ### For pure javascript version -> [v3.0.5](https://github.com/beefe/react-native-picker/tree/pure-javascript-version) 198 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 27 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 27 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(include: ['*.jar'], dir: 'libs') 26 | implementation 'com.facebook.react:react-native:+' 27 | } 28 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/heng/Desktop/android-sdk-macosx/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/PickerViewModule.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker; 2 | 3 | import android.content.res.AssetManager; 4 | import android.app.Activity; 5 | import android.app.Dialog; 6 | import android.graphics.Color; 7 | import android.graphics.PixelFormat; 8 | import android.graphics.Typeface; 9 | import android.support.annotation.Nullable; 10 | import android.text.TextUtils; 11 | import android.view.Gravity; 12 | import android.view.View; 13 | import android.view.Window; 14 | import android.view.WindowManager; 15 | import android.widget.RelativeLayout; 16 | import android.widget.TextView; 17 | import android.os.Build; 18 | 19 | import com.beefe.picker.util.MIUIUtils; 20 | import com.beefe.picker.view.OnSelectedListener; 21 | import com.beefe.picker.view.PickerViewAlone; 22 | import com.beefe.picker.view.PickerViewLinkage; 23 | import com.beefe.picker.view.ReturnData; 24 | import com.facebook.react.bridge.Arguments; 25 | import com.facebook.react.bridge.Callback; 26 | import com.facebook.react.bridge.LifecycleEventListener; 27 | import com.facebook.react.bridge.ReactApplicationContext; 28 | import com.facebook.react.bridge.ReactContext; 29 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 30 | import com.facebook.react.bridge.ReactMethod; 31 | import com.facebook.react.bridge.ReadableArray; 32 | import com.facebook.react.bridge.ReadableMap; 33 | import com.facebook.react.bridge.WritableArray; 34 | import com.facebook.react.bridge.WritableMap; 35 | import com.facebook.react.modules.core.DeviceEventManagerModule; 36 | 37 | import java.util.ArrayList; 38 | 39 | import static android.graphics.Color.argb; 40 | 41 | /** 42 | * Author: heng 43 | *

44 | * Created by heng on 16/9/5. 45 | *

46 | * Edited by heng on 16/9/22. 47 | * 1. PopupWindow height : full screen -> assignation 48 | * 2. Added pickerToolBarHeight support 49 | *

50 | * Edited by heng on 2016/10/19. 51 | * 1. Added weights support 52 | * 2. Fixed return data bug 53 | *

54 | * Edited by heng on 2016/11/16. 55 | * 1. Used WindowManager replace PopupWindow 56 | * 2. Removed method initOK() toggle() show() isPickerShow() 57 | * 3. Implements Application.ActivityLifecycleCallbacks 58 | *

59 | * Edited by heng on 2016/11/17 60 | * 1. Used Dialog replace WindowManger 61 | * 2. Restore method show() isPickerShow() 62 | *

63 | * Edited by heng on 2016/12/23 64 | * 1. Changed returnData type 65 | * 2. Added pickerToolBarFontSize 66 | *

67 | * Edited by heng on 2016/12/26 68 | * 1. Fixed returnData bug 69 | * 2. Added pickerFontColor 70 | * 3. Added pickerFontSize 71 | * 4. Used LifecycleEventListener replace Application.ActivityLifecycleCallbacks 72 | * 5. Fixed other bug 73 | * 74 | * Edited by heng on 2017/01/17 75 | * 1. Added select(ReadableArray array, Callback callback) 76 | * 2. Optimization code 77 | */ 78 | 79 | public class PickerViewModule extends ReactContextBaseJavaModule implements LifecycleEventListener { 80 | 81 | private static final String FONTS = "fonts/"; 82 | private static final String OTF = ".otf"; 83 | private static final String TTF = ".ttf"; 84 | 85 | private static final String REACT_CLASS = "BEEPickerManager"; 86 | 87 | private static final String PICKER_DATA = "pickerData"; 88 | private static final String SELECTED_VALUE = "selectedValue"; 89 | 90 | private static final String IS_LOOP = "isLoop"; 91 | 92 | private static final String WEIGHTS = "wheelFlex"; 93 | 94 | private static final String PICKER_BG_COLOR = "pickerBg"; 95 | 96 | private static final String PICKER_TOOL_BAR_BG = "pickerToolBarBg"; 97 | private static final String PICKER_TOOL_BAR_HEIGHT = "pickerToolBarHeight"; 98 | private static final String PICKER_TOOL_BAR_TEXT_SIZE = "pickerToolBarFontSize"; 99 | 100 | private static final String PICKER_CONFIRM_BTN_TEXT = "pickerConfirmBtnText"; 101 | private static final String PICKER_CONFIRM_BTN_COLOR = "pickerConfirmBtnColor"; 102 | 103 | private static final String PICKER_CANCEL_BTN_TEXT = "pickerCancelBtnText"; 104 | private static final String PICKER_CANCEL_BTN_COLOR = "pickerCancelBtnColor"; 105 | 106 | private static final String PICKER_TITLE_TEXT = "pickerTitleText"; 107 | private static final String PICKER_TITLE_TEXT_COLOR = "pickerTitleColor"; 108 | 109 | private static final String PICKER_TEXT_COLOR = "pickerFontColor"; 110 | private static final String PICKER_TEXT_SIZE = "pickerFontSize"; 111 | private static final String PICKER_TEXT_ELLIPSIS_LEN = "pickerTextEllipsisLen"; 112 | 113 | private static final String PICKER_FONT_FAMILY = "pickerFontFamily"; 114 | 115 | private static final String PICKER_EVENT_NAME = "pickerEvent"; 116 | private static final String EVENT_KEY_CONFIRM = "confirm"; 117 | private static final String EVENT_KEY_CANCEL = "cancel"; 118 | private static final String EVENT_KEY_SELECTED = "select"; 119 | 120 | private static final String ERROR_NOT_INIT = "please initialize the component first"; 121 | 122 | private Dialog dialog = null; 123 | 124 | private boolean isLoop = true; 125 | 126 | private String confirmText; 127 | private String cancelText; 128 | private String titleText; 129 | private int pickerTextEllipsisLen; 130 | 131 | private double[] weights; 132 | 133 | private ArrayList returnData; 134 | 135 | private int curStatus; 136 | 137 | private PickerViewLinkage pickerViewLinkage; 138 | private PickerViewAlone pickerViewAlone; 139 | 140 | public PickerViewModule(ReactApplicationContext reactContext) { 141 | super(reactContext); 142 | reactContext.addLifecycleEventListener(this); 143 | } 144 | 145 | @Override 146 | public String getName() { 147 | return REACT_CLASS; 148 | } 149 | 150 | @ReactMethod 151 | public void _init(ReadableMap options) { 152 | Activity activity = getCurrentActivity(); 153 | if (activity != null && options.hasKey(PICKER_DATA)) { 154 | View view = activity.getLayoutInflater().inflate(R.layout.picker_view, null); 155 | RelativeLayout barLayout = (RelativeLayout) view.findViewById(R.id.barLayout); 156 | TextView cancelTV = (TextView) view.findViewById(R.id.cancel); 157 | TextView titleTV = (TextView) view.findViewById(R.id.title); 158 | TextView confirmTV = (TextView) view.findViewById(R.id.confirm); 159 | RelativeLayout pickerLayout = (RelativeLayout) view.findViewById(R.id.pickerLayout); 160 | pickerViewLinkage = (PickerViewLinkage) view.findViewById(R.id.pickerViewLinkage); 161 | pickerViewAlone = (PickerViewAlone) view.findViewById(R.id.pickerViewAlone); 162 | 163 | int barViewHeight; 164 | if (options.hasKey(PICKER_TOOL_BAR_HEIGHT)) { 165 | try { 166 | barViewHeight = options.getInt(PICKER_TOOL_BAR_HEIGHT); 167 | } catch (Exception e) { 168 | barViewHeight = (int) options.getDouble(PICKER_TOOL_BAR_HEIGHT); 169 | } 170 | } else { 171 | barViewHeight = (int) (activity.getResources().getDisplayMetrics().density * 40); 172 | } 173 | RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( 174 | RelativeLayout.LayoutParams.MATCH_PARENT, 175 | barViewHeight); 176 | barLayout.setLayoutParams(params); 177 | 178 | if (options.hasKey(PICKER_TOOL_BAR_BG)) { 179 | ReadableArray array = options.getArray(PICKER_TOOL_BAR_BG); 180 | int[] colors = getColor(array); 181 | barLayout.setBackgroundColor(argb(colors[3], colors[0], colors[1], colors[2])); 182 | } 183 | 184 | if (options.hasKey(PICKER_TOOL_BAR_TEXT_SIZE)) { 185 | int toolBarTextSize = options.getInt(PICKER_TOOL_BAR_TEXT_SIZE); 186 | cancelTV.setTextSize(toolBarTextSize); 187 | titleTV.setTextSize(toolBarTextSize); 188 | confirmTV.setTextSize(toolBarTextSize); 189 | } 190 | 191 | if (options.hasKey(PICKER_CONFIRM_BTN_TEXT)) { 192 | confirmText = options.getString(PICKER_CONFIRM_BTN_TEXT); 193 | } 194 | confirmTV.setText(!TextUtils.isEmpty(confirmText) ? confirmText : ""); 195 | 196 | if (options.hasKey(PICKER_CONFIRM_BTN_COLOR)) { 197 | ReadableArray array = options.getArray(PICKER_CONFIRM_BTN_COLOR); 198 | int[] colors = getColor(array); 199 | confirmTV.setTextColor(argb(colors[3], colors[0], colors[1], colors[2])); 200 | } 201 | confirmTV.setOnClickListener(new View.OnClickListener() { 202 | @Override 203 | public void onClick(View v) { 204 | switch (curStatus) { 205 | case 0: 206 | returnData = pickerViewAlone.getSelectedData(); 207 | break; 208 | case 1: 209 | returnData = pickerViewLinkage.getSelectedData(); 210 | break; 211 | } 212 | commonEvent(EVENT_KEY_CONFIRM); 213 | hide(); 214 | } 215 | }); 216 | 217 | 218 | if (options.hasKey(PICKER_TITLE_TEXT)) { 219 | titleText = options.getString(PICKER_TITLE_TEXT); 220 | } 221 | titleTV.setText(!TextUtils.isEmpty(titleText) ? titleText : ""); 222 | if (options.hasKey(PICKER_TITLE_TEXT_COLOR)) { 223 | ReadableArray array = options.getArray(PICKER_TITLE_TEXT_COLOR); 224 | int[] colors = getColor(array); 225 | titleTV.setTextColor(argb(colors[3], colors[0], colors[1], colors[2])); 226 | } 227 | 228 | if (options.hasKey(PICKER_CANCEL_BTN_TEXT)) { 229 | cancelText = options.getString(PICKER_CANCEL_BTN_TEXT); 230 | } 231 | cancelTV.setText(!TextUtils.isEmpty(cancelText) ? cancelText : ""); 232 | if (options.hasKey(PICKER_CANCEL_BTN_COLOR)) { 233 | ReadableArray array = options.getArray(PICKER_CANCEL_BTN_COLOR); 234 | int[] colors = getColor(array); 235 | cancelTV.setTextColor(argb(colors[3], colors[0], colors[1], colors[2])); 236 | } 237 | cancelTV.setOnClickListener(new View.OnClickListener() { 238 | @Override 239 | public void onClick(View v) { 240 | switch (curStatus) { 241 | case 0: 242 | returnData = pickerViewAlone.getSelectedData(); 243 | break; 244 | case 1: 245 | returnData = pickerViewLinkage.getSelectedData(); 246 | break; 247 | } 248 | commonEvent(EVENT_KEY_CANCEL); 249 | hide(); 250 | } 251 | }); 252 | 253 | if(options.hasKey(PICKER_TEXT_ELLIPSIS_LEN)){ 254 | pickerTextEllipsisLen = options.getInt(PICKER_TEXT_ELLIPSIS_LEN); 255 | } 256 | 257 | if (options.hasKey(IS_LOOP)) { 258 | isLoop = options.getBoolean(IS_LOOP); 259 | } 260 | 261 | if (options.hasKey(WEIGHTS)) { 262 | ReadableArray array = options.getArray(WEIGHTS); 263 | weights = new double[array.size()]; 264 | for (int i = 0; i < array.size(); i++) { 265 | switch (array.getType(i).name()) { 266 | case "Number": 267 | try { 268 | weights[i] = array.getInt(i); 269 | } catch (Exception e) { 270 | weights[i] = array.getDouble(i); 271 | } 272 | break; 273 | case "String": 274 | try { 275 | weights[i] = Double.parseDouble(array.getString(i)); 276 | } catch (Exception e) { 277 | weights[i] = 1.0; 278 | } 279 | break; 280 | default: 281 | weights[i] = 1.0; 282 | break; 283 | } 284 | } 285 | } 286 | 287 | int pickerTextColor = 0xff000000; 288 | if (options.hasKey(PICKER_TEXT_COLOR)) { 289 | ReadableArray array = options.getArray(PICKER_TEXT_COLOR); 290 | int[] colors = getColor(array); 291 | pickerTextColor = Color.argb(colors[3], colors[0], colors[1], colors[2]); 292 | } 293 | 294 | int pickerTextSize = 16; 295 | if (options.hasKey(PICKER_TEXT_SIZE)) { 296 | try { 297 | pickerTextSize = options.getInt(PICKER_TEXT_SIZE); 298 | } catch (Exception e) { 299 | pickerTextSize = (int) options.getDouble(PICKER_TEXT_SIZE); 300 | } 301 | } 302 | 303 | ReadableArray pickerData = options.getArray(PICKER_DATA); 304 | 305 | int pickerViewHeight; 306 | String name = pickerData.getType(0).name(); 307 | switch (name) { 308 | case "Map": 309 | curStatus = 1; 310 | pickerViewLinkage.setVisibility(View.VISIBLE); 311 | pickerViewAlone.setVisibility(View.GONE); 312 | 313 | pickerViewLinkage.setPickerData(pickerData, weights); 314 | pickerViewLinkage.setTextColor(pickerTextColor); 315 | pickerViewLinkage.setTextSize(pickerTextSize); 316 | pickerViewLinkage.setTextEllipsisLen(pickerTextEllipsisLen); 317 | pickerViewLinkage.setIsLoop(isLoop); 318 | 319 | pickerViewLinkage.setOnSelectListener(new OnSelectedListener() { 320 | @Override 321 | public void onSelected(ArrayList selectedList) { 322 | returnData = selectedList; 323 | commonEvent(EVENT_KEY_SELECTED); 324 | } 325 | }); 326 | pickerViewHeight = pickerViewLinkage.getViewHeight(); 327 | break; 328 | default: 329 | curStatus = 0; 330 | pickerViewAlone.setVisibility(View.VISIBLE); 331 | pickerViewLinkage.setVisibility(View.GONE); 332 | 333 | pickerViewAlone.setPickerData(pickerData, weights); 334 | pickerViewAlone.setTextColor(pickerTextColor); 335 | pickerViewAlone.setTextSize(pickerTextSize); 336 | pickerViewAlone.setTextEllipsisLen(pickerTextEllipsisLen); 337 | pickerViewAlone.setIsLoop(isLoop); 338 | 339 | pickerViewAlone.setOnSelectedListener(new OnSelectedListener() { 340 | @Override 341 | public void onSelected(ArrayList selectedList) { 342 | returnData = selectedList; 343 | commonEvent(EVENT_KEY_SELECTED); 344 | } 345 | }); 346 | 347 | pickerViewHeight = pickerViewAlone.getViewHeight(); 348 | break; 349 | } 350 | 351 | if (options.hasKey(PICKER_FONT_FAMILY)) { 352 | Typeface typeface = null; 353 | AssetManager assetManager = activity.getApplicationContext().getAssets(); 354 | final String fontFamily = options.getString(PICKER_FONT_FAMILY); 355 | try { 356 | String path = FONTS + fontFamily + TTF; 357 | typeface = Typeface.createFromAsset(assetManager, path); 358 | } catch (Exception ignored) { 359 | try { 360 | String path = FONTS + fontFamily + OTF; 361 | typeface = Typeface.createFromAsset(assetManager, path); 362 | } catch (Exception ignored2) { 363 | try { 364 | typeface = Typeface.create(fontFamily, Typeface.NORMAL); 365 | } catch (Exception ignored3) { 366 | } 367 | } 368 | } 369 | cancelTV.setTypeface(typeface); 370 | titleTV.setTypeface(typeface); 371 | confirmTV.setTypeface(typeface); 372 | 373 | pickerViewAlone.setTypeface(typeface); 374 | pickerViewLinkage.setTypeface(typeface); 375 | } 376 | 377 | if (options.hasKey(SELECTED_VALUE)) { 378 | ReadableArray array = options.getArray(SELECTED_VALUE); 379 | String[] selectedValue = getSelectedValue(array); 380 | select(selectedValue); 381 | } 382 | 383 | if (options.hasKey(PICKER_BG_COLOR)) { 384 | ReadableArray array = options.getArray(PICKER_BG_COLOR); 385 | int[] colors = getColor(array); 386 | pickerLayout.setBackgroundColor(argb(colors[3], colors[0], colors[1], colors[2])); 387 | } 388 | 389 | int height = barViewHeight + pickerViewHeight; 390 | if (dialog == null) { 391 | dialog = new Dialog(activity, R.style.Dialog_Full_Screen); 392 | dialog.setContentView(view); 393 | WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); 394 | Window window = dialog.getWindow(); 395 | if (window != null) { 396 | if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 397 | window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 398 | }else{ 399 | if (MIUIUtils.isMIUI()) { 400 | layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION; 401 | }else { 402 | //layoutParams.type = WindowManager.LayoutParams.TYPE_TOAST; 403 | } 404 | } 405 | layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 406 | layoutParams.format = PixelFormat.TRANSPARENT; 407 | layoutParams.windowAnimations = R.style.PickerAnim; 408 | layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; 409 | layoutParams.height = height; 410 | layoutParams.gravity = Gravity.BOTTOM; 411 | window.setAttributes(layoutParams); 412 | } 413 | } else { 414 | dialog.dismiss(); 415 | dialog.setContentView(view); 416 | } 417 | } 418 | } 419 | 420 | @ReactMethod 421 | public void select(ReadableArray array, Callback callback) { 422 | if (dialog == null) { 423 | if (callback != null) { 424 | callback.invoke(ERROR_NOT_INIT); 425 | } 426 | return; 427 | } 428 | String[] selectedValue = getSelectedValue(array); 429 | select(selectedValue); 430 | } 431 | 432 | @ReactMethod 433 | public void show() { 434 | if (dialog == null) { 435 | return; 436 | } 437 | if (!dialog.isShowing()) { 438 | dialog.show(); 439 | } 440 | } 441 | 442 | @ReactMethod 443 | public void hide() { 444 | if (dialog == null) { 445 | return; 446 | } 447 | if (dialog.isShowing()) { 448 | dialog.dismiss(); 449 | } 450 | } 451 | 452 | @ReactMethod 453 | public void isPickerShow(Callback callback) { 454 | if (callback == null) 455 | return; 456 | if (dialog == null) { 457 | callback.invoke(ERROR_NOT_INIT); 458 | } else { 459 | callback.invoke(null, dialog.isShowing()); 460 | } 461 | } 462 | 463 | private int[] getColor(ReadableArray array) { 464 | int[] colors = new int[4]; 465 | for (int i = 0; i < array.size(); i++) { 466 | switch (i) { 467 | case 0: 468 | case 1: 469 | case 2: 470 | colors[i] = array.getInt(i); 471 | break; 472 | case 3: 473 | colors[i] = (int) (array.getDouble(i) * 255); 474 | break; 475 | default: 476 | break; 477 | } 478 | } 479 | return colors; 480 | } 481 | 482 | private String[] getSelectedValue(ReadableArray array) { 483 | String[] selectValue = new String[array.size()]; 484 | String value = ""; 485 | for (int i = 0; i < array.size(); i++) { 486 | switch (array.getType(i).name()) { 487 | case "Boolean": 488 | value = String.valueOf(array.getBoolean(i)); 489 | break; 490 | case "Number": 491 | try { 492 | value = String.valueOf(array.getInt(i)); 493 | } catch (Exception e) { 494 | value = String.valueOf(array.getDouble(i)); 495 | } 496 | break; 497 | case "String": 498 | value = array.getString(i); 499 | break; 500 | } 501 | selectValue[i] = value; 502 | } 503 | return selectValue; 504 | } 505 | 506 | private void select(String[] selectedValue) { 507 | switch (curStatus) { 508 | case 0: 509 | pickerViewAlone.setSelectValue(selectedValue); 510 | break; 511 | case 1: 512 | pickerViewLinkage.setSelectValue(selectedValue); 513 | break; 514 | } 515 | } 516 | 517 | private void commonEvent(String eventKey) { 518 | WritableMap map = Arguments.createMap(); 519 | map.putString("type", eventKey); 520 | WritableArray indexes = Arguments.createArray(); 521 | WritableArray values = Arguments.createArray(); 522 | for (ReturnData data : returnData) { 523 | indexes.pushInt(data.getIndex()); 524 | values.pushString(data.getItem()); 525 | } 526 | map.putArray("selectedValue", values); 527 | map.putArray("selectedIndex", indexes); 528 | sendEvent(getReactApplicationContext(), PICKER_EVENT_NAME, map); 529 | } 530 | 531 | private void sendEvent(ReactContext reactContext, 532 | String eventName, 533 | @Nullable WritableMap params) { 534 | reactContext 535 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 536 | .emit(eventName, params); 537 | } 538 | 539 | @Override 540 | public void onHostResume() { 541 | 542 | } 543 | 544 | @Override 545 | public void onHostPause() { 546 | hide(); 547 | dialog = null; 548 | } 549 | 550 | @Override 551 | public void onHostDestroy() { 552 | hide(); 553 | dialog = null; 554 | } 555 | } 556 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/PickerViewPackage.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker; 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 | * Created by heng on 16/9/5. 15 | */ 16 | 17 | public class PickerViewPackage implements ReactPackage { 18 | 19 | @Override 20 | public List createNativeModules(ReactApplicationContext reactContext) { 21 | return Arrays.asList(new PickerViewModule(reactContext)); 22 | } 23 | 24 | // Deprecated RN 0.47 25 | public List> createJSModules() { 26 | return Collections.emptyList(); 27 | } 28 | 29 | @Override 30 | public List createViewManagers(ReactApplicationContext reactContext) { 31 | return Collections.emptyList(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/util/BuildProperties.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.util; 2 | 3 | /** 4 | * Created by geezer. on 2017/9/21. 5 | */ 6 | 7 | import android.os.Environment; 8 | 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.IOException; 12 | import java.util.Collection; 13 | import java.util.Enumeration; 14 | import java.util.Map.Entry; 15 | import java.util.Properties; 16 | import java.util.Set; 17 | 18 | 19 | public class BuildProperties { 20 | 21 | private final Properties properties; 22 | 23 | private BuildProperties() throws IOException { 24 | properties = new Properties(); 25 | properties.load(new FileInputStream(new File(Environment.getRootDirectory(), "build.prop"))); 26 | } 27 | 28 | public boolean containsKey(final Object key) { 29 | return properties.containsKey(key); 30 | } 31 | 32 | public boolean containsValue(final Object value) { 33 | return properties.containsValue(value); 34 | } 35 | 36 | public Set> entrySet() { 37 | return properties.entrySet(); 38 | } 39 | 40 | public String getProperty(final String name) { 41 | return properties.getProperty(name); 42 | } 43 | 44 | public String getProperty(final String name, final String defaultValue) { 45 | return properties.getProperty(name, defaultValue); 46 | } 47 | 48 | public boolean isEmpty() { 49 | return properties.isEmpty(); 50 | } 51 | 52 | public Enumeration keys() { 53 | return properties.keys(); 54 | } 55 | 56 | public Set keySet() { 57 | return properties.keySet(); 58 | } 59 | 60 | public int size() { 61 | return properties.size(); 62 | } 63 | 64 | public Collection values() { 65 | return properties.values(); 66 | } 67 | 68 | public static BuildProperties newInstance() throws IOException { 69 | return new BuildProperties(); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/util/MIUIUtils.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.util; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Created by geezer. on 2017/9/21. 7 | */ 8 | 9 | public final class MIUIUtils { 10 | 11 | private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code"; 12 | private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name"; 13 | private static final String KEY_MIUI_INTERNAL_STORAGE = "ro.miui.internal.storage"; 14 | 15 | public static boolean isMIUI() { 16 | try { 17 | final BuildProperties prop = BuildProperties.newInstance(); 18 | return prop.getProperty(KEY_MIUI_VERSION_CODE, null) != null 19 | || prop.getProperty(KEY_MIUI_VERSION_NAME, null) != null 20 | || prop.getProperty(KEY_MIUI_INTERNAL_STORAGE, null) != null; 21 | } catch (final IOException e) { 22 | return false; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/InertiaTimerTask.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | import java.util.TimerTask; 4 | 5 | final class InertiaTimerTask extends TimerTask { 6 | 7 | float a; 8 | final float velocityY; 9 | final LoopView loopView; 10 | 11 | InertiaTimerTask(LoopView loopview, float velocityY) { 12 | super(); 13 | loopView = loopview; 14 | this.velocityY = velocityY; 15 | a = Integer.MAX_VALUE; 16 | } 17 | 18 | @Override 19 | public final void run() { 20 | if (a == Integer.MAX_VALUE) { 21 | if (Math.abs(velocityY) > 2000F) { 22 | if (velocityY > 0.0F) { 23 | a = 2000F; 24 | } else { 25 | a = -2000F; 26 | } 27 | } else { 28 | a = velocityY; 29 | } 30 | } 31 | if (Math.abs(a) >= 0.0F && Math.abs(a) <= 20F) { 32 | loopView.cancelFuture(); 33 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_SMOOTH_SCROLL); 34 | return; 35 | } 36 | int i = (int) ((a * 10F) / 1000F); 37 | LoopView loopview = loopView; 38 | loopview.totalScrollY = loopview.totalScrollY - i; 39 | if (!loopView.isLoop) { 40 | float itemHeight = loopView.lineSpacingMultiplier * loopView.maxTextHeight; 41 | if (loopView.totalScrollY <= (int) ((float) (-loopView.initPosition) * itemHeight)) { 42 | a = 40F; 43 | loopView.totalScrollY = (int) ((float) (-loopView.initPosition) * itemHeight); 44 | } else if (loopView.totalScrollY >= (int) ((float) (loopView.items.size() - 1 - loopView.initPosition) * itemHeight)) { 45 | loopView.totalScrollY = (int) ((float) (loopView.items.size() - 1 - loopView.initPosition) * itemHeight); 46 | a = -40F; 47 | } 48 | } 49 | if (a < 0.0F) { 50 | a = a + 20F; 51 | } else { 52 | a = a - 20F; 53 | } 54 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/LoopView.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.graphics.Rect; 7 | import android.graphics.RectF; 8 | import android.graphics.Typeface; 9 | import android.os.Handler; 10 | import android.util.AttributeSet; 11 | import android.view.GestureDetector; 12 | import android.view.MotionEvent; 13 | import android.view.View; 14 | 15 | import java.util.List; 16 | import java.util.concurrent.Executors; 17 | import java.util.concurrent.ScheduledExecutorService; 18 | import java.util.concurrent.ScheduledFuture; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | /** 22 | * Edited by heng on 2016/10/20 23 | * 1. Added method getY 24 | * 2. Changed line color 0xffc5c5c5 -> 0xffb8bbc2 25 | * 26 | * Edited by heng on 2016/12/26 27 | * 1. Added setTextColor 28 | * 2. Added setTextSize 29 | */ 30 | public class LoopView extends View { 31 | 32 | private float scaleX = 1.05F; 33 | 34 | enum ACTION { 35 | // 点击,滑翔(滑到尽头),拖拽事件 36 | CLICK, FLING, DRAG 37 | } 38 | 39 | private Context context; 40 | 41 | Handler handler; 42 | private GestureDetector gestureDetector; 43 | OnItemSelectedListener onItemSelectedListener; 44 | 45 | private ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor(); 46 | private ScheduledFuture mFuture; 47 | 48 | private Paint paintOuterText; 49 | private Paint paintCenterText; 50 | private Paint paintIndicator; 51 | 52 | List items; 53 | 54 | private int textSize; 55 | int maxTextHeight; 56 | 57 | // 条目间距倍数 58 | float lineSpacingMultiplier; 59 | boolean isLoop; 60 | 61 | // 第一条线Y坐标值 62 | private int firstLineY; 63 | private int secondLineY; 64 | 65 | int totalScrollY; 66 | int initPosition; 67 | private String selectedItem; 68 | private int selectedIndex; 69 | private int preCurrentIndex; 70 | private int textEllipsisLen = 7; 71 | 72 | 73 | // 显示几个条目 74 | private int itemsVisible; 75 | 76 | private int measuredHeight; 77 | 78 | // 半圆周长 79 | private int halfCircumference; 80 | // 半径 81 | private int radius; 82 | 83 | private int mOffset = 0; 84 | private float previousY; 85 | private long startTime = 0; 86 | 87 | private Rect tempRect = new Rect(); 88 | 89 | public LoopView(Context context) { 90 | super(context); 91 | initLoopView(context); 92 | } 93 | 94 | public LoopView(Context context, AttributeSet attributeset) { 95 | super(context, attributeset); 96 | initLoopView(context); 97 | } 98 | 99 | public LoopView(Context context, AttributeSet attributeset, int defStyleAttr) { 100 | super(context, attributeset, defStyleAttr); 101 | initLoopView(context); 102 | } 103 | 104 | private void initLoopView(Context context) { 105 | this.context = context; 106 | handler = new MessageHandler(this); 107 | gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this)); 108 | gestureDetector.setIsLongpressEnabled(false); 109 | 110 | lineSpacingMultiplier = 2.0F; 111 | isLoop = true; 112 | itemsVisible = 9; 113 | textSize = (int) (context.getResources().getDisplayMetrics().density * 16); 114 | 115 | totalScrollY = 0; 116 | initPosition = -1; 117 | 118 | initPaints(); 119 | } 120 | 121 | private void initPaints() { 122 | paintOuterText = new Paint(); 123 | paintOuterText.setColor(0xffafafaf); 124 | paintOuterText.setAntiAlias(true); 125 | paintOuterText.setTypeface(Typeface.MONOSPACE); 126 | paintOuterText.setTextSize(textSize); 127 | 128 | paintCenterText = new Paint(); 129 | paintCenterText.setColor(0xff000000); 130 | paintCenterText.setAntiAlias(true); 131 | paintCenterText.setTextScaleX(scaleX); 132 | paintCenterText.setTypeface(Typeface.MONOSPACE); 133 | paintCenterText.setTextSize(textSize); 134 | 135 | paintIndicator = new Paint(); 136 | paintIndicator.setColor(0xffb8bbc2); 137 | paintIndicator.setAntiAlias(true); 138 | 139 | if (android.os.Build.VERSION.SDK_INT >= 11) { 140 | setLayerType(LAYER_TYPE_SOFTWARE, null); 141 | } 142 | } 143 | 144 | @Override 145 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 146 | remeasure(); 147 | setMeasuredDimension(widthMeasureSpec, measuredHeight); 148 | } 149 | 150 | private void remeasure() { 151 | if (items == null) { 152 | return; 153 | } 154 | maxTextHeight = textSize; 155 | 156 | halfCircumference = (int) (maxTextHeight * lineSpacingMultiplier * (itemsVisible - 1)); 157 | measuredHeight = (int) ((halfCircumference * 2) / Math.PI); 158 | radius = (int) (halfCircumference / Math.PI); 159 | firstLineY = (int) ((measuredHeight - lineSpacingMultiplier * maxTextHeight) / 2.0F); 160 | secondLineY = (int) ((measuredHeight + lineSpacingMultiplier * maxTextHeight) / 2.0F); 161 | if (initPosition == -1) { 162 | if (isLoop) { 163 | initPosition = (items.size() + 1) / 2; 164 | } else { 165 | initPosition = 0; 166 | } 167 | } 168 | 169 | preCurrentIndex = initPosition; 170 | } 171 | 172 | void smoothScroll(ACTION action) { 173 | cancelFuture(); 174 | if (action == ACTION.FLING || action == ACTION.DRAG) { 175 | float itemHeight = lineSpacingMultiplier * maxTextHeight; 176 | mOffset = (int) ((totalScrollY % itemHeight + itemHeight) % itemHeight); 177 | if ((float) mOffset > itemHeight / 2.0F) { 178 | mOffset = (int) (itemHeight - (float) mOffset); 179 | } else { 180 | mOffset = -mOffset; 181 | } 182 | } 183 | mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10, TimeUnit.MILLISECONDS); 184 | } 185 | 186 | 187 | protected final void scrollBy(float velocityY) { 188 | cancelFuture(); 189 | // 修改这个值可以改变滑行速度 190 | int velocityFling = 10; 191 | mFuture = mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, velocityFling, TimeUnit.MILLISECONDS); 192 | } 193 | 194 | public void cancelFuture() { 195 | if (mFuture != null && !mFuture.isCancelled()) { 196 | mFuture.cancel(true); 197 | mFuture = null; 198 | } 199 | } 200 | 201 | public void setTextColor(int color){ 202 | paintCenterText.setColor(color); 203 | invalidate(); 204 | } 205 | 206 | public void setTypeface(Typeface typeface){ 207 | paintOuterText.setTypeface(typeface); 208 | paintCenterText.setTypeface(typeface); 209 | invalidate(); 210 | } 211 | 212 | public final void setNotLoop() { 213 | isLoop = false; 214 | } 215 | 216 | public final void setTextSize(float size) { 217 | if (size > 0.0F) { 218 | this.textSize = (int) (context.getResources().getDisplayMetrics().density * size); 219 | paintOuterText.setTextSize(textSize); 220 | paintCenterText.setTextSize(textSize); 221 | remeasure(); 222 | invalidate(); 223 | } 224 | } 225 | 226 | public final void setTextEllipsisLen(int len){ 227 | textEllipsisLen = len; 228 | } 229 | 230 | public boolean hasItem(String item) { 231 | int result = items.indexOf(item); 232 | return result != -1; 233 | } 234 | 235 | public void setSelectedItem(String item) { 236 | int selectedIndex = items.indexOf(item); 237 | setSelectedPosition(selectedIndex); 238 | } 239 | 240 | public int getItemPosition(String item) { 241 | return items.indexOf(item); 242 | } 243 | 244 | public int getViewHeight(){ 245 | return measuredHeight; 246 | } 247 | 248 | public final void setSelectedPosition(int initPosition) { 249 | if (initPosition < 0) { 250 | this.initPosition = 0; 251 | } else { 252 | if (items != null && items.size() > initPosition) { 253 | this.initPosition = initPosition; 254 | } 255 | } 256 | selectedIndex = initPosition; 257 | totalScrollY = 0; 258 | cancelFuture(); 259 | invalidate(); 260 | } 261 | 262 | public final void setListener(OnItemSelectedListener OnItemSelectedListener) { 263 | onItemSelectedListener = OnItemSelectedListener; 264 | } 265 | 266 | public final void setItems(List items) { 267 | this.items = items; 268 | remeasure(); 269 | invalidate(); 270 | } 271 | 272 | public String getIndexItem(int index) { 273 | return items.get(index); 274 | } 275 | public String getSelectedItem() { 276 | return selectedItem; 277 | } 278 | 279 | public final int getSelectedIndex() { 280 | return selectedIndex; 281 | } 282 | 283 | protected final void onItemSelected() { 284 | if (onItemSelectedListener != null) { 285 | postDelayed(new OnItemSelectedRunnable(this), 200L); 286 | } 287 | } 288 | 289 | protected final void drawText(Canvas canvas, String text, float posX, float posY, Paint paint) { 290 | StringBuffer stringBuffer = new StringBuffer(); 291 | char[] array = text.toCharArray(); 292 | int sum = 0; 293 | for(int i=0;i= (textEllipsisLen * 2)){ 295 | break; 296 | } 297 | char bt = array[i]; 298 | if(bt > 127 || bt == 94){ 299 | sum += 2; 300 | } 301 | else{ 302 | sum ++; 303 | } 304 | stringBuffer.append(String.valueOf(bt)); 305 | } 306 | String string = ""; 307 | if(array.length != stringBuffer.toString().toCharArray().length){ 308 | string = stringBuffer.toString() + "..."; 309 | } 310 | else{ 311 | string = text; 312 | } 313 | canvas.drawText(string, posX, posY, paint); 314 | } 315 | 316 | @Override 317 | protected void onDraw(Canvas canvas) { 318 | if (items == null) { 319 | return; 320 | } 321 | 322 | String as[] = new String[itemsVisible]; 323 | int change = (int) (totalScrollY / (lineSpacingMultiplier * maxTextHeight)); 324 | preCurrentIndex = initPosition + change % items.size(); 325 | 326 | if (!isLoop) { 327 | if (preCurrentIndex < 0) { 328 | preCurrentIndex = 0; 329 | } 330 | if (preCurrentIndex > items.size() - 1) { 331 | preCurrentIndex = items.size() - 1; 332 | } 333 | } else { 334 | if (preCurrentIndex < 0) { 335 | preCurrentIndex = items.size() + preCurrentIndex; 336 | } 337 | if (preCurrentIndex > items.size() - 1) { 338 | preCurrentIndex = preCurrentIndex - items.size(); 339 | } 340 | } 341 | 342 | int j2 = (int) (totalScrollY % (lineSpacingMultiplier * maxTextHeight)); 343 | // 设置as数组中每个元素的值 344 | int k1 = 0; 345 | while (k1 < itemsVisible) { 346 | int l1 = preCurrentIndex - (itemsVisible / 2 - k1); 347 | if (isLoop) { 348 | while (l1 < 0) { 349 | l1 = l1 + items.size(); 350 | } 351 | while (l1 > items.size() - 1) { 352 | l1 = l1 - items.size(); 353 | } 354 | as[k1] = items.get(l1); 355 | } else if (l1 < 0) { 356 | as[k1] = ""; 357 | } else if (l1 > items.size() - 1) { 358 | as[k1] = ""; 359 | } else { 360 | as[k1] = items.get(l1); 361 | } 362 | k1++; 363 | } 364 | canvas.drawLine(0.0F, firstLineY, getWidth(), firstLineY, paintIndicator); 365 | canvas.drawLine(0.0F, secondLineY, getWidth(), secondLineY, paintIndicator); 366 | 367 | int j1 = 0; 368 | while (j1 < itemsVisible) { 369 | canvas.save(); 370 | // L(弧长)=α(弧度)* r(半径) (弧度制) 371 | // 求弧度--> (L * π ) / (π * r) (弧长X派/半圆周长) 372 | float itemHeight = maxTextHeight * lineSpacingMultiplier; 373 | double radian = ((itemHeight * j1 - j2) * Math.PI) / halfCircumference; 374 | // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限 375 | float angle = (float) (90D - (radian / Math.PI) * 180D); 376 | if (angle >= 90F || angle <= -90F) { 377 | canvas.restore(); 378 | } else { 379 | int translateY = (int) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D); 380 | canvas.translate(0.0F, translateY); 381 | canvas.scale(1.0F, (float) Math.sin(radian)); 382 | String text = as[j1]; 383 | if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) { 384 | // 条目经过第一条线 385 | canvas.save(); 386 | canvas.clipRect(0, 0, getWidth(), firstLineY - translateY); 387 | drawText(canvas, text, getX(text, paintOuterText), getY(paintOuterText), paintOuterText); 388 | canvas.restore(); 389 | canvas.save(); 390 | canvas.clipRect(0, firstLineY - translateY, getWidth(), (int) (itemHeight)); 391 | drawText(canvas, text, getX(text, paintCenterText), getY(paintCenterText), paintCenterText); 392 | canvas.restore(); 393 | } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) { 394 | // 条目经过第二条线 395 | canvas.save(); 396 | canvas.clipRect(0, 0, getWidth(), secondLineY - translateY); 397 | drawText(canvas, text, getX(text, paintCenterText), getY(paintCenterText), paintCenterText); 398 | canvas.restore(); 399 | canvas.save(); 400 | canvas.clipRect(0, secondLineY - translateY, getWidth(), (int) (itemHeight)); 401 | drawText(canvas, text, getX(text, paintOuterText), getY(paintOuterText), paintOuterText); 402 | canvas.restore(); 403 | } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) { 404 | // 中间条目 405 | canvas.clipRect(0, 0, getWidth(), (int) (itemHeight)); 406 | drawText(canvas, text, getX(text, paintCenterText), getY(paintCenterText), paintCenterText); 407 | selectedItem = text; 408 | selectedIndex = items.indexOf(text); 409 | } else { 410 | // 其他条目 411 | canvas.clipRect(0, 0, getWidth(), (int) (itemHeight)); 412 | drawText(canvas, text, getX(text, paintOuterText), getY(paintOuterText), paintOuterText); 413 | } 414 | canvas.restore(); 415 | } 416 | j1++; 417 | } 418 | } 419 | 420 | private float getX(String text, Paint paint) { 421 | paint.getTextBounds(text, 0, text.length(), tempRect); 422 | //return (getWidth() - tempRect.width() * scaleX) / 2; 423 | if((getWidth() - tempRect.width() * scaleX)/2 > 0){ 424 | return (getWidth() - tempRect.width() * scaleX) / 2; 425 | } 426 | else{ 427 | return 0; 428 | } 429 | } 430 | 431 | /** 432 | * Added by shexiaoheng 433 | * 让字体垂直方向居中 434 | * */ 435 | private float getY(Paint paint) { 436 | Rect rect = new Rect(0, 0, getWidth(), maxTextHeight); 437 | RectF bounds = new RectF(rect); 438 | bounds.bottom = paint.descent() - paint.ascent(); 439 | bounds.top += (rect.height() - bounds.bottom) / 2.0f; 440 | return bounds.top - paint.ascent(); 441 | } 442 | 443 | 444 | @Override 445 | public boolean onTouchEvent(MotionEvent event) { 446 | boolean eventConsumed = gestureDetector.onTouchEvent(event); 447 | float itemHeight = lineSpacingMultiplier * maxTextHeight; 448 | 449 | switch (event.getAction()) { 450 | case MotionEvent.ACTION_DOWN: 451 | startTime = System.currentTimeMillis(); 452 | cancelFuture(); 453 | previousY = event.getRawY(); 454 | break; 455 | 456 | case MotionEvent.ACTION_MOVE: 457 | float dy = previousY - event.getRawY(); 458 | previousY = event.getRawY(); 459 | 460 | totalScrollY = (int) (totalScrollY + dy); 461 | 462 | // 边界处理。 463 | if (!isLoop) { 464 | float top = -initPosition * itemHeight; 465 | float bottom = (items.size() - 1 - initPosition) * itemHeight; 466 | 467 | if (totalScrollY < top) { 468 | totalScrollY = (int) top; 469 | } else if (totalScrollY > bottom) { 470 | totalScrollY = (int) bottom; 471 | } 472 | } 473 | break; 474 | 475 | case MotionEvent.ACTION_UP: 476 | default: 477 | if (!eventConsumed) { 478 | float y = event.getY(); 479 | double l = Math.acos((radius - y) / radius) * radius; 480 | int circlePosition = (int) ((l + itemHeight / 2) / itemHeight); 481 | 482 | float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight; 483 | mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset); 484 | 485 | if ((System.currentTimeMillis() - startTime) > 120) { 486 | // 处理拖拽事件 487 | smoothScroll(ACTION.DRAG); 488 | } else { 489 | // 处理条目点击事件 490 | smoothScroll(ACTION.CLICK); 491 | } 492 | } 493 | break; 494 | } 495 | invalidate(); 496 | return true; 497 | } 498 | } -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/LoopViewGestureListener.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | import android.view.MotionEvent; 4 | 5 | final class LoopViewGestureListener extends android.view.GestureDetector.SimpleOnGestureListener { 6 | 7 | final LoopView loopView; 8 | 9 | LoopViewGestureListener(LoopView loopview) { 10 | loopView = loopview; 11 | } 12 | 13 | @Override 14 | public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 15 | loopView.scrollBy(velocityY); 16 | return true; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/MessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | import android.os.Handler; 4 | import android.os.Message; 5 | 6 | final class MessageHandler extends Handler { 7 | 8 | public static final int WHAT_INVALIDATE_LOOP_VIEW = 1000; 9 | public static final int WHAT_SMOOTH_SCROLL = 2000; 10 | public static final int WHAT_ITEM_SELECTED = 3000; 11 | 12 | final LoopView loopview; 13 | 14 | MessageHandler(LoopView loopview) { 15 | this.loopview = loopview; 16 | } 17 | 18 | @Override 19 | public final void handleMessage(Message msg) { 20 | switch (msg.what) { 21 | case WHAT_INVALIDATE_LOOP_VIEW: 22 | loopview.invalidate(); 23 | break; 24 | 25 | case WHAT_SMOOTH_SCROLL: 26 | loopview.smoothScroll(LoopView.ACTION.FLING); 27 | break; 28 | 29 | case WHAT_ITEM_SELECTED: 30 | loopview.onItemSelected(); 31 | break; 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/OnItemSelectedListener.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | 4 | public interface OnItemSelectedListener { 5 | void onItemSelected(String item, int index); 6 | } 7 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/OnItemSelectedRunnable.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | final class OnItemSelectedRunnable implements Runnable { 4 | 5 | final LoopView loopView; 6 | 7 | OnItemSelectedRunnable(LoopView loopview) { 8 | loopView = loopview; 9 | } 10 | 11 | @Override 12 | public final void run() { 13 | loopView.onItemSelectedListener.onItemSelected(loopView.getSelectedItem(),loopView.getSelectedIndex()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/OnSelectedListener.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by heng on 16/9/6. 7 | */ 8 | 9 | public interface OnSelectedListener { 10 | 11 | void onSelected(ArrayList selectedList); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/PickerViewAlone.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.widget.LinearLayout; 9 | 10 | import com.beefe.picker.R; 11 | import com.facebook.react.bridge.ReadableArray; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | 16 | /** 17 | * Created by heng on 16/9/6. 18 | *

19 | * Edited by heng on 16/10/09: 20 | * 修复滚动后返回值错误的bug 21 | * 22 | * Edited by heng on 2016/12/26 23 | * 1. Fixed returnData bug 24 | * 2. Added LoopView TextColor and TextSize support 25 | */ 26 | 27 | public class PickerViewAlone extends LinearLayout { 28 | 29 | private LinearLayout pickerViewAloneLayout; 30 | 31 | private OnSelectedListener onSelectedListener; 32 | 33 | private ArrayList curSelectedList; 34 | 35 | public PickerViewAlone(Context context) { 36 | super(context); 37 | init(context); 38 | } 39 | 40 | public PickerViewAlone(Context context, AttributeSet attrs) { 41 | super(context, attrs); 42 | init(context); 43 | } 44 | 45 | private void init(Context context) { 46 | View view = LayoutInflater.from(context).inflate(R.layout.picker_view_alone, this); 47 | pickerViewAloneLayout = (LinearLayout) view.findViewById(R.id.pickerViewAloneLayout); 48 | } 49 | 50 | public void setOnSelectedListener(OnSelectedListener listener) { 51 | this.onSelectedListener = listener; 52 | } 53 | 54 | public void setPickerData(ReadableArray array, double[] weights) { 55 | curSelectedList = new ArrayList<>(); 56 | switch (array.getType(0).name()) { 57 | case "Array": 58 | setMultipleData(array, weights); 59 | break; 60 | default: 61 | setAloneData(array); 62 | break; 63 | } 64 | } 65 | 66 | public ArrayList getSelectedData() { 67 | return this.curSelectedList; 68 | } 69 | 70 | private void setAloneData(ReadableArray array) { 71 | ArrayList values = arrayToList(array); 72 | final LoopView loopView = new LoopView(getContext()); 73 | LayoutParams params = new LayoutParams( 74 | LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 75 | params.weight = 1.0f; 76 | loopView.setLayoutParams(params); 77 | loopView.setItems(values); 78 | loopView.setSelectedPosition(0); 79 | ReturnData returnData = new ReturnData(); 80 | returnData.setItem(values.get(0)); 81 | returnData.setIndex(loopView.getSelectedIndex()); 82 | if (curSelectedList.size() > 0) { 83 | curSelectedList.set(0, returnData); 84 | } else { 85 | curSelectedList.add(0, returnData); 86 | } 87 | loopView.setListener(new OnItemSelectedListener() { 88 | @Override 89 | public void onItemSelected(String item, int index) { 90 | if (onSelectedListener != null) { 91 | ReturnData returnData1 = new ReturnData(); 92 | returnData1.setItem(item); 93 | returnData1.setIndex(index); 94 | curSelectedList.set(0, returnData1); 95 | onSelectedListener.onSelected(curSelectedList); 96 | } 97 | } 98 | }); 99 | pickerViewAloneLayout.addView(loopView); 100 | } 101 | 102 | private void setMultipleData(ReadableArray array, double[] weights) { 103 | final String[] selectedItems = new String[array.size()]; 104 | final int[] selectedIndexes = new int[array.size()]; 105 | for (int i = 0; i < array.size(); i++) { 106 | switch (array.getType(i).name()) { 107 | case "Array": 108 | ReadableArray childArray = array.getArray(i); 109 | ArrayList values = arrayToList(childArray); 110 | final LoopView loopView = new LoopView(getContext()); 111 | LayoutParams params = new LayoutParams(0, LayoutParams.MATCH_PARENT); 112 | if (weights != null) { 113 | if (i < weights.length) { 114 | params.weight = (float) weights[i]; 115 | } else { 116 | params.weight = 1.0f; 117 | } 118 | } else { 119 | params.weight = 1.0f; 120 | } 121 | loopView.setLayoutParams(params); 122 | loopView.setItems(values); 123 | loopView.setTag(i); 124 | loopView.setSelectedPosition(0); 125 | 126 | ReturnData returnData = new ReturnData(); 127 | returnData.setItem(values.get(0)); 128 | returnData.setIndex(loopView.getSelectedIndex()); 129 | if (curSelectedList.size() > i) { 130 | curSelectedList.set(i, returnData); 131 | } else { 132 | curSelectedList.add(i, returnData); 133 | } 134 | selectedItems[i] = values.get(0); 135 | loopView.setListener(new OnItemSelectedListener() { 136 | @Override 137 | public void onItemSelected(String item, int index) { 138 | int viewCount = pickerViewAloneLayout.getChildCount(); 139 | for (int k = 0; k < viewCount; k++) { 140 | View view = pickerViewAloneLayout.getChildAt(k); 141 | if (view instanceof LoopView) { 142 | LoopView loop = (LoopView) view; 143 | selectedItems[k] = loop.getSelectedItem(); 144 | selectedIndexes[k] = loop.getSelectedIndex(); 145 | } 146 | } 147 | 148 | if (onSelectedListener != null) { 149 | for (int i = 0; i < selectedItems.length; i++) { 150 | ReturnData returnData1 = new ReturnData(); 151 | returnData1.setItem(selectedItems[i]); 152 | returnData1.setIndex(selectedIndexes[i]); 153 | curSelectedList.set(i, returnData1); 154 | } 155 | onSelectedListener.onSelected(curSelectedList); 156 | } 157 | } 158 | }); 159 | pickerViewAloneLayout.addView(loopView); 160 | break; 161 | default: 162 | break; 163 | } 164 | } 165 | } 166 | 167 | public void setSelectValue(String[] selectValue) { 168 | int viewCount = pickerViewAloneLayout.getChildCount(); 169 | int valueCount = selectValue.length; 170 | if (valueCount <= viewCount) { 171 | setSelect(valueCount, selectValue, curSelectedList); 172 | } else { 173 | String[] values = Arrays.copyOf(selectValue, viewCount); 174 | setSelect(viewCount, values, curSelectedList); 175 | } 176 | } 177 | 178 | private void setSelect(int size, String[] values, ArrayList curSelectedList) { 179 | for (int i = 0; i < size; i++) { 180 | View view = pickerViewAloneLayout.getChildAt(i); 181 | if (view instanceof LoopView) { 182 | LoopView loop = (LoopView) view; 183 | if (loop.hasItem(values[i])) { 184 | loop.setSelectedItem(values[i]); 185 | ReturnData returnData = new ReturnData(); 186 | returnData.setItem(values[i]); 187 | returnData.setIndex(loop.getSelectedIndex()); 188 | curSelectedList.set(i, returnData); 189 | } 190 | } 191 | } 192 | } 193 | 194 | public void setTextColor(int color){ 195 | int viewCount = pickerViewAloneLayout.getChildCount(); 196 | for (int i = 0; i < viewCount; i++) { 197 | View view = pickerViewAloneLayout.getChildAt(i); 198 | if (view instanceof LoopView) { 199 | LoopView loopView = (LoopView) view; 200 | loopView.setTextColor(color); 201 | } 202 | } 203 | } 204 | 205 | public void setTextSize(float size){ 206 | int viewCount = pickerViewAloneLayout.getChildCount(); 207 | for (int i = 0; i < viewCount; i++) { 208 | View view = pickerViewAloneLayout.getChildAt(i); 209 | if (view instanceof LoopView) { 210 | LoopView loopView = (LoopView) view; 211 | loopView.setTextSize(size); 212 | } 213 | } 214 | } 215 | 216 | public void setTypeface(Typeface typeface){ 217 | int viewCount = pickerViewAloneLayout.getChildCount(); 218 | for (int i = 0; i < viewCount; i++) { 219 | View view = pickerViewAloneLayout.getChildAt(i); 220 | if (view instanceof LoopView) { 221 | LoopView loopView = (LoopView) view; 222 | loopView.setTypeface(typeface); 223 | } 224 | } 225 | } 226 | 227 | public void setTextEllipsisLen(int len){ 228 | int viewCount = pickerViewAloneLayout.getChildCount(); 229 | for (int i = 0; i < viewCount; i++) { 230 | View view = pickerViewAloneLayout.getChildAt(i); 231 | if (view instanceof LoopView) { 232 | LoopView loopView = (LoopView) view; 233 | loopView.setTextEllipsisLen(len); 234 | } 235 | } 236 | } 237 | 238 | public void setIsLoop(boolean isLoop) { 239 | if (!isLoop) { 240 | int viewCount = pickerViewAloneLayout.getChildCount(); 241 | for (int i = 0; i < viewCount; i++) { 242 | View view = pickerViewAloneLayout.getChildAt(i); 243 | if (view instanceof LoopView) { 244 | LoopView loopView = (LoopView) view; 245 | loopView.setNotLoop(); 246 | } 247 | } 248 | } 249 | } 250 | 251 | public int getViewHeight() { 252 | int viewHeight = 0; 253 | View view = pickerViewAloneLayout.getChildAt(0); 254 | if (view instanceof LoopView) { 255 | LoopView loopView = (LoopView) view; 256 | viewHeight = loopView.getViewHeight(); 257 | } 258 | return viewHeight; 259 | } 260 | 261 | private ArrayList arrayToList(ReadableArray array) { 262 | ArrayList values = new ArrayList<>(); 263 | for (int i = 0; i < array.size(); i++) { 264 | String value = ""; 265 | switch (array.getType(i).name()) { 266 | case "Boolean": 267 | value = String.valueOf(array.getBoolean(i)); 268 | break; 269 | case "Number": 270 | try { 271 | value = String.valueOf(array.getInt(i)); 272 | } catch (Exception e) { 273 | value = String.valueOf(array.getDouble(i)); 274 | } 275 | break; 276 | case "String": 277 | value = array.getString(i); 278 | break; 279 | } 280 | values.add(value); 281 | } 282 | return values; 283 | } 284 | 285 | } -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/PickerViewLinkage.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.widget.LinearLayout; 9 | 10 | import com.beefe.picker.R; 11 | import com.facebook.react.bridge.ReadableArray; 12 | import com.facebook.react.bridge.ReadableMap; 13 | import com.facebook.react.bridge.ReadableMapKeySetIterator; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | 18 | /** 19 | * Created by heng on 2016/09/01 20 | * 21 | * Edited by heng on 2016/12/26 22 | * 1. Fixed returnData bug 23 | * 2. Added LoopView TextColor and TextSize support 24 | */ 25 | 26 | public class PickerViewLinkage extends LinearLayout { 27 | 28 | private LoopView loopViewOne; 29 | private LoopView loopViewTwo; 30 | private LoopView loopViewThree; 31 | 32 | private OnSelectedListener onSelectedListener; 33 | 34 | private int curRow; 35 | 36 | public PickerViewLinkage(Context context) { 37 | super(context); 38 | init(context); 39 | } 40 | 41 | public PickerViewLinkage(Context context, AttributeSet attrs) { 42 | super(context, attrs); 43 | init(context); 44 | } 45 | 46 | public PickerViewLinkage(Context context, AttributeSet attrs, int defStyleAttr) { 47 | super(context, attrs, defStyleAttr); 48 | init(context); 49 | } 50 | 51 | private void init(Context context) { 52 | View view = LayoutInflater.from(context).inflate(R.layout.picker_view_linkage, this); 53 | loopViewOne = (LoopView) view.findViewById(R.id.loopViewOne); 54 | loopViewTwo = (LoopView) view.findViewById(R.id.loopViewTwo); 55 | loopViewThree = (LoopView) view.findViewById(R.id.loopViewThree); 56 | } 57 | 58 | private void setRow(int row) { 59 | switch (row) { 60 | case 2: 61 | curRow = 2; 62 | loopViewTwo.setVisibility(VISIBLE); 63 | loopViewOne.setVisibility(VISIBLE); 64 | loopViewThree.setVisibility(GONE); 65 | break; 66 | case 3: 67 | curRow = 3; 68 | loopViewOne.setVisibility(VISIBLE); 69 | loopViewTwo.setVisibility(VISIBLE); 70 | loopViewThree.setVisibility(VISIBLE); 71 | break; 72 | default: 73 | break; 74 | } 75 | } 76 | 77 | private ArrayList oneList = new ArrayList<>(); 78 | private ArrayList twoList = new ArrayList<>(); 79 | private ArrayList threeList = new ArrayList<>(); 80 | 81 | private ArrayList data = new ArrayList<>(); 82 | private int selectOneIndex; 83 | private int selectTwoIndex; 84 | 85 | private ArrayList curSelectedList; 86 | 87 | private ReturnData returnData; 88 | private ReturnData returnData1; 89 | private ReturnData returnData2; 90 | 91 | private void checkItems(LoopView loopView, ArrayList list) { 92 | if (list != null && list.size() > 0) { 93 | loopView.setItems(list); 94 | loopView.setSelectedPosition(0); 95 | } 96 | } 97 | 98 | private void setWeights(double[] weights) { 99 | LayoutParams paramsOne = new LayoutParams(0, LayoutParams.MATCH_PARENT); 100 | LayoutParams paramsTwo = new LayoutParams(0, LayoutParams.MATCH_PARENT); 101 | LayoutParams paramsThree = new LayoutParams(0, LayoutParams.MATCH_PARENT); 102 | 103 | switch (curRow) { 104 | case 2: 105 | switch (weights.length) { 106 | case 1: 107 | paramsOne.weight = (float) weights[0]; 108 | paramsTwo.weight = 1.0f; 109 | break; 110 | default: 111 | paramsOne.weight = (float) weights[0]; 112 | paramsTwo.weight = (float) weights[1]; 113 | break; 114 | } 115 | loopViewOne.setLayoutParams(paramsOne); 116 | loopViewTwo.setLayoutParams(paramsTwo); 117 | break; 118 | case 3: 119 | switch (weights.length) { 120 | case 1: 121 | paramsOne.weight = (float) weights[0]; 122 | paramsTwo.weight = 1.0f; 123 | paramsThree.weight = 1.0f; 124 | break; 125 | case 2: 126 | paramsOne.weight = (float) weights[0]; 127 | paramsTwo.weight = (float) weights[1]; 128 | paramsThree.weight = 1.0f; 129 | break; 130 | default: 131 | paramsOne.weight = (float) weights[0]; 132 | paramsTwo.weight = (float) weights[1]; 133 | paramsThree.weight = (float) weights[2]; 134 | break; 135 | } 136 | loopViewOne.setLayoutParams(paramsOne); 137 | loopViewTwo.setLayoutParams(paramsTwo); 138 | loopViewThree.setLayoutParams(paramsThree); 139 | break; 140 | } 141 | } 142 | 143 | /** 144 | * ReadableArray getMap will remove the item. 145 | * 146 | */ 147 | public void setPickerData(ReadableArray array, double[] weights) { 148 | curSelectedList = new ArrayList<>(); 149 | returnData = new ReturnData(); 150 | returnData1 = new ReturnData(); 151 | returnData2 = new ReturnData(); 152 | oneList.clear(); 153 | for (int i = 0; i < array.size(); i++) { 154 | ReadableMap map = array.getMap(i); 155 | data.add(map); 156 | ReadableMapKeySetIterator iterator = map.keySetIterator(); 157 | if (iterator.hasNextKey()) { 158 | String oneValue = iterator.nextKey(); 159 | oneList.add(oneValue); 160 | } 161 | } 162 | checkItems(loopViewOne, oneList); 163 | 164 | returnData.setItem(oneList.get(0)); 165 | returnData.setIndex(loopViewOne.getSelectedIndex()); 166 | if (curSelectedList.size() > 0) { 167 | curSelectedList.set(0, returnData); 168 | } else { 169 | curSelectedList.add(0, returnData); 170 | } 171 | 172 | ReadableArray childArray = data.get(0).getArray(oneList.get(0)); 173 | String name = childArray.getType(0).name(); 174 | if (name.equals("Map")) { 175 | setRow(3); 176 | 177 | twoList.clear(); 178 | getTwoListData(); 179 | checkItems(loopViewTwo, twoList); 180 | returnData1.setItem(twoList.get(0)); 181 | returnData1.setIndex(loopViewTwo.getSelectedIndex()); 182 | if (curSelectedList.size() > 1) { 183 | curSelectedList.set(1, returnData1); 184 | } else { 185 | curSelectedList.add(1, returnData1); 186 | } 187 | 188 | ReadableMap childMap = data.get(0).getArray(oneList.get(0)).getMap(0); 189 | String key = childMap.keySetIterator().nextKey(); 190 | ReadableArray sunArray = childMap.getArray(key); 191 | threeList.clear(); 192 | threeList = arrayToList(sunArray); 193 | checkItems(loopViewThree, threeList); 194 | 195 | if(threeList!=null&&threeList.size()>0){ 196 | returnData2.setItem(threeList.get(0)); 197 | returnData2.setIndex(loopViewThree.getSelectedIndex()); 198 | if (curSelectedList.size() > 2) { 199 | curSelectedList.set(2, returnData2); 200 | } else { 201 | curSelectedList.add(2, returnData2); 202 | } 203 | } 204 | 205 | loopViewOne.setListener(new OnItemSelectedListener() { 206 | @Override 207 | public void onItemSelected(String item, int index) { 208 | selectOneIndex = index; 209 | returnData = new ReturnData(); 210 | returnData.setIndex(index); 211 | returnData.setItem(item); 212 | curSelectedList.set(0, returnData); 213 | twoList.clear(); 214 | ReadableArray arr = data.get(index).getArray(item); 215 | for (int i = 0; i < arr.size(); i++) { 216 | ReadableMap map = arr.getMap(i); 217 | ReadableMapKeySetIterator ite = map.keySetIterator(); 218 | if (ite.hasNextKey()) { 219 | twoList.add(ite.nextKey()); 220 | } 221 | } 222 | checkItems(loopViewTwo, twoList); 223 | returnData1 = new ReturnData(); 224 | returnData1.setItem(twoList.get(0)); 225 | returnData1.setIndex(loopViewTwo.getSelectedIndex()); 226 | curSelectedList.set(1, returnData1); 227 | 228 | 229 | ReadableArray ar = data.get(index).getArray(item); 230 | ReadableMap childMap = ar.getMap(0); 231 | String key = childMap.keySetIterator().nextKey(); 232 | ReadableArray sunArray = childMap.getArray(key); 233 | threeList.clear(); 234 | threeList = arrayToList(sunArray); 235 | checkItems(loopViewThree, threeList); 236 | returnData2 = new ReturnData(); 237 | 238 | if (threeList!=null&&threeList.size()>0){ 239 | returnData2.setItem(threeList.get(0)); 240 | returnData2.setIndex(loopViewThree.getSelectedIndex()); 241 | curSelectedList.set(2, returnData2); 242 | if (onSelectedListener != null) { 243 | onSelectedListener.onSelected(curSelectedList); 244 | } 245 | } 246 | 247 | } 248 | }); 249 | 250 | loopViewTwo.setListener(new OnItemSelectedListener() { 251 | @Override 252 | public void onItemSelected(String item, int index) { 253 | selectTwoIndex = index; 254 | 255 | ReadableArray arr = data.get(selectOneIndex).getArray(oneList.get(selectOneIndex)); 256 | int arrSize = arr.size(); 257 | //fix IndexOutOfBoundsException 258 | //by zooble @2018-1-10 259 | if(index > arrSize){ 260 | index = arrSize - 1; 261 | } 262 | ReadableMap childMap = arr.getMap(index); 263 | String key = childMap.keySetIterator().nextKey(); 264 | ReadableArray sunArray = childMap.getArray(key); 265 | threeList.clear(); 266 | threeList = arrayToList(sunArray); 267 | checkItems(loopViewThree, threeList); 268 | 269 | returnData = new ReturnData(); 270 | returnData.setItem(oneList.get(selectOneIndex)); 271 | returnData.setIndex(loopViewOne.getSelectedIndex()); 272 | curSelectedList.set(0, returnData); 273 | 274 | returnData1 = new ReturnData(); 275 | returnData1.setItem(item); 276 | returnData1.setIndex(index); 277 | curSelectedList.set(1, returnData1); 278 | 279 | returnData2 = new ReturnData(); 280 | 281 | if (threeList!=null&&threeList.size()>0){ 282 | returnData2.setItem(threeList.get(0)); 283 | returnData2.setIndex(loopViewThree.getSelectedIndex()); 284 | curSelectedList.set(2, returnData2); 285 | if (onSelectedListener != null) { 286 | onSelectedListener.onSelected(curSelectedList); 287 | } 288 | 289 | } 290 | 291 | 292 | } 293 | }); 294 | 295 | loopViewThree.setListener(new OnItemSelectedListener() { 296 | @Override 297 | public void onItemSelected(String item, int index) { 298 | //fix IndexOutOfBoundsException 299 | //by zooble @2018-1-10 300 | int arrOneSize = oneList.size(); 301 | if(selectOneIndex >= arrOneSize){ 302 | selectOneIndex = arrOneSize - 1; 303 | } 304 | int arrTwoSize = twoList.size(); 305 | if(selectTwoIndex >= arrTwoSize){ 306 | selectTwoIndex = arrTwoSize - 1; 307 | } 308 | 309 | returnData = new ReturnData(); 310 | returnData.setItem(oneList.get(selectOneIndex)); 311 | returnData.setIndex(loopViewOne.getSelectedIndex()); 312 | curSelectedList.set(0, returnData); 313 | 314 | returnData1 = new ReturnData(); 315 | returnData1.setItem(twoList.get(selectTwoIndex)); 316 | returnData1.setIndex(loopViewTwo.getSelectedIndex()); 317 | curSelectedList.set(1, returnData1); 318 | 319 | returnData2 = new ReturnData(); 320 | returnData2.setItem(item); 321 | returnData2.setIndex(index); 322 | curSelectedList.set(2, returnData2); 323 | if (onSelectedListener != null) { 324 | onSelectedListener.onSelected(curSelectedList); 325 | } 326 | } 327 | }); 328 | } else { 329 | setRow(2); 330 | loopViewOne.setListener(new OnItemSelectedListener() { 331 | @Override 332 | public void onItemSelected(String item, int index) { 333 | selectOneIndex = index; 334 | ReadableArray arr = data.get(index).getArray(item); 335 | twoList.clear(); 336 | twoList = arrayToList(arr); 337 | checkItems(loopViewTwo, twoList); 338 | returnData = new ReturnData(); 339 | returnData.setItem(item); 340 | returnData.setIndex(index); 341 | curSelectedList.set(0, returnData); 342 | returnData1 = new ReturnData(); 343 | returnData1.setItem(twoList.get(0)); 344 | returnData1.setIndex(loopViewTwo.getSelectedIndex()); 345 | curSelectedList.set(1, returnData1); 346 | if (onSelectedListener != null) { 347 | onSelectedListener.onSelected(curSelectedList); 348 | } 349 | } 350 | }); 351 | 352 | twoList.clear(); 353 | twoList = arrayToList(childArray); 354 | checkItems(loopViewTwo, twoList); 355 | returnData1 = new ReturnData(); 356 | returnData1.setItem(twoList.get(0)); 357 | returnData1.setIndex(loopViewTwo.getSelectedIndex()); 358 | if (curSelectedList.size() > 1) { 359 | curSelectedList.set(1, returnData1); 360 | } else { 361 | curSelectedList.add(1, returnData1); 362 | } 363 | loopViewTwo.setListener(new OnItemSelectedListener() { 364 | @Override 365 | public void onItemSelected(String item, int index) { 366 | returnData = new ReturnData(); 367 | returnData.setItem(oneList.get(selectOneIndex)); 368 | returnData.setIndex(loopViewOne.getSelectedIndex()); 369 | curSelectedList.set(0, returnData); 370 | 371 | returnData1 = new ReturnData(); 372 | returnData1.setIndex(index); 373 | returnData1.setItem(item); 374 | curSelectedList.set(1, returnData1); 375 | if (onSelectedListener != null) { 376 | onSelectedListener.onSelected(curSelectedList); 377 | } 378 | } 379 | }); 380 | } 381 | if (weights != null) { 382 | setWeights(weights); 383 | } 384 | } 385 | 386 | private ArrayList arrayToList(ReadableArray array) { 387 | try { 388 | ArrayList list = new ArrayList<>(); 389 | for (int i = 0; i < array.size(); i++) { 390 | String values = ""; 391 | switch (array.getType(i).name()) { 392 | case "Boolean": 393 | values = String.valueOf(array.getBoolean(i)); 394 | break; 395 | case "Number": 396 | try { 397 | values = String.valueOf(array.getInt(i)); 398 | } catch (Exception e) { 399 | values = String.valueOf(array.getDouble(i)); 400 | } 401 | break; 402 | case "String": 403 | values = array.getString(i); 404 | break; 405 | } 406 | list.add(values); 407 | } 408 | return list; 409 | } catch (Exception e) { 410 | return null; 411 | } 412 | } 413 | 414 | public void setSelectValue(String[] selectValue) { 415 | if (curRow <= selectValue.length) { 416 | String[] values = Arrays.copyOf(selectValue, curRow); 417 | selectValues(values, curSelectedList); 418 | } else { 419 | switch (selectValue.length) { 420 | case 1: 421 | selectOneLoop(selectValue, curSelectedList); 422 | switch (curRow) { 423 | case 3: 424 | twoList.clear(); 425 | getTwoListData(); 426 | loopViewTwo.setItems(twoList); 427 | loopViewTwo.setSelectedPosition(0); 428 | returnData1 = new ReturnData(); 429 | returnData1.setItem(loopViewTwo.getIndexItem(0)); 430 | returnData1.setIndex(loopViewTwo.getSelectedIndex()); 431 | curSelectedList.set(1, returnData1); 432 | 433 | threeList.clear(); 434 | getThreeListData(); 435 | loopViewThree.setItems(threeList); 436 | loopViewThree.setSelectedPosition(0); 437 | returnData2 = new ReturnData(); 438 | returnData2.setItem(loopViewThree.getIndexItem(0)); 439 | returnData2.setIndex(loopViewThree.getSelectedIndex()); 440 | curSelectedList.set(2, returnData2); 441 | 442 | break; 443 | case 2: 444 | twoList.clear(); 445 | getAllTwoListData(); 446 | loopViewTwo.setItems(twoList); 447 | loopViewTwo.setSelectedPosition(0); 448 | returnData1 = new ReturnData(); 449 | returnData1.setItem(loopViewTwo.getIndexItem(0)); 450 | returnData1.setIndex(loopViewTwo.getSelectedIndex()); 451 | curSelectedList.set(1, returnData1); 452 | break; 453 | } 454 | break; 455 | case 2: 456 | switch (curRow) { 457 | case 3: 458 | selectOneLoop(selectValue, curSelectedList); 459 | 460 | twoList.clear(); 461 | getTwoListData(); 462 | selectTwoLoop(selectValue, curSelectedList); 463 | 464 | 465 | threeList.clear(); 466 | getThreeListData(); 467 | loopViewThree.setItems(threeList); 468 | loopViewThree.setSelectedPosition(0); 469 | returnData2 = new ReturnData(); 470 | returnData2.setItem(loopViewThree.getIndexItem(0)); 471 | returnData2.setIndex(loopViewThree.getSelectedIndex()); 472 | curSelectedList.set(2, returnData2); 473 | break; 474 | } 475 | break; 476 | default: 477 | break; 478 | } 479 | } 480 | } 481 | 482 | private void selectValues(String[] values, final ArrayList curSelectedList) { 483 | switch (values.length) { 484 | case 3: 485 | selectOneLoop(values, curSelectedList); 486 | 487 | twoList.clear(); 488 | getTwoListData(); 489 | selectTwoLoop(values, curSelectedList); 490 | 491 | threeList.clear(); 492 | getThreeListData(); 493 | selectThreeLoop(values, curSelectedList); 494 | break; 495 | case 2: 496 | selectOneLoop(values, curSelectedList); 497 | 498 | twoList.clear(); 499 | getAllTwoListData(); 500 | selectTwoLoop(values, curSelectedList); 501 | break; 502 | default: 503 | break; 504 | } 505 | } 506 | 507 | /** 508 | * 设置第一个滚轮选中的值 509 | */ 510 | private void selectOneLoop(String[] values, final ArrayList curSelectedList) { 511 | if (loopViewOne.hasItem(values[0])) { 512 | selectOneIndex = loopViewOne.getItemPosition(values[0]); 513 | } else { 514 | selectOneIndex = 0; 515 | } 516 | loopViewOne.setSelectedPosition(selectOneIndex); 517 | 518 | returnData = new ReturnData(); 519 | returnData.setItem(loopViewOne.getIndexItem(selectOneIndex)); 520 | returnData.setIndex(loopViewOne.getSelectedIndex()); 521 | curSelectedList.set(0, returnData); 522 | } 523 | 524 | /** 525 | * 设置第二个滚轮选中的值 526 | */ 527 | private void selectTwoLoop(String[] values, final ArrayList curSelectedList) { 528 | loopViewTwo.setItems(twoList); 529 | if (loopViewTwo.hasItem(values[1])) { 530 | selectTwoIndex = loopViewTwo.getItemPosition(values[1]); 531 | } else { 532 | selectTwoIndex = 0; 533 | } 534 | returnData1 = new ReturnData(); 535 | loopViewTwo.setSelectedPosition(selectTwoIndex); 536 | returnData1.setItem(loopViewTwo.getIndexItem(selectTwoIndex)); 537 | returnData1.setIndex(loopViewTwo.getSelectedIndex()); 538 | curSelectedList.set(1, returnData1); 539 | } 540 | 541 | /** 542 | * 设置第三个滚轮选中的值 543 | */ 544 | private void selectThreeLoop(String[] values, final ArrayList curSelectedList) { 545 | loopViewThree.setItems(threeList); 546 | int selectThreeIndex; 547 | if (loopViewThree.hasItem(values[2])) { 548 | selectThreeIndex = loopViewThree.getItemPosition(values[2]); 549 | } else { 550 | selectThreeIndex = 0; 551 | } 552 | returnData2 = new ReturnData(); 553 | loopViewThree.setSelectedPosition(selectThreeIndex); 554 | returnData2.setItem(loopViewThree.getIndexItem(selectThreeIndex)); 555 | returnData2.setIndex(loopViewThree.getSelectedIndex()); 556 | curSelectedList.set(2, returnData2); 557 | } 558 | 559 | /** 560 | * 只有两个滚轮 561 | * 获取第二个滚轮的值 562 | */ 563 | private void getAllTwoListData() { 564 | ReadableArray arr = data.get(selectOneIndex).getArray(oneList.get(selectOneIndex)); 565 | twoList = arrayToList(arr); 566 | } 567 | 568 | /** 569 | * 有三个滚轮 570 | * 获取第二个滚轮的值 571 | */ 572 | private void getTwoListData() { 573 | ReadableArray childArray = data.get(selectOneIndex).getArray(oneList.get(selectOneIndex)); 574 | for (int i = 0; i < childArray.size(); i++) { 575 | ReadableMap map = childArray.getMap(i); 576 | ReadableMapKeySetIterator iterator = map.keySetIterator(); 577 | if (iterator.hasNextKey()) { 578 | twoList.add(iterator.nextKey()); 579 | } 580 | } 581 | } 582 | 583 | /** 584 | * 获取第三个滚轮的值 585 | */ 586 | private void getThreeListData() { 587 | ReadableMap childMap = data.get(selectOneIndex).getArray(oneList.get(selectOneIndex)).getMap(selectTwoIndex); 588 | String key = childMap.keySetIterator().nextKey(); 589 | ReadableArray sunArray = childMap.getArray(key); 590 | threeList = arrayToList(sunArray); 591 | } 592 | 593 | public void setTextSize(float size){ 594 | switch (curRow) { 595 | case 2: 596 | loopViewOne.setTextSize(size); 597 | loopViewTwo.setTextSize(size); 598 | break; 599 | case 3: 600 | loopViewOne.setTextSize(size); 601 | loopViewTwo.setTextSize(size); 602 | loopViewThree.setTextSize(size); 603 | break; 604 | } 605 | } 606 | 607 | public void setTypeface(Typeface typeface){ 608 | switch (curRow) { 609 | case 2: 610 | loopViewOne.setTypeface(typeface); 611 | loopViewTwo.setTypeface(typeface); 612 | break; 613 | case 3: 614 | loopViewOne.setTypeface(typeface); 615 | loopViewTwo.setTypeface(typeface); 616 | loopViewThree.setTypeface(typeface); 617 | break; 618 | } 619 | } 620 | 621 | public void setTextEllipsisLen(int len){ 622 | switch (curRow) { 623 | case 2: 624 | loopViewOne.setTextEllipsisLen(len); 625 | loopViewTwo.setTextEllipsisLen(len); 626 | break; 627 | case 3: 628 | loopViewOne.setTextEllipsisLen(len); 629 | loopViewTwo.setTextEllipsisLen(len); 630 | loopViewThree.setTextEllipsisLen(len); 631 | break; 632 | } 633 | } 634 | 635 | public void setTextColor(int color){ 636 | switch (curRow) { 637 | case 2: 638 | loopViewOne.setTextColor(color); 639 | loopViewTwo.setTextColor(color); 640 | break; 641 | case 3: 642 | loopViewOne.setTextColor(color); 643 | loopViewTwo.setTextColor(color); 644 | loopViewThree.setTextColor(color); 645 | break; 646 | } 647 | } 648 | 649 | public void setIsLoop(boolean isLoop) { 650 | if (!isLoop) { 651 | switch (curRow) { 652 | case 2: 653 | loopViewOne.setNotLoop(); 654 | loopViewTwo.setNotLoop(); 655 | break; 656 | case 3: 657 | loopViewOne.setNotLoop(); 658 | loopViewTwo.setNotLoop(); 659 | loopViewThree.setNotLoop(); 660 | break; 661 | } 662 | } 663 | } 664 | 665 | public int getViewHeight() { 666 | return loopViewOne.getViewHeight(); 667 | } 668 | 669 | public ArrayList getSelectedData() { 670 | return this.curSelectedList; 671 | } 672 | 673 | public void setOnSelectListener(OnSelectedListener listener) { 674 | this.onSelectedListener = listener; 675 | } 676 | } -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/ReturnData.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | /** 4 | * Created by shexiaoheng on 2016/12/23. 5 | */ 6 | 7 | public class ReturnData { 8 | 9 | private String item; 10 | private int index; 11 | 12 | public String getItem() { 13 | return item; 14 | } 15 | 16 | public void setItem(String item) { 17 | this.item = item; 18 | } 19 | 20 | public int getIndex() { 21 | return index; 22 | } 23 | 24 | public void setIndex(int index) { 25 | this.index = index; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /android/src/main/java/com/beefe/picker/view/SmoothScrollTimerTask.java: -------------------------------------------------------------------------------- 1 | package com.beefe.picker.view; 2 | 3 | import java.util.TimerTask; 4 | 5 | final class SmoothScrollTimerTask extends TimerTask { 6 | 7 | int realTotalOffset; 8 | int realOffset; 9 | int offset; 10 | final LoopView loopView; 11 | 12 | SmoothScrollTimerTask(LoopView loopview, int offset) { 13 | this.loopView = loopview; 14 | this.offset = offset; 15 | realTotalOffset = Integer.MAX_VALUE; 16 | realOffset = 0; 17 | } 18 | 19 | @Override 20 | public final void run() { 21 | if (realTotalOffset == Integer.MAX_VALUE) { 22 | realTotalOffset = offset; 23 | } 24 | realOffset = (int) ((float) realTotalOffset * 0.1F); 25 | 26 | if (realOffset == 0) { 27 | if (realTotalOffset < 0) { 28 | realOffset = -1; 29 | } else { 30 | realOffset = 1; 31 | } 32 | } 33 | if (Math.abs(realTotalOffset) <= 0) { 34 | loopView.cancelFuture(); 35 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED); 36 | } else { 37 | loopView.totalScrollY = loopView.totalScrollY + realOffset; 38 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW); 39 | realTotalOffset = realTotalOffset - realOffset; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android/src/main/res/anim/picker_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | -------------------------------------------------------------------------------- /android/src/main/res/anim/picker_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /android/src/main/res/layout/picker_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 13 | 14 | 20 | 21 | 27 | 28 | 36 | 37 | 38 | 39 | 44 | 45 | 49 | 50 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /android/src/main/res/layout/picker_view_alone.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /android/src/main/res/layout/picker_view_linkage.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 17 | 18 | 23 | 24 | -------------------------------------------------------------------------------- /android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | react-native-picker 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 15 | -------------------------------------------------------------------------------- /doc/ui3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beefe/react-native-picker/6fa89e921c93279e8c0ef6dc871bd13aeddad158/doc/ui3.jpg -------------------------------------------------------------------------------- /doc/ui4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beefe/react-native-picker/6fa89e921c93279e8c0ef6dc871bd13aeddad158/doc/ui4.jpg -------------------------------------------------------------------------------- /example/PickerTest/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } 4 | -------------------------------------------------------------------------------- /example/PickerTest/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/PickerTest/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | [include] 20 | 21 | [libs] 22 | node_modules/react-native/Libraries/react-native/react-native-interface.js 23 | node_modules/react-native/flow/ 24 | 25 | [options] 26 | emoji=true 27 | 28 | module.system=haste 29 | 30 | munge_underscores=true 31 | 32 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 33 | 34 | suppress_type=$FlowIssue 35 | suppress_type=$FlowFixMe 36 | suppress_type=$FlowFixMeProps 37 | suppress_type=$FlowFixMeState 38 | suppress_type=$FixMe 39 | 40 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-7]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 41 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-7]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 42 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 43 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 44 | 45 | unsafe.enable_getters_and_setters=true 46 | 47 | [version] 48 | ^0.57.0 49 | -------------------------------------------------------------------------------- /example/PickerTest/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /example/PickerTest/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | -------------------------------------------------------------------------------- /example/PickerTest/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/PickerTest/PickerTest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootstrap of PickerTest 3 | */ 4 | 5 | import React, {Component} from 'react'; 6 | import { 7 | View, 8 | Text, 9 | TextInput, 10 | TouchableOpacity, 11 | Dimensions 12 | } from 'react-native'; 13 | 14 | import Picker from 'react-native-picker'; 15 | import area from './area.json'; 16 | 17 | export default class PickerTest extends Component { 18 | 19 | constructor(props, context) { 20 | super(props, context); 21 | } 22 | 23 | _createDateData() { 24 | let date = []; 25 | for(let i=1970;i<2020;i++){ 26 | let month = []; 27 | for(let j = 1;j<13;j++){ 28 | let day = []; 29 | if(j === 2){ 30 | for(let k=1;k<29;k++){ 31 | day.push(k+'日'); 32 | } 33 | //Leap day for years that are divisible by 4, such as 2000, 2004 34 | if(i%4 === 0){ 35 | day.push(29+'日'); 36 | } 37 | } 38 | else if(j in {1:1, 3:1, 5:1, 7:1, 8:1, 10:1, 12:1}){ 39 | for(let k=1;k<32;k++){ 40 | day.push(k+'日'); 41 | } 42 | } 43 | else{ 44 | for(let k=1;k<31;k++){ 45 | day.push(k+'日'); 46 | } 47 | } 48 | let _month = {}; 49 | _month[j+'月'] = day; 50 | month.push(_month); 51 | } 52 | let _date = {}; 53 | _date[i+'年'] = month; 54 | date.push(_date); 55 | } 56 | return date; 57 | } 58 | 59 | _createAreaData() { 60 | let data = []; 61 | let len = area.length; 62 | for(let i=0;i { 82 | console.log('date', pickedValue, pickedIndex); 83 | }, 84 | onPickerCancel: (pickedValue, pickedIndex) => { 85 | console.log('date', pickedValue, pickedIndex); 86 | }, 87 | onPickerSelect: (pickedValue, pickedIndex) => { 88 | console.log('date', pickedValue, pickedIndex); 89 | } 90 | }); 91 | Picker.show(); 92 | } 93 | 94 | _showAreaPicker() { 95 | Picker.init({ 96 | pickerData: this._createAreaData(), 97 | selectedValue: ['河北', '唐山', '古冶区'], 98 | onPickerConfirm: pickedValue => { 99 | console.log('area', pickedValue); 100 | }, 101 | onPickerCancel: pickedValue => { 102 | console.log('area', pickedValue); 103 | }, 104 | onPickerSelect: pickedValue => { 105 | //Picker.select(['山东', '青岛', '黄岛区']) 106 | console.log('area', pickedValue); 107 | } 108 | }); 109 | Picker.show(); 110 | } 111 | 112 | _showTimePicker() { 113 | let years = [], 114 | months = [], 115 | days = [], 116 | hours = [], 117 | minutes = []; 118 | 119 | for(let i=1;i<51;i++){ 120 | years.push(i+1980); 121 | } 122 | for(let i=1;i<13;i++){ 123 | months.push(i); 124 | hours.push(i); 125 | } 126 | for(let i=1;i<32;i++){ 127 | days.push(i); 128 | } 129 | for(let i=1;i<61;i++){ 130 | minutes.push(i); 131 | } 132 | let pickerData = [years, months, days, ['am', 'pm'], hours, minutes]; 133 | let date = new Date(); 134 | let selectedValue = [ 135 | date.getFullYear(), 136 | date.getMonth()+1, 137 | date.getDate(), 138 | date.getHours() > 11 ? 'pm' : 'am', 139 | date.getHours() === 12 ? 12 : date.getHours()%12, 140 | date.getMinutes() 141 | ]; 142 | Picker.init({ 143 | pickerData, 144 | selectedValue, 145 | pickerTitleText: 'Select Date and Time', 146 | wheelFlex: [2, 1, 1, 2, 1, 1], 147 | onPickerConfirm: pickedValue => { 148 | console.log('area', pickedValue); 149 | }, 150 | onPickerCancel: pickedValue => { 151 | console.log('area', pickedValue); 152 | }, 153 | onPickerSelect: pickedValue => { 154 | let targetValue = [...pickedValue]; 155 | if(parseInt(targetValue[1]) === 2){ 156 | if(targetValue[0]%4 === 0 && targetValue[2] > 29){ 157 | targetValue[2] = 29; 158 | } 159 | else if(targetValue[0]%4 !== 0 && targetValue[2] > 28){ 160 | targetValue[2] = 28; 161 | } 162 | } 163 | else if(targetValue[1] in {4:1, 6:1, 9:1, 11:1} && targetValue[2] > 30){ 164 | targetValue[2] = 30; 165 | 166 | } 167 | // forbidden some value such as some 2.29, 4.31, 6.31... 168 | if(JSON.stringify(targetValue) !== JSON.stringify(pickedValue)){ 169 | // android will return String all the time,but we put Number into picker at first 170 | // so we need to convert them to Number again 171 | targetValue.map((v, k) => { 172 | if(k !== 3){ 173 | targetValue[k] = parseInt(v); 174 | } 175 | }); 176 | Picker.select(targetValue); 177 | pickedValue = targetValue; 178 | } 179 | } 180 | }); 181 | Picker.show(); 182 | } 183 | 184 | _toggle() { 185 | Picker.toggle(); 186 | } 187 | 188 | _isPickerShow(){ 189 | Picker.isPickerShow(status => { 190 | alert(status); 191 | }); 192 | } 193 | 194 | render() { 195 | return ( 196 | 197 | 198 | DatePicker 199 | 200 | 201 | TimePicker 202 | 203 | 204 | AreaPicker 205 | 206 | 207 | toggle 208 | 209 | 210 | isPickerShow 211 | 212 | 224 | 225 | ); 226 | } 227 | }; -------------------------------------------------------------------------------- /example/PickerTest/__tests__/App.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import App from '../App'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /example/PickerTest/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.pickertest", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.pickertest", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /example/PickerTest/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 37 | * // for example: to disable dev mode in the staging build type (if configured) 38 | * devDisabledInStaging: true, 39 | * // The configuration property can be in the following formats 40 | * // 'devDisabledIn${productFlavor}${buildType}' 41 | * // 'devDisabledIn${buildType}' 42 | * 43 | * // the root of your project, i.e. where "package.json" lives 44 | * root: "../../", 45 | * 46 | * // where to put the JS bundle asset in debug mode 47 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 48 | * 49 | * // where to put the JS bundle asset in release mode 50 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 51 | * 52 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 53 | * // require('./image.png')), in debug mode 54 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 55 | * 56 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 57 | * // require('./image.png')), in release mode 58 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 59 | * 60 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 61 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 62 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 63 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 64 | * // for example, you might want to remove it from here. 65 | * inputExcludes: ["android/**", "ios/**"], 66 | * 67 | * // override which node gets called and with what additional arguments 68 | * nodeExecutableAndArgs: ["node"], 69 | * 70 | * // supply additional arguments to the packager 71 | * extraPackagerArgs: [] 72 | * ] 73 | */ 74 | 75 | project.ext.react = [ 76 | entryFile: "index.js" 77 | ] 78 | 79 | apply from: "../../node_modules/react-native/react.gradle" 80 | 81 | /** 82 | * Set this to true to create two separate APKs instead of one: 83 | * - An APK that only works on ARM devices 84 | * - An APK that only works on x86 devices 85 | * The advantage is the size of the APK is reduced by about 4MB. 86 | * Upload all the APKs to the Play Store and people will download 87 | * the correct one based on the CPU architecture of their device. 88 | */ 89 | def enableSeparateBuildPerCPUArchitecture = false 90 | 91 | /** 92 | * Run Proguard to shrink the Java bytecode in release builds. 93 | */ 94 | def enableProguardInReleaseBuilds = false 95 | 96 | android { 97 | compileSdkVersion 23 98 | buildToolsVersion "23.0.1" 99 | 100 | defaultConfig { 101 | applicationId "com.pickertest" 102 | minSdkVersion 16 103 | targetSdkVersion 22 104 | versionCode 1 105 | versionName "1.0" 106 | ndk { 107 | abiFilters "armeabi-v7a", "x86" 108 | } 109 | } 110 | splits { 111 | abi { 112 | reset() 113 | enable enableSeparateBuildPerCPUArchitecture 114 | universalApk false // If true, also generate a universal APK 115 | include "armeabi-v7a", "x86" 116 | } 117 | } 118 | buildTypes { 119 | release { 120 | minifyEnabled enableProguardInReleaseBuilds 121 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 122 | } 123 | } 124 | // applicationVariants are e.g. debug, release 125 | applicationVariants.all { variant -> 126 | variant.outputs.each { output -> 127 | // For each separate APK per architecture, set a unique version code as described here: 128 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 129 | def versionCodes = ["armeabi-v7a":1, "x86":2] 130 | def abi = output.getFilter(OutputFile.ABI) 131 | if (abi != null) { // null for the universal-debug, universal-release variants 132 | output.versionCodeOverride = 133 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 134 | } 135 | } 136 | } 137 | } 138 | 139 | dependencies { 140 | compile fileTree(dir: "libs", include: ["*.jar"]) 141 | compile "com.android.support:appcompat-v7:23.0.1" 142 | compile "com.facebook.react:react-native:+" // From node_modules 143 | } 144 | 145 | // Run this once to be able to run the application with BUCK 146 | // puts all compile dependencies into folder libs for BUCK to use 147 | task copyDownloadableDepsToLibs(type: Copy) { 148 | from configurations.compile 149 | into 'libs' 150 | } 151 | -------------------------------------------------------------------------------- /example/PickerTest/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # TextLayoutBuilder uses a non-public Android constructor within StaticLayout. 54 | # See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. 55 | -dontwarn android.text.StaticLayout 56 | 57 | # okhttp 58 | 59 | -keepattributes Signature 60 | -keepattributes *Annotation* 61 | -keep class okhttp3.** { *; } 62 | -keep interface okhttp3.** { *; } 63 | -dontwarn okhttp3.** 64 | 65 | # okio 66 | 67 | -keep class sun.misc.Unsafe { *; } 68 | -dontwarn java.nio.file.* 69 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 70 | -dontwarn okio.** 71 | -------------------------------------------------------------------------------- /example/PickerTest/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/PickerTest/android/app/src/main/java/com/pickertest/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.pickertest; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "PickerTest"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/PickerTest/android/app/src/main/java/com/pickertest/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.pickertest; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.shell.MainReactPackage; 9 | import com.facebook.soloader.SoLoader; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | public class MainApplication extends Application implements ReactApplication { 15 | 16 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 17 | @Override 18 | public boolean getUseDeveloperSupport() { 19 | return BuildConfig.DEBUG; 20 | } 21 | 22 | @Override 23 | protected List getPackages() { 24 | return Arrays.asList( 25 | new MainReactPackage() 26 | ); 27 | } 28 | 29 | @Override 30 | protected String getJSMainModuleName() { 31 | return "index"; 32 | } 33 | }; 34 | 35 | @Override 36 | public ReactNativeHost getReactNativeHost() { 37 | return mReactNativeHost; 38 | } 39 | 40 | @Override 41 | public void onCreate() { 42 | super.onCreate(); 43 | SoLoader.init(this, /* native exopackage */ false); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /example/PickerTest/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beefe/react-native-picker/6fa89e921c93279e8c0ef6dc871bd13aeddad158/example/PickerTest/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/PickerTest/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beefe/react-native-picker/6fa89e921c93279e8c0ef6dc871bd13aeddad158/example/PickerTest/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/PickerTest/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beefe/react-native-picker/6fa89e921c93279e8c0ef6dc871bd13aeddad158/example/PickerTest/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/PickerTest/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beefe/react-native-picker/6fa89e921c93279e8c0ef6dc871bd13aeddad158/example/PickerTest/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/PickerTest/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PickerTest 3 | 4 | -------------------------------------------------------------------------------- /example/PickerTest/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/PickerTest/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$rootDir/../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/PickerTest/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /example/PickerTest/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beefe/react-native-picker/6fa89e921c93279e8c0ef6dc871bd13aeddad158/example/PickerTest/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/PickerTest/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 6 | -------------------------------------------------------------------------------- /example/PickerTest/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /example/PickerTest/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /example/PickerTest/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /example/PickerTest/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /example/PickerTest/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'PickerTest' 2 | 3 | include ':app' 4 | -------------------------------------------------------------------------------- /example/PickerTest/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PickerTest", 3 | "displayName": "PickerTest" 4 | } -------------------------------------------------------------------------------- /example/PickerTest/index.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import PickerTest from './PickerTest'; 3 | 4 | AppRegistry.registerComponent('PickerTest', () => PickerTest); 5 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest.xcodeproj/xcshareddata/xcschemes/PickerTest-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest.xcodeproj/xcshareddata/xcschemes/PickerTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import 13 | #import 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | NSURL *jsCodeLocation; 20 | 21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 22 | 23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 24 | moduleName:@"PickerTest" 25 | initialProperties:nil 26 | launchOptions:launchOptions]; 27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | UIViewController *rootViewController = [UIViewController new]; 31 | rootViewController.view = rootView; 32 | self.window.rootViewController = rootViewController; 33 | [self.window makeKeyAndVisible]; 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | PickerTest 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | NSLocationWhenInUseUsageDescription 42 | 43 | NSAppTransportSecurity 44 | 45 | 46 | NSExceptionDomains 47 | 48 | localhost 49 | 50 | NSExceptionAllowsInsecureHTTPLoads 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTest/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTestTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/PickerTest/ios/PickerTestTests/PickerTestTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import 14 | #import 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface PickerTestTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation PickerTestTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /example/PickerTest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PickerTest", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest" 8 | }, 9 | "dependencies": { 10 | "react": "16.0.0", 11 | "react-native": "^0.51.0", 12 | "react-native-picker": "github:beefe/react-native-picker" 13 | }, 14 | "devDependencies": { 15 | "babel-jest": "21.2.0", 16 | "babel-preset-react-native": "4.0.0", 17 | "jest": "21.2.1", 18 | "react-test-renderer": "16.0.0" 19 | }, 20 | "jest": { 21 | "preset": "react-native" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for react-native-picker 2 | // Project: https://github.com/beefe/react-native-picker 3 | // Definitions by: Kyle Roach 4 | // TypeScript Version: 2.3.2 5 | 6 | /** 7 | * Options to create a picker object 8 | * 9 | * @interface PickerOptions 10 | */ 11 | interface PickerOptions { 12 | /** 13 | * Items to be passed into the picker 14 | * 15 | * Default is an empty array 16 | * 17 | * @type {any[]} 18 | * @memberof PickerOptions 19 | */ 20 | pickerData?: any[] 21 | 22 | /** 23 | * The selected item in the picker 24 | * 25 | * Accepts the item in an array 26 | * Example: ['selected'] 27 | * 28 | * Default is an empty array 29 | * 30 | * @type {any[]} 31 | * @memberof PickerOptions 32 | */ 33 | selectedValue?: any[] 34 | 35 | /** 36 | * Title text shown at the top of the picker 37 | * 38 | * Default value is 'pls select' 39 | * 40 | * @type {string} 41 | * @memberof PickerOptions 42 | */ 43 | pickerTitleText?: string 44 | 45 | /** 46 | * Text for the confirm button 47 | * 48 | * Default value is 'confirm' 49 | * 50 | * @type {string} 51 | * @memberof PickerOptions 52 | */ 53 | pickerConfirmBtnText?: string 54 | 55 | /** 56 | * Text for the cancel button 57 | * 58 | * Default value is 'cancel' 59 | * 60 | * @type {string} 61 | * @memberof PickerOptions 62 | */ 63 | pickerCancelBtnText?: string 64 | 65 | /** 66 | * The color of the text for the confirm button 67 | * 68 | * Accepts rgba values as an array 69 | * [R, G, B, A] 70 | * 71 | * Default is [1, 186, 245, 1] 72 | * 73 | * @type {number[]} 74 | * @memberof PickerOptions 75 | */ 76 | pickerConfirmBtnColor?: number[] 77 | 78 | /** 79 | * The color of the text for the cancel button 80 | * 81 | * Accepts rgba values as an array 82 | * [R, G, B, A] 83 | * 84 | * Default is [1, 186, 245, 1] 85 | * 86 | * @type {number[]} 87 | * @memberof PickerOptions 88 | */ 89 | pickerCancelBtnColor?: number[] 90 | 91 | /** 92 | * The color of the Title text 93 | * 94 | * Accepts rgba values as an array 95 | * [R, G, B, A] 96 | * 97 | * Default is [20, 20, 20, 1] 98 | * 99 | * @type {number[]} 100 | * @memberof PickerOptions 101 | */ 102 | pickerTitleColor?: number[] 103 | 104 | /** 105 | * The background color of the toobar 106 | * 107 | * Accepts rgba values as an array 108 | * [R, G, B, A] 109 | * 110 | * Default is [232, 232, 232, 1] 111 | * 112 | * @type {number[]} 113 | * @memberof PickerOptions 114 | */ 115 | pickerToolBarBg?: number[] 116 | 117 | /** 118 | * Background color of the picker 119 | * 120 | * Accepts rgba values as an array 121 | * [R, G, B, A] 122 | * 123 | * Default is [196, 199, 206, 1] 124 | * 125 | * @type {number[]} 126 | * @memberof PickerOptions 127 | */ 128 | pickerBg?: number[] 129 | 130 | 131 | /** 132 | * Font size of the items in the toolbar 133 | * 134 | * Default is 16 135 | * 136 | * @type {number} 137 | * @memberof PickerOptions 138 | */ 139 | pickerToolBarFontSize?: number 140 | 141 | /** 142 | * Font size of the items in the picker 143 | * 144 | * Default is 16 145 | * 146 | * @type {number} 147 | * @memberof PickerOptions 148 | */ 149 | pickerFontSize?: number 150 | 151 | /** 152 | * Row height of the items in the picker 153 | * 154 | * Default is 24 155 | * 156 | * @type {number} 157 | * @memberof PickerOptions 158 | */ 159 | pickerRowHeight?: number 160 | 161 | /** 162 | * Color of the text for the items in the picker 163 | * 164 | * Accepts rgba values as an array 165 | * [R, G, B, A] 166 | * 167 | * Default is [31, 31, 31, 1] 168 | * 169 | * @type {number[]} 170 | * @memberof PickerOptions 171 | */ 172 | pickerFontColor?: number[] 173 | 174 | /** 175 | * Event fired when user confirms the picker 176 | * 177 | * Returns the selected item 178 | * 179 | * @param {any[]} item 180 | * 181 | * @memberof PickerOptions 182 | */ 183 | onPickerConfirm?(item: any[]): void 184 | 185 | /** 186 | * Event fired when user cancels the picker 187 | * 188 | * Returns the selected item 189 | * 190 | * @param {any[]} item 191 | * 192 | * @memberof PickerOptions 193 | */ 194 | onPickerCancel?(item: any[]): void 195 | 196 | 197 | /** 198 | * Event fired when user scrolls over or selects a value in the picker 199 | * 200 | * Returns the selected item 201 | * 202 | * @param {any[]} item 203 | * 204 | * @memberof PickerOptions 205 | */ 206 | onPickerSelect?(item: any[]): void 207 | } 208 | 209 | 210 | export default class Picker { 211 | /** 212 | * Creates a new Picker objects 213 | * 214 | * @static 215 | * @param {PickerOptions} options 216 | * 217 | * @memberof Picker 218 | */ 219 | static init(options: PickerOptions): void 220 | 221 | /** 222 | * Shows the picker 223 | * 224 | * @static 225 | * 226 | * @memberof Picker 227 | */ 228 | static show(): void 229 | 230 | /** 231 | * Hides the picker 232 | * 233 | * @static 234 | * 235 | * @memberof Picker 236 | */ 237 | static hide(): void 238 | 239 | /** 240 | * Toggles the visibility of the picker 241 | * 242 | * @static 243 | * 244 | * @memberof Picker 245 | */ 246 | static toggle(): void 247 | 248 | /** 249 | * Sets an item in the picker as the selected value 250 | * 251 | * Accepts the item in an array 252 | * Example: ['selected'] 253 | * 254 | * @static 255 | * @param {any[]} item 256 | * 257 | * @memberof Picker 258 | */ 259 | static select(item: any[]): void 260 | 261 | /** 262 | * Checks if the picker is showing currently 263 | * 264 | * @static 265 | * @returns {boolean} 266 | * 267 | * @memberof Picker 268 | */ 269 | static isPickerShow(fn?: (err: any, message: any) => void): boolean 270 | } 271 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { 2 | Platform, 3 | NativeModules, 4 | NativeAppEventEmitter 5 | } from 'react-native'; 6 | 7 | const ios = Platform.OS === 'ios'; 8 | const android = Platform.OS === 'android'; 9 | const Picker = NativeModules.BEEPickerManager; 10 | const options = { 11 | isLoop: false, 12 | pickerConfirmBtnText: 'confirm', 13 | pickerCancelBtnText: 'cancel', 14 | pickerTitleText: 'pls select', 15 | pickerConfirmBtnColor: [1, 186, 245, 1], 16 | pickerCancelBtnColor: [1, 186, 245, 1], 17 | pickerTitleColor: [20, 20, 20, 1], 18 | pickerToolBarBg: [232, 232, 232, 1], 19 | pickerTextEllipsisLen: 6, 20 | pickerBg: [196, 199, 206, 1], 21 | pickerRowHeight: 24, 22 | wheelFlex: [1, 1, 1], 23 | pickerData: [], 24 | selectedValue: [], 25 | onPickerConfirm(){}, 26 | onPickerCancel(){}, 27 | onPickerSelect(){}, 28 | pickerToolBarFontSize: 16, 29 | pickerFontSize: 16, 30 | pickerFontColor: [31, 31 ,31, 1] 31 | }; 32 | 33 | export default { 34 | init(params){ 35 | const opt = { 36 | ...options, 37 | ...params 38 | }; 39 | const fnConf = { 40 | confirm: opt.onPickerConfirm, 41 | cancel: opt.onPickerCancel, 42 | select: opt.onPickerSelect 43 | }; 44 | 45 | Picker._init(opt); 46 | //there are no `removeListener` for NativeAppEventEmitter & DeviceEventEmitter 47 | this.listener && this.listener.remove(); 48 | this.listener = NativeAppEventEmitter.addListener('pickerEvent', event => { 49 | fnConf[event['type']](event['selectedValue'], event['selectedIndex']); 50 | }); 51 | }, 52 | 53 | show(){ 54 | Picker.show(); 55 | }, 56 | 57 | hide(){ 58 | Picker.hide(); 59 | }, 60 | 61 | select(arr, fn) { 62 | if(ios){ 63 | Picker.select(arr); 64 | } 65 | else if(android){ 66 | Picker.select(arr, err => { 67 | typeof fn === 'function' && fn(err); 68 | }); 69 | } 70 | }, 71 | 72 | toggle(){ 73 | this.isPickerShow(show => { 74 | if(show){ 75 | this.hide(); 76 | } 77 | else{ 78 | this.show(); 79 | } 80 | }); 81 | }, 82 | 83 | isPickerShow(fn){ 84 | //android return two params: err(error massage) and status(show or not) 85 | //ios return only one param: hide or not... 86 | Picker.isPickerShow((err, status) => { 87 | let returnValue = null; 88 | if(android){ 89 | returnValue = err ? false : status; 90 | } 91 | else if(ios){ 92 | returnValue = !err; 93 | } 94 | fn && fn(returnValue); 95 | }); 96 | } 97 | }; 98 | -------------------------------------------------------------------------------- /ios/RCTBEEPickerManager.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 17D41E591D7EE0CA0031415E /* RCTBEEPickerManager.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17D41E581D7EE0CA0031415E /* RCTBEEPickerManager.h */; }; 11 | 17D41E5B1D7EE0CA0031415E /* RCTBEEPickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D41E5A1D7EE0CA0031415E /* RCTBEEPickerManager.m */; }; 12 | 17D41E631D7EE0EE0031415E /* BzwPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D41E621D7EE0EE0031415E /* BzwPicker.m */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXCopyFilesBuildPhase section */ 16 | 17D41E531D7EE0CA0031415E /* CopyFiles */ = { 17 | isa = PBXCopyFilesBuildPhase; 18 | buildActionMask = 2147483647; 19 | dstPath = "include/$(PRODUCT_NAME)"; 20 | dstSubfolderSpec = 16; 21 | files = ( 22 | 17D41E591D7EE0CA0031415E /* RCTBEEPickerManager.h in CopyFiles */, 23 | ); 24 | runOnlyForDeploymentPostprocessing = 0; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 17D41E551D7EE0CA0031415E /* libRCTBEEPickerManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTBEEPickerManager.a; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 17D41E581D7EE0CA0031415E /* RCTBEEPickerManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBEEPickerManager.h; sourceTree = ""; }; 31 | 17D41E5A1D7EE0CA0031415E /* RCTBEEPickerManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTBEEPickerManager.m; sourceTree = ""; }; 32 | 17D41E611D7EE0EE0031415E /* BzwPicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BzwPicker.h; sourceTree = ""; }; 33 | 17D41E621D7EE0EE0031415E /* BzwPicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BzwPicker.m; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | 17D41E521D7EE0CA0031415E /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | 17D41E4C1D7EE0CA0031415E = { 48 | isa = PBXGroup; 49 | children = ( 50 | 17D41E571D7EE0CA0031415E /* RCTBEEPickerManager */, 51 | 17D41E561D7EE0CA0031415E /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 17D41E561D7EE0CA0031415E /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 17D41E551D7EE0CA0031415E /* libRCTBEEPickerManager.a */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 17D41E571D7EE0CA0031415E /* RCTBEEPickerManager */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 17D41E611D7EE0EE0031415E /* BzwPicker.h */, 67 | 17D41E621D7EE0EE0031415E /* BzwPicker.m */, 68 | 17D41E581D7EE0CA0031415E /* RCTBEEPickerManager.h */, 69 | 17D41E5A1D7EE0CA0031415E /* RCTBEEPickerManager.m */, 70 | ); 71 | path = RCTBEEPickerManager; 72 | sourceTree = ""; 73 | }; 74 | /* End PBXGroup section */ 75 | 76 | /* Begin PBXNativeTarget section */ 77 | 17D41E541D7EE0CA0031415E /* RCTBEEPickerManager */ = { 78 | isa = PBXNativeTarget; 79 | buildConfigurationList = 17D41E5E1D7EE0CA0031415E /* Build configuration list for PBXNativeTarget "RCTBEEPickerManager" */; 80 | buildPhases = ( 81 | 17D41E511D7EE0CA0031415E /* Sources */, 82 | 17D41E521D7EE0CA0031415E /* Frameworks */, 83 | 17D41E531D7EE0CA0031415E /* CopyFiles */, 84 | ); 85 | buildRules = ( 86 | ); 87 | dependencies = ( 88 | ); 89 | name = RCTBEEPickerManager; 90 | productName = RCTBEEPickerManager; 91 | productReference = 17D41E551D7EE0CA0031415E /* libRCTBEEPickerManager.a */; 92 | productType = "com.apple.product-type.library.static"; 93 | }; 94 | /* End PBXNativeTarget section */ 95 | 96 | /* Begin PBXProject section */ 97 | 17D41E4D1D7EE0CA0031415E /* Project object */ = { 98 | isa = PBXProject; 99 | attributes = { 100 | LastUpgradeCheck = 0720; 101 | ORGANIZATIONNAME = "MFHJ-DZ-001-417"; 102 | TargetAttributes = { 103 | 17D41E541D7EE0CA0031415E = { 104 | CreatedOnToolsVersion = 7.2.1; 105 | }; 106 | }; 107 | }; 108 | buildConfigurationList = 17D41E501D7EE0CA0031415E /* Build configuration list for PBXProject "RCTBEEPickerManager" */; 109 | compatibilityVersion = "Xcode 3.2"; 110 | developmentRegion = English; 111 | hasScannedForEncodings = 0; 112 | knownRegions = ( 113 | en, 114 | ); 115 | mainGroup = 17D41E4C1D7EE0CA0031415E; 116 | productRefGroup = 17D41E561D7EE0CA0031415E /* Products */; 117 | projectDirPath = ""; 118 | projectRoot = ""; 119 | targets = ( 120 | 17D41E541D7EE0CA0031415E /* RCTBEEPickerManager */, 121 | ); 122 | }; 123 | /* End PBXProject section */ 124 | 125 | /* Begin PBXSourcesBuildPhase section */ 126 | 17D41E511D7EE0CA0031415E /* Sources */ = { 127 | isa = PBXSourcesBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | 17D41E5B1D7EE0CA0031415E /* RCTBEEPickerManager.m in Sources */, 131 | 17D41E631D7EE0EE0031415E /* BzwPicker.m in Sources */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXSourcesBuildPhase section */ 136 | 137 | /* Begin XCBuildConfiguration section */ 138 | 17D41E5C1D7EE0CA0031415E /* Debug */ = { 139 | isa = XCBuildConfiguration; 140 | buildSettings = { 141 | ALWAYS_SEARCH_USER_PATHS = NO; 142 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 143 | CLANG_CXX_LIBRARY = "libc++"; 144 | CLANG_ENABLE_MODULES = YES; 145 | CLANG_ENABLE_OBJC_ARC = YES; 146 | CLANG_WARN_BOOL_CONVERSION = YES; 147 | CLANG_WARN_CONSTANT_CONVERSION = YES; 148 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 149 | CLANG_WARN_EMPTY_BODY = YES; 150 | CLANG_WARN_ENUM_CONVERSION = YES; 151 | CLANG_WARN_INT_CONVERSION = YES; 152 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 153 | CLANG_WARN_UNREACHABLE_CODE = YES; 154 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 155 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 156 | COPY_PHASE_STRIP = NO; 157 | DEBUG_INFORMATION_FORMAT = dwarf; 158 | ENABLE_STRICT_OBJC_MSGSEND = YES; 159 | ENABLE_TESTABILITY = YES; 160 | GCC_C_LANGUAGE_STANDARD = gnu99; 161 | GCC_DYNAMIC_NO_PIC = NO; 162 | GCC_NO_COMMON_BLOCKS = YES; 163 | GCC_OPTIMIZATION_LEVEL = 0; 164 | GCC_PREPROCESSOR_DEFINITIONS = ( 165 | "DEBUG=1", 166 | "$(inherited)", 167 | ); 168 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 169 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 170 | GCC_WARN_UNDECLARED_SELECTOR = YES; 171 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 172 | GCC_WARN_UNUSED_FUNCTION = YES; 173 | GCC_WARN_UNUSED_VARIABLE = YES; 174 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 175 | MTL_ENABLE_DEBUG_INFO = YES; 176 | ONLY_ACTIVE_ARCH = YES; 177 | SDKROOT = iphoneos; 178 | }; 179 | name = Debug; 180 | }; 181 | 17D41E5D1D7EE0CA0031415E /* Release */ = { 182 | isa = XCBuildConfiguration; 183 | buildSettings = { 184 | ALWAYS_SEARCH_USER_PATHS = NO; 185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 186 | CLANG_CXX_LIBRARY = "libc++"; 187 | CLANG_ENABLE_MODULES = YES; 188 | CLANG_ENABLE_OBJC_ARC = YES; 189 | CLANG_WARN_BOOL_CONVERSION = YES; 190 | CLANG_WARN_CONSTANT_CONVERSION = YES; 191 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 192 | CLANG_WARN_EMPTY_BODY = YES; 193 | CLANG_WARN_ENUM_CONVERSION = YES; 194 | CLANG_WARN_INT_CONVERSION = YES; 195 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 196 | CLANG_WARN_UNREACHABLE_CODE = YES; 197 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 198 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 199 | COPY_PHASE_STRIP = NO; 200 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 201 | ENABLE_NS_ASSERTIONS = NO; 202 | ENABLE_STRICT_OBJC_MSGSEND = YES; 203 | GCC_C_LANGUAGE_STANDARD = gnu99; 204 | GCC_NO_COMMON_BLOCKS = YES; 205 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 206 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 207 | GCC_WARN_UNDECLARED_SELECTOR = YES; 208 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 209 | GCC_WARN_UNUSED_FUNCTION = YES; 210 | GCC_WARN_UNUSED_VARIABLE = YES; 211 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 212 | MTL_ENABLE_DEBUG_INFO = NO; 213 | SDKROOT = iphoneos; 214 | VALIDATE_PRODUCT = YES; 215 | }; 216 | name = Release; 217 | }; 218 | 17D41E5F1D7EE0CA0031415E /* Debug */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | HEADER_SEARCH_PATHS = ( 222 | "$(inherited)", 223 | "$(SRCROOT)/../../react-native/React/**", 224 | ); 225 | OTHER_LDFLAGS = "-ObjC"; 226 | PRODUCT_NAME = "$(TARGET_NAME)"; 227 | SKIP_INSTALL = YES; 228 | }; 229 | name = Debug; 230 | }; 231 | 17D41E601D7EE0CA0031415E /* Release */ = { 232 | isa = XCBuildConfiguration; 233 | buildSettings = { 234 | HEADER_SEARCH_PATHS = ( 235 | "$(inherited)", 236 | "$(SRCROOT)/../../react-native/React/**", 237 | ); 238 | OTHER_LDFLAGS = "-ObjC"; 239 | PRODUCT_NAME = "$(TARGET_NAME)"; 240 | SKIP_INSTALL = YES; 241 | }; 242 | name = Release; 243 | }; 244 | /* End XCBuildConfiguration section */ 245 | 246 | /* Begin XCConfigurationList section */ 247 | 17D41E501D7EE0CA0031415E /* Build configuration list for PBXProject "RCTBEEPickerManager" */ = { 248 | isa = XCConfigurationList; 249 | buildConfigurations = ( 250 | 17D41E5C1D7EE0CA0031415E /* Debug */, 251 | 17D41E5D1D7EE0CA0031415E /* Release */, 252 | ); 253 | defaultConfigurationIsVisible = 0; 254 | defaultConfigurationName = Release; 255 | }; 256 | 17D41E5E1D7EE0CA0031415E /* Build configuration list for PBXNativeTarget "RCTBEEPickerManager" */ = { 257 | isa = XCConfigurationList; 258 | buildConfigurations = ( 259 | 17D41E5F1D7EE0CA0031415E /* Debug */, 260 | 17D41E601D7EE0CA0031415E /* Release */, 261 | ); 262 | defaultConfigurationIsVisible = 0; 263 | }; 264 | /* End XCConfigurationList section */ 265 | }; 266 | rootObject = 17D41E4D1D7EE0CA0031415E /* Project object */; 267 | } 268 | -------------------------------------------------------------------------------- /ios/RCTBEEPickerManager/BzwPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // BzwPicker.h 3 | // PickerView 4 | // 5 | // Created by Bao on 15/12/14. 6 | // Copyright © 2015年 Microlink. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width) 12 | #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height) 13 | 14 | 15 | typedef void(^backBolock)(NSDictionary * ); 16 | 17 | @interface BzwPicker : UIView 18 | 19 | @property (strong,nonatomic)UIPickerView *pick; 20 | 21 | @property(nonatomic,copy)backBolock bolock; 22 | 23 | @property (strong, nonatomic) NSDictionary *pickerDic;//一开始进来的字典 24 | 25 | @property(strong,nonatomic)NSArray *dataDry;//一进来的就没有数组和字典的区别肯定是一个字典 26 | 27 | @property (strong, nonatomic) NSMutableArray *provinceArray;//省、市 28 | @property (strong, nonatomic) NSMutableArray *cityArray;//市,县 29 | @property (strong, nonatomic) NSArray *townArray;//县,区 30 | 31 | 32 | @property(strong,nonatomic)NSArray *selectthreeAry; 33 | 34 | @property (strong,nonatomic)NSArray *selectArry;//2级联动时候用的 35 | 36 | @property (strong,nonatomic)UIButton *leftBtn;//取消 37 | @property (strong,nonatomic)UIButton *rightBtn; 38 | 39 | @property(strong,nonatomic)NSString *leftStr; 40 | @property(strong,nonatomic)NSString *centStr; 41 | @property(strong,nonatomic)NSString *rightStr; 42 | @property(strong,nonatomic)NSString *pickerToolBarFontSize; 43 | @property(strong,nonatomic)NSString *pickerFontSize; 44 | @property(strong,nonatomic)NSString *pickerFontFamily; 45 | @property(strong,nonatomic)NSArray *pickerFontColor; 46 | @property(strong,nonatomic)NSString *pickerRowHeight; 47 | 48 | 49 | 50 | @property(assign,nonatomic)BOOL Correlation;//判断有没有没有关联 51 | 52 | @property(nonatomic,strong)NSString *numberCorrela;//关联是2行 还是3行 53 | 54 | @property(nonatomic,strong)NSArray *noCorreArry; 55 | 56 | //创建一个数组来传递返回的值 57 | @property(nonatomic,strong)NSMutableArray *backArry; 58 | 59 | @property(assign,nonatomic)BOOL noArryElementBool; 60 | 61 | @property(strong,nonatomic)NSMutableArray *infoArry; 62 | 63 | //创建一个数组 接收进来的选择Value 64 | 65 | @property(strong,nonatomic)NSArray *selectValueArry; 66 | 67 | @property(strong,nonatomic)NSArray *weightArry; 68 | 69 | @property(assign,nonatomic)CGFloat lineWith; 70 | //创建一个下角标记录是第几行 来一进来判断第一行被选中 当进来的是关联两行的逻辑的时候 或者三行关联的时候取第二行做记录 71 | 72 | @property(assign,nonatomic)NSInteger num; 73 | 74 | //创建一个下角标 第三行做记录 75 | 76 | @property(assign,nonatomic)NSInteger threenum; 77 | 78 | @property(assign,nonatomic)NSInteger seleNum;//用来做索引下标用 79 | 80 | 81 | 82 | -(instancetype)initWithFrame:(CGRect)frame dic:(NSDictionary *)dic leftStr:(NSString *)leftStr centerStr:(NSString *)centerStr rightStr:(NSString *)rightStr topbgColor:(NSArray *)topbgColor bottombgColor:(NSArray *)bottombgColor leftbtnbgColor:(NSArray *)leftbtnbgColor rightbtnbgColor:(NSArray *)rightbtnbgColor centerbtnColor:(NSArray *)centerbtnColor selectValueArry:(NSArray *)selectValueArry weightArry:(NSArray *)weightArry 83 | pickerToolBarFontSize:(NSString *)pickerToolBarFontSize pickerFontSize:(NSString *)pickerFontSize pickerFontColor:(NSArray *)pickerFontColor pickerRowHeight:(NSString *)pickerRowHeight pickerFontFamily:(NSString *)pickerFontFamily; 84 | 85 | -(void)selectRow; 86 | @end 87 | -------------------------------------------------------------------------------- /ios/RCTBEEPickerManager/RCTBEEPickerManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTBEEPickerManager.h 3 | // RCTBEEPickerManager 4 | // 5 | // Created by MFHJ-DZ-001-417 on 16/9/6. 6 | // Copyright © 2016年 MFHJ-DZ-001-417. All rights reserved. 7 | // 8 | 9 | #import 10 | #if __has_include() 11 | #import 12 | #else 13 | #import "RCTBridgeModule.h" 14 | #endif 15 | 16 | @interface RCTBEEPickerManager : NSObject 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/RCTBEEPickerManager/RCTBEEPickerManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTBEEPickerManager.m 3 | // RCTBEEPickerManager 4 | // 5 | // Created by MFHJ-DZ-001-417 on 16/9/6. 6 | // Copyright © 2016年 MFHJ-DZ-001-417. All rights reserved. 7 | // 8 | 9 | #import "RCTBEEPickerManager.h" 10 | #import "BzwPicker.h" 11 | #import 12 | 13 | @interface RCTBEEPickerManager() 14 | 15 | @property(nonatomic,strong)BzwPicker *pick; 16 | @property(nonatomic,assign)float height; 17 | @property(nonatomic,weak)UIWindow * window; 18 | 19 | @end 20 | 21 | @implementation RCTBEEPickerManager 22 | 23 | @synthesize bridge = _bridge; 24 | 25 | - (dispatch_queue_t)methodQueue 26 | { 27 | return dispatch_get_main_queue(); 28 | } 29 | 30 | + (BOOL)requiresMainQueueSetup 31 | { 32 | return YES; 33 | } 34 | 35 | RCT_EXPORT_MODULE(); 36 | 37 | RCT_EXPORT_METHOD(_init:(NSDictionary *)indic){ 38 | 39 | dispatch_async(dispatch_get_main_queue(), ^{ 40 | [[UIApplication sharedApplication].keyWindow endEditing:YES]; 41 | }); 42 | 43 | self.window = [UIApplication sharedApplication].keyWindow; 44 | 45 | NSString *pickerConfirmBtnText=indic[@"pickerConfirmBtnText"]; 46 | NSString *pickerCancelBtnText=indic[@"pickerCancelBtnText"]; 47 | NSString *pickerTitleText=indic[@"pickerTitleText"]; 48 | NSArray *pickerConfirmBtnColor=indic[@"pickerConfirmBtnColor"]; 49 | NSArray *pickerCancelBtnColor=indic[@"pickerCancelBtnColor"]; 50 | NSArray *pickerTitleColor=indic[@"pickerTitleColor"]; 51 | NSArray *pickerToolBarBg=indic[@"pickerToolBarBg"]; 52 | NSArray *pickerBg=indic[@"pickerBg"]; 53 | NSArray *selectArry=indic[@"selectedValue"]; 54 | NSArray *weightArry=indic[@"wheelFlex"]; 55 | NSString *pickerToolBarFontSize=[NSString stringWithFormat:@"%@",indic[@"pickerToolBarFontSize"]]; 56 | NSString *pickerFontSize=[NSString stringWithFormat:@"%@",indic[@"pickerFontSize"]]; 57 | NSString *pickerFontFamily=[NSString stringWithFormat:@"%@",indic[@"pickerFontFamily"]]; 58 | NSArray *pickerFontColor=indic[@"pickerFontColor"]; 59 | NSString *pickerRowHeight=indic[@"pickerRowHeight"]; 60 | id pickerData=indic[@"pickerData"]; 61 | 62 | NSMutableDictionary *dataDic=[[NSMutableDictionary alloc]init]; 63 | 64 | dataDic[@"pickerData"]=pickerData; 65 | 66 | [self.window.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 67 | 68 | if ([obj isKindOfClass:[BzwPicker class]]) { 69 | dispatch_async(dispatch_get_main_queue(), ^{ 70 | 71 | [obj removeFromSuperview]; 72 | }); 73 | } 74 | 75 | }]; 76 | 77 | if ([[UIDevice currentDevice].systemVersion doubleValue] >= 9.0 ) { 78 | self.height=250; 79 | }else{ 80 | self.height=220; 81 | } 82 | 83 | self.pick=[[BzwPicker alloc]initWithFrame:CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, self.height) dic:dataDic leftStr:pickerCancelBtnText centerStr:pickerTitleText rightStr:pickerConfirmBtnText topbgColor:pickerToolBarBg bottombgColor:pickerBg leftbtnbgColor:pickerCancelBtnColor rightbtnbgColor:pickerConfirmBtnColor centerbtnColor:pickerTitleColor selectValueArry:selectArry weightArry:weightArry pickerToolBarFontSize:pickerToolBarFontSize pickerFontSize:pickerFontSize pickerFontColor:pickerFontColor pickerRowHeight: pickerRowHeight pickerFontFamily:pickerFontFamily]; 84 | 85 | _pick.bolock=^(NSDictionary *backinfoArry){ 86 | 87 | dispatch_async(dispatch_get_main_queue(), ^{ 88 | 89 | [self.bridge.eventDispatcher sendAppEventWithName:@"pickerEvent" body:backinfoArry]; 90 | }); 91 | }; 92 | 93 | dispatch_async(dispatch_get_main_queue(), ^{ 94 | 95 | [self.window addSubview:_pick]; 96 | }); 97 | 98 | } 99 | 100 | RCT_EXPORT_METHOD(show){ 101 | if (self.pick) { 102 | 103 | dispatch_async(dispatch_get_main_queue(), ^{ 104 | [UIView animateWithDuration:.3 animations:^{ 105 | 106 | [_pick setFrame:CGRectMake(0, SCREEN_HEIGHT-self.height, SCREEN_WIDTH, self.height)]; 107 | 108 | }]; 109 | }); 110 | }return; 111 | } 112 | 113 | RCT_EXPORT_METHOD(hide){ 114 | 115 | if (self.pick) { 116 | dispatch_async(dispatch_get_main_queue(), ^{ 117 | [UIView animateWithDuration:.3 animations:^{ 118 | [_pick setFrame:CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, self.height)]; 119 | }]; 120 | }); 121 | } 122 | 123 | self.pick.hidden=YES; 124 | 125 | return; 126 | } 127 | 128 | RCT_EXPORT_METHOD(select: (NSArray*)data){ 129 | 130 | if (self.pick) { 131 | dispatch_async(dispatch_get_main_queue(), ^{ 132 | _pick.selectValueArry = data; 133 | [_pick selectRow]; 134 | }); 135 | }return; 136 | } 137 | 138 | RCT_EXPORT_METHOD(isPickerShow:(RCTResponseSenderBlock)getBack){ 139 | 140 | if (self.pick) { 141 | 142 | CGFloat pickY=_pick.frame.origin.y; 143 | 144 | if (pickY==SCREEN_HEIGHT) { 145 | 146 | getBack(@[@YES]); 147 | }else 148 | { 149 | getBack(@[@NO]); 150 | } 151 | }else{ 152 | getBack(@[@"picker不存在"]); 153 | } 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-picker", 3 | "version": "4.3.7", 4 | "description": "A Native Picker with high performance.", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git@github.com:beefe/react-native-picker.git" 13 | }, 14 | "author": "zooble", 15 | "license": "MIT", 16 | "homepage": "https://github.com/beefe/react-native-picker" 17 | } 18 | --------------------------------------------------------------------------------