├── .gitignore ├── LICENSE ├── README.md ├── android-pickerdialog ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── sunjian │ │ └── android_pickview_lib │ │ ├── BaseDatePickerDialog.java │ │ ├── BaseOptionsPickerDialog.java │ │ ├── PhoneDatePickerDialog.java │ │ ├── PhoneOptionsPickerDialog.java │ │ ├── TVDatePickerDialog.java │ │ ├── TVOptionsPickerDialog.java │ │ ├── adapter │ │ ├── ArrayWheelAdapter.java │ │ ├── NumericWheelAdapter.java │ │ ├── WheelAdapter.java │ │ ├── WheelOptions.java │ │ └── WheelTime.java │ │ ├── listener │ │ └── OnItemSelectedListener.java │ │ ├── model │ │ └── IPickerViewData.java │ │ └── view │ │ ├── InertiaTimerTask.java │ │ ├── LoopViewGestureListener.java │ │ ├── MessageHandler.java │ │ ├── OnItemSelectedRunnable.java │ │ ├── SmoothScrollTimerTask.java │ │ └── WheelView.java │ └── res │ ├── anim │ ├── dialog_push_in.xml │ └── dialog_push_out.xml │ ├── drawable │ ├── selector_pickerview_btn.xml │ ├── selector_pickerview_ok.xml │ ├── shape_ok_selected.xml │ ├── shape_ok_unselected.xml │ └── shape_tv_layout_bg.xml │ ├── layout │ ├── include_pickerview_topbar.xml │ ├── pickerview_phone_date.xml │ ├── pickerview_phone_options.xml │ ├── pickerview_tv_date.xml │ └── pickerview_tv_options.xml │ └── values │ ├── attrs.xml │ ├── bools.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sunjian │ │ └── android_pickerview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sunjian │ │ │ └── android_pickerview │ │ │ ├── MainActivity.java │ │ │ ├── MyOptionsDialog.java │ │ │ └── bean │ │ │ ├── PickerViewData.java │ │ │ └── ProvinceBean.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── sunjian │ └── android_pickerview │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── picture ├── TVDate.png ├── TVOptions.png ├── optionsPhone .png └── phoneDate.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | /.idea 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android-PickerDialog 2 | 3 | ## 这是一个兼容手机和电视(焦点选择)的高仿IOS风格的选择器。 4 | 5 | 6 | #### Gradle依赖 7 | 8 | ``` 9 | compile 'com.crazysunj:android-pickerdialog:1.1.1' 10 | ``` 11 | 12 | #### 效果图 13 | 14 | * 手机日期 15 | 16 | ![](https://github.com/crazysunj/Android-PickerView/blob/master/picture/phoneDate.png) 17 | 18 | * 手机选项(根据集合显示,这里以地址为例) 19 | 20 | ![](https://github.com/crazysunj/Android-PickerDialog/blob/master/picture/optionsPhone%20.png?raw=true) 21 | 22 | * 电视日期(红色表示聚焦,颜色可修改) 23 | 24 | ![](https://github.com/crazysunj/Android-PickerView/blob/master/picture/TVDate.png) 25 | 26 | * 电视选项 27 | 28 | ![](https://github.com/crazysunj/Android-PickerView/blob/master/picture/TVOptions.png) 29 | 30 | #### 属性 31 | 32 | ``` 33 | //位置,支持左中右 34 | 35 | 36 | 37 | 38 | 39 | 40 | //字体大小 41 | 42 | 43 | //上下字体颜色 44 | 45 | 46 | //中间字体颜色 47 | 48 | 49 | 50 | //聚焦字体颜色,用于聚焦类APP 51 | 52 | 53 | //分割线颜色 54 | 55 | 56 | //是否用于聚焦 57 | 58 | 59 | //是否循环 60 | 61 | 62 | //单位 63 | 64 | ``` 65 | 66 | 初始化都以传bundle的形式传入,对话框都是继承于DialogFragment,方便管理。 67 | 68 | 如果是继承于BaseOptionsPickerDialog或者BaseDatePickerDialog,实现getResLayoutId的时候注意id的命名。 69 | 70 | 时间命名为year,month,day,hour,min 71 | 72 | 条件命名为options1,options2,options3 73 | 74 | id是通过resources.getIdentifier找到的。 75 | 76 | 简单修改样式可继承已有对话框修改。 77 | 78 | ### 传送门 79 | 80 | 博客地址:[http://crazysunj.com/](http://crazysunj.com/) 81 | 82 | 参考:[Android-PickerView](https://github.com/Bigkoo/Android-PickerView) 83 | 84 | ### License 85 | 86 | > ``` 87 | > Copyright 2016 Sun Jian 88 | > 89 | > Licensed under the Apache License, Version 2.0 (the "License"); 90 | > you may not use this file except in compliance with the License. 91 | > You may obtain a copy of the License at 92 | > 93 | > http://www.apache.org/licenses/LICENSE-2.0 94 | > 95 | > Unless required by applicable law or agreed to in writing, software 96 | > distributed under the License is distributed on an "AS IS" BASIS, 97 | > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 98 | > See the License for the specific language governing permissions and 99 | > limitations under the License. 100 | > ``` 101 | -------------------------------------------------------------------------------- /android-pickerdialog/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android-pickerdialog/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | version = "1.1.1" 5 | android { 6 | compileSdkVersion 26 7 | buildToolsVersion "26.0.2" 8 | 9 | defaultConfig { 10 | minSdkVersion 11 11 | targetSdkVersion 26 12 | versionCode 1 13 | versionName version 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | lintOptions { 22 | abortOnError false 23 | } 24 | } 25 | 26 | dependencies { 27 | compile fileTree(dir: 'libs', include: ['*.jar']) 28 | compile 'com.android.support:support-annotations:27.0.2' 29 | } 30 | 31 | def siteUrl = 'https://github.com/crazysunj/Android-PickerDialog' 32 | def gitUrl = 'https://github.com/crazysunj/Android-PickerDialog.git' 33 | group = "com.crazysunj" 34 | install { 35 | repositories.mavenInstaller { 36 | pom { 37 | project { 38 | packaging 'aar' 39 | name 'PickerDialog For Android' 40 | url siteUrl 41 | licenses { 42 | license { 43 | name 'The Apache Software License, Version 2.0' 44 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 45 | } 46 | } 47 | developers { 48 | developer { 49 | id 'twsunj' 50 | name 'crazysunj' 51 | email 'twsunj@gmail.com' 52 | } 53 | } 54 | scm { 55 | connection gitUrl 56 | developerConnection gitUrl 57 | url siteUrl 58 | } 59 | } 60 | } 61 | } 62 | } 63 | task sourcesJar(type: Jar) { 64 | from android.sourceSets.main.java.srcDirs 65 | classifier = 'sources' 66 | } 67 | 68 | task javadoc(type: Javadoc) { 69 | source = android.sourceSets.main.java.srcDirs 70 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 71 | } 72 | 73 | task javadocJar(type: Jar, dependsOn: javadoc) { 74 | classifier = 'javadoc' 75 | from javadoc.destinationDir 76 | } 77 | artifacts { 78 | archives javadocJar 79 | archives sourcesJar 80 | } 81 | Properties properties = new Properties() 82 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 83 | allprojects { 84 | tasks.withType(Javadoc) { 85 | options.addStringOption('Xdoclint:none','-quiet') 86 | options.addStringOption('encoding','UTF-8') 87 | options{ 88 | encoding "UTF-8" 89 | charSet 'UTF-8' 90 | links "http://docs.oracle.com/javase/7/docs/api" 91 | } 92 | } 93 | } 94 | bintray { 95 | user = properties.getProperty("bintray.user") 96 | key = properties.getProperty("bintray.apikey") 97 | configurations = ['archives'] 98 | pkg { 99 | repo = "maven" 100 | name = "pickerdialog" 101 | websiteUrl = siteUrl 102 | vcsUrl = gitUrl 103 | licenses = ["Apache-2.0"] 104 | publish = true 105 | } 106 | } -------------------------------------------------------------------------------- /android-pickerdialog/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/sunjian/Downloads/sdk/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-pickerdialog/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/BaseDatePickerDialog.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib; 2 | 3 | 4 | import android.app.DialogFragment; 5 | import android.os.Bundle; 6 | import android.support.annotation.IdRes; 7 | import android.support.annotation.LayoutRes; 8 | import android.support.annotation.Nullable; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | 13 | 14 | import com.sunjian.android_pickview_lib.adapter.WheelTime; 15 | 16 | import java.text.ParseException; 17 | import java.util.Calendar; 18 | import java.util.Date; 19 | 20 | /** 21 | * 封装基类日期dialog为dialogfragment,方便生命周期的管理 22 | * Created by sunjian on 2016/12/22. 23 | */ 24 | public abstract class BaseDatePickerDialog extends DialogFragment { 25 | 26 | public static final String START_YEAR = "START_YEAR";//开始年份 27 | public static final String END_YEAR = "END_YEAR";//结束年份 28 | public static final String IS_CYCLIC = "IS_CYCLIC";//是否循环 29 | public static final String INIT_DATE = "INIT_DATE";//初始化日期 30 | public static final String SAVE_DATE = "SAVE_DATE";//保存日期 31 | public static final String DATE_TYPE = "DATE_TYPE";//日期类型 32 | //基础字体大小 33 | public static final String BASE_TEXT_SIZE = "BASE_TEXT_SIZE"; 34 | public static final String START_TIME_MILLIS = "START_TIME_MILLIS"; 35 | public static final String START_TIME_DATE = "START_TIME_DATE"; 36 | public static final String START_TIME_CALENDAR = "START_TIME_CALENDAR"; 37 | public static final String END_TIME_MILLIS = "END_TIME_MILLIS"; 38 | public static final String END_TIME_DATE = "END_TIME_DATE"; 39 | public static final String END_TIME_CALENDAR = "END_TIME_CALENDAR"; 40 | 41 | protected WheelTime mWheelTime; 42 | protected OnTimeSelectListener mTimeSelectListener; 43 | 44 | @Nullable 45 | @Override 46 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 47 | View v = inflater.inflate(getResLayoutId(), container, false); 48 | init(v, savedInstanceState); 49 | return v; 50 | } 51 | 52 | private void init(View v, Bundle savedInstanceState) { 53 | 54 | Bundle bundle = getArguments(); 55 | 56 | int startYear = bundle.getInt(START_YEAR, -1); 57 | 58 | int endYear = bundle.getInt(END_YEAR, -1); 59 | 60 | boolean isCyclic = bundle.getBoolean(IS_CYCLIC, false); 61 | 62 | @WheelTime.DateType int type = bundle.getInt(DATE_TYPE, WheelTime.ALL); 63 | 64 | int baseTextSize = bundle.getInt(BASE_TEXT_SIZE, 6); 65 | 66 | Date date; 67 | 68 | if (savedInstanceState != null) { 69 | //恢复数据 70 | try { 71 | String dateStr = savedInstanceState.getString(SAVE_DATE); 72 | date = WheelTime.dateFormat.parse(dateStr); 73 | } catch (ParseException e) { 74 | date = null; 75 | e.printStackTrace(); 76 | } 77 | } else { 78 | date = (Date) bundle.getSerializable(INIT_DATE); 79 | } 80 | 81 | initView(v); 82 | 83 | // ----时间转轮 84 | final View timepickerview = v.findViewById(getPickerViewId()); 85 | mWheelTime = new WheelTime(timepickerview, type, baseTextSize); 86 | 87 | //设置时间范围 88 | if (startYear != -1) { 89 | mWheelTime.setStartYear(startYear); 90 | } 91 | 92 | if (endYear != -1) { 93 | mWheelTime.setEndYear(endYear); 94 | } 95 | 96 | long startTimeMillils = bundle.getLong(START_TIME_MILLIS, -1); 97 | if (startTimeMillils != -1) { 98 | mWheelTime.setStartDate(startTimeMillils); 99 | } 100 | 101 | long endTimeMillils = bundle.getLong(END_TIME_MILLIS, -1); 102 | if (endTimeMillils != -1) { 103 | mWheelTime.setEndDate(endTimeMillils); 104 | } 105 | 106 | Date startTimeDate = (Date) bundle.getSerializable(START_TIME_DATE); 107 | if (startTimeDate != null) { 108 | mWheelTime.setStartDate(startTimeDate); 109 | } 110 | 111 | Date endTimeDate = (Date) bundle.getSerializable(END_TIME_DATE); 112 | if (endTimeDate != null) { 113 | mWheelTime.setEndDate(endTimeDate); 114 | } 115 | 116 | Calendar startTimeCal = (Calendar) bundle.getSerializable(START_TIME_CALENDAR); 117 | if (startTimeCal != null) { 118 | mWheelTime.setStartDate(startTimeCal); 119 | } 120 | 121 | Calendar endTimeCal = (Calendar) bundle.getSerializable(END_TIME_CALENDAR); 122 | if (endTimeCal != null) { 123 | mWheelTime.setEndDate(endTimeCal); 124 | } 125 | 126 | //设置选中时间 127 | Calendar calendar = Calendar.getInstance(); 128 | if (date == null) { 129 | calendar.setTimeInMillis(System.currentTimeMillis()); 130 | } else { 131 | calendar.setTime(date); 132 | } 133 | int year = calendar.get(Calendar.YEAR); 134 | int month = calendar.get(Calendar.MONTH); 135 | int day = calendar.get(Calendar.DAY_OF_MONTH); 136 | int hours = calendar.get(Calendar.HOUR_OF_DAY); 137 | int minute = calendar.get(Calendar.MINUTE); 138 | mWheelTime.setPicker(year, month, day, hours, minute); 139 | 140 | //获取焦点 141 | requestFocus(); 142 | 143 | //设置是否循环 144 | mWheelTime.setCyclic(isCyclic); 145 | } 146 | 147 | protected void requestFocus() { 148 | 149 | } 150 | 151 | protected void initView(View v) { 152 | 153 | } 154 | 155 | @IdRes 156 | protected abstract int getPickerViewId(); 157 | 158 | //信息保存 159 | @Override 160 | public void onSaveInstanceState(Bundle outState) { 161 | //保存数据 162 | outState.putString(SAVE_DATE, mWheelTime.getTime()); 163 | super.onSaveInstanceState(outState); 164 | } 165 | 166 | /** 167 | * 如果想调整布局可通过继承,重写该方法 168 | * 若需求较为复杂,请自己自定义dialog 169 | */ 170 | @LayoutRes 171 | protected abstract int getResLayoutId(); 172 | 173 | public interface OnTimeSelectListener { 174 | void onTimeSelect(Date date); 175 | } 176 | 177 | public void setOnTimeSelectListener(OnTimeSelectListener timeSelectListener) { 178 | this.mTimeSelectListener = timeSelectListener; 179 | } 180 | 181 | public void clickSubmit(View v) { 182 | if (mTimeSelectListener != null) { 183 | try { 184 | Date date = WheelTime.dateFormat.parse(mWheelTime.getTime()); 185 | mTimeSelectListener.onTimeSelect(date); 186 | } catch (ParseException e) { 187 | e.printStackTrace(); 188 | } 189 | } 190 | dismiss(); 191 | } 192 | 193 | public void clickCancel(View v) { 194 | dismiss(); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/BaseOptionsPickerDialog.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib; 2 | 3 | import android.app.DialogFragment; 4 | import android.os.Bundle; 5 | import android.support.annotation.IdRes; 6 | import android.support.annotation.LayoutRes; 7 | import android.support.annotation.Nullable; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | 13 | import com.sunjian.android_pickview_lib.adapter.WheelOptions; 14 | 15 | import java.util.ArrayList; 16 | 17 | /** 18 | * 集合选项选择器基类 19 | * Created by sunjian on 2016/12/22. 20 | */ 21 | 22 | public abstract class BaseOptionsPickerDialog extends DialogFragment { 23 | 24 | //单位 25 | public static final String LABEL_FIRST = "LABEL_FIRST"; 26 | public static final String LABEL_SECOND = "LABEL_SECOND"; 27 | public static final String LABEL_THIRD = "LABEL_THIRD"; 28 | //循环 29 | public static final String CYCLIC_FIRST = "CYCLIC_FIRST"; 30 | public static final String CYCLIC_SECOND = "CYCLIC_SECOND"; 31 | public static final String CYCLIC_THIRD = "CYCLIC_THIRD"; 32 | //挑选位置 33 | public static final String SELECT_FIRST = "SELECT_FIRST"; 34 | public static final String SELECT_SECOND = "SELECT_SECOND"; 35 | public static final String SELECT_THIRD = "SELECT_THIRD"; 36 | //字体大小 37 | public static final String TEXT_SIZE = "TEXT_SIZE"; 38 | 39 | //联动 40 | public static final String LINKAGE = "LINKAGE"; 41 | 42 | protected WheelOptions mWheelOptions; 43 | 44 | private OnOptionsSelectListener mOptionsSelectListener; 45 | 46 | protected ArrayList mOptions1Items; 47 | protected ArrayList> mOptions2Items; 48 | protected ArrayList>> mOptions3Items; 49 | 50 | @Nullable 51 | @Override 52 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 53 | View v = inflater.inflate(getResLayoutId(), container, false); 54 | init(v, savedInstanceState); 55 | return v; 56 | } 57 | 58 | private void init(View v, Bundle savedInstanceState) { 59 | 60 | Bundle bundle = getArguments(); 61 | 62 | int selectFirst; 63 | int selectSecond; 64 | int selectThird; 65 | 66 | String firstLabel = bundle.getString(LABEL_FIRST); 67 | String secondLabel = bundle.getString(LABEL_SECOND); 68 | String thirdLabel = bundle.getString(LABEL_THIRD); 69 | 70 | boolean firstCyclic = bundle.getBoolean(CYCLIC_FIRST, false); 71 | boolean secondCyclic = bundle.getBoolean(CYCLIC_SECOND, false); 72 | boolean thirdCyclic = bundle.getBoolean(CYCLIC_THIRD, false); 73 | 74 | boolean linkage = bundle.getBoolean(LINKAGE, true); 75 | 76 | int textSize = bundle.getInt(TEXT_SIZE, 25); 77 | 78 | //恢复数据 79 | if (savedInstanceState != null) { 80 | selectFirst = savedInstanceState.getInt(SELECT_FIRST, 0); 81 | selectSecond = savedInstanceState.getInt(SELECT_SECOND, 0); 82 | selectThird = savedInstanceState.getInt(SELECT_THIRD, 0); 83 | } else { 84 | selectFirst = bundle.getInt(SELECT_FIRST, 0); 85 | selectSecond = bundle.getInt(SELECT_SECOND, 0); 86 | selectThird = bundle.getInt(SELECT_THIRD, 0); 87 | } 88 | 89 | initView(v); 90 | 91 | // ----转轮 92 | final View optionspicker = v.findViewById(getPickerViewId()); 93 | mWheelOptions = new WheelOptions(optionspicker, textSize); 94 | 95 | mWheelOptions.setPicker(mOptions1Items, mOptions2Items, mOptions3Items, linkage); 96 | 97 | //获取焦点 98 | requestFocus(); 99 | 100 | mWheelOptions.setLabels(firstLabel, secondLabel, thirdLabel); 101 | 102 | mWheelOptions.setCyclic(firstCyclic, secondCyclic, thirdCyclic); 103 | 104 | if (selectFirst != 0 || selectSecond != 0 || selectThird != 0) { 105 | mWheelOptions.setCurrentItems(selectFirst, selectSecond, selectThird); 106 | } 107 | 108 | } 109 | 110 | protected void requestFocus() { 111 | 112 | } 113 | 114 | /** 115 | * @param v 初始化view 116 | */ 117 | protected abstract void initView(View v); 118 | 119 | //信息保存 120 | @Override 121 | public void onSaveInstanceState(Bundle outState) { 122 | //保存数据 123 | int[] optionsCurrentItems = mWheelOptions.getCurrentItems(); 124 | outState.putInt(SELECT_FIRST, optionsCurrentItems[0]); 125 | outState.putInt(SELECT_SECOND, optionsCurrentItems[1]); 126 | outState.putInt(SELECT_THIRD, optionsCurrentItems[2]); 127 | super.onSaveInstanceState(outState); 128 | } 129 | 130 | /** 131 | * 关于wheelview的id都是规定的, year, month, day, hour, min 132 | * 133 | * @return pickerview的id 134 | */ 135 | @IdRes 136 | protected abstract int getPickerViewId(); 137 | 138 | /** 139 | * 如果想调整布局可通过继承,重写该方法,但是id不能变 140 | * 若需求较为复杂,请自己自定义dialog 141 | * 142 | * @return 资源Id 143 | */ 144 | @LayoutRes 145 | protected abstract int getResLayoutId(); 146 | 147 | 148 | /** 149 | * 以下方法不能作为初始化使用,即创建页面后使用 150 | */ 151 | public void setPicker(ArrayList optionsItems) { 152 | if (mWheelOptions == null) { 153 | return; 154 | } 155 | mWheelOptions.setPicker(optionsItems, null, null, false); 156 | } 157 | 158 | public void setPicker(ArrayList options1Items, 159 | ArrayList> options2Items, boolean linkage) { 160 | if (mWheelOptions == null) { 161 | return; 162 | } 163 | mWheelOptions.setPicker(options1Items, options2Items, null, linkage); 164 | } 165 | 166 | public void setPicker(ArrayList options1Items, 167 | ArrayList> options2Items, 168 | ArrayList>> options3Items, 169 | boolean linkage) { 170 | if (mWheelOptions == null) { 171 | return; 172 | } 173 | mWheelOptions.setPicker(options1Items, options2Items, options3Items, 174 | linkage); 175 | } 176 | 177 | /** 178 | * 设置选中的item位置 179 | */ 180 | public void setSelectOptions(int option1) { 181 | if (mWheelOptions == null) { 182 | return; 183 | } 184 | mWheelOptions.setCurrentItems(option1, 0, 0); 185 | } 186 | 187 | /** 188 | * 设置选中的item位置 189 | */ 190 | public void setSelectOptions(int option1, int option2) { 191 | if (mWheelOptions == null) { 192 | return; 193 | } 194 | mWheelOptions.setCurrentItems(option1, option2, 0); 195 | } 196 | 197 | /** 198 | * 设置选中的item位置 199 | */ 200 | public void setSelectOptions(int option1, int option2, int option3) { 201 | if (mWheelOptions == null) { 202 | return; 203 | } 204 | mWheelOptions.setCurrentItems(option1, option2, option3); 205 | } 206 | 207 | public interface OnOptionsSelectListener { 208 | void onOptionsSelect(int options1, int option2, int options3); 209 | } 210 | 211 | public void setOnoptionsSelectListener(OnOptionsSelectListener optionsSelectListener) { 212 | this.mOptionsSelectListener = optionsSelectListener; 213 | } 214 | 215 | public void clickSubmit(View v) { 216 | if (mOptionsSelectListener != null) { 217 | int[] optionsCurrentItems = mWheelOptions.getCurrentItems(); 218 | mOptionsSelectListener.onOptionsSelect(optionsCurrentItems[0], optionsCurrentItems[1], optionsCurrentItems[2]); 219 | } 220 | dismiss(); 221 | } 222 | 223 | public void clickCancel(View v) { 224 | dismiss(); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/PhoneDatePickerDialog.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib; 2 | 3 | import android.graphics.Color; 4 | import android.graphics.drawable.ColorDrawable; 5 | import android.os.Bundle; 6 | import android.view.Gravity; 7 | import android.view.View; 8 | import android.view.Window; 9 | import android.view.WindowManager; 10 | import android.widget.TextView; 11 | 12 | /** 13 | * 手机定制版日期选择器 14 | * Created by sunjian on 2016/12/22. 15 | */ 16 | 17 | public class PhoneDatePickerDialog extends BaseDatePickerDialog { 18 | 19 | protected TextView mBtnSubmit, mBtnCancel, mTvTitle; 20 | protected View mBgView; 21 | 22 | public static PhoneDatePickerDialog newInstance() { 23 | return newInstance(null); 24 | } 25 | 26 | public static PhoneDatePickerDialog newInstance(Bundle bundle) { 27 | PhoneDatePickerDialog dialog = new PhoneDatePickerDialog(); 28 | dialog.setArguments(bundle == null ? new Bundle() : bundle); 29 | return dialog; 30 | } 31 | 32 | //设置样式 33 | @Override 34 | public void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setStyle(STYLE_NO_TITLE, R.style.PickerDialogStyle); 37 | } 38 | 39 | //实现从底部弹出 40 | @Override 41 | public void onStart() { 42 | super.onStart(); 43 | Window window = getDialog().getWindow(); 44 | if (window != null) { 45 | WindowManager.LayoutParams params = window.getAttributes(); 46 | params.gravity = Gravity.BOTTOM; 47 | params.width = WindowManager.LayoutParams.MATCH_PARENT; 48 | window.setAttributes(params); 49 | window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 50 | } 51 | } 52 | 53 | @Override 54 | protected void initView(View v) { 55 | mBtnSubmit = (TextView) v.findViewById(R.id.btnSubmit); 56 | mBtnCancel = (TextView) v.findViewById(R.id.btnCancel); 57 | mBgView = v.findViewById(R.id.bg_view); 58 | mBtnSubmit.setOnClickListener(new View.OnClickListener() { 59 | @Override 60 | public void onClick(View v) { 61 | clickSubmit(v); 62 | } 63 | }); 64 | mBtnCancel.setOnClickListener(new View.OnClickListener() { 65 | @Override 66 | public void onClick(View v) { 67 | clickCancel(v); 68 | } 69 | }); 70 | //顶部标题 71 | mTvTitle = (TextView) v.findViewById(R.id.tvTitle); 72 | } 73 | 74 | @Override 75 | protected int getResLayoutId() { 76 | return R.layout.pickerview_phone_date; 77 | } 78 | 79 | @Override 80 | protected int getPickerViewId() { 81 | return R.id.phone_time_picker; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/PhoneOptionsPickerDialog.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib; 2 | 3 | import android.graphics.Color; 4 | import android.graphics.drawable.ColorDrawable; 5 | import android.os.Bundle; 6 | import android.view.Gravity; 7 | import android.view.View; 8 | import android.view.Window; 9 | import android.view.WindowManager; 10 | import android.widget.TextView; 11 | 12 | import java.util.ArrayList; 13 | 14 | /** 15 | * 手机选项选择器 16 | * Created by sunjian on 2016/12/23. 17 | */ 18 | 19 | public class PhoneOptionsPickerDialog extends BaseOptionsPickerDialog { 20 | 21 | protected TextView mBtnSubmit, mBtnCancel, mTvTitle; 22 | protected View mBgView; 23 | 24 | public static PhoneOptionsPickerDialog newInstance() { 25 | return newInstance(null); 26 | } 27 | 28 | public static PhoneOptionsPickerDialog newInstance(Bundle bundle) { 29 | return newInstance(bundle, null); 30 | } 31 | 32 | public static PhoneOptionsPickerDialog newInstance(Bundle bundle, ArrayList options1Items) { 33 | return newInstance(bundle, options1Items, null); 34 | } 35 | 36 | public static PhoneOptionsPickerDialog newInstance( 37 | Bundle bundle, ArrayList options1Items, ArrayList> options2Items) { 38 | return newInstance(bundle, options1Items, options2Items, null); 39 | } 40 | 41 | public static PhoneOptionsPickerDialog newInstance( 42 | ArrayList options1Items, ArrayList> options2Items, 43 | ArrayList>> options3Items) { 44 | return newInstance(null, options1Items, options2Items, options3Items); 45 | } 46 | 47 | public static PhoneOptionsPickerDialog newInstance( 48 | Bundle bundle, ArrayList options1Items, ArrayList> options2Items, 49 | ArrayList>> options3Items) { 50 | PhoneOptionsPickerDialog dialog = new PhoneOptionsPickerDialog<>(); 51 | dialog.setArguments(bundle == null ? new Bundle() : bundle); 52 | dialog.mOptions1Items = options1Items; 53 | dialog.mOptions2Items = options2Items; 54 | dialog.mOptions3Items = options3Items; 55 | return dialog; 56 | } 57 | 58 | //设置样式 59 | @Override 60 | public void onCreate(Bundle savedInstanceState) { 61 | super.onCreate(savedInstanceState); 62 | setStyle(STYLE_NO_TITLE, R.style.PickerDialogStyle); 63 | } 64 | 65 | //实现从底部弹出 66 | @Override 67 | public void onStart() { 68 | super.onStart(); 69 | Window window = getDialog().getWindow(); 70 | if (window != null) { 71 | WindowManager.LayoutParams params = window.getAttributes(); 72 | params.gravity = Gravity.BOTTOM; 73 | params.width = WindowManager.LayoutParams.MATCH_PARENT; 74 | window.setAttributes(params); 75 | window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 76 | } 77 | } 78 | 79 | @Override 80 | protected void initView(View v) { 81 | mBgView = v.findViewById(R.id.bg_view); 82 | mBtnSubmit = (TextView) v.findViewById(R.id.btnSubmit); 83 | mBtnCancel = (TextView) v.findViewById(R.id.btnCancel); 84 | mBtnSubmit.setOnClickListener(new View.OnClickListener() { 85 | @Override 86 | public void onClick(View v) { 87 | clickSubmit(v); 88 | } 89 | }); 90 | mBtnCancel.setOnClickListener(new View.OnClickListener() { 91 | @Override 92 | public void onClick(View v) { 93 | clickCancel(v); 94 | } 95 | }); 96 | //顶部标题 97 | mTvTitle = (TextView) v.findViewById(R.id.tvTitle); 98 | } 99 | 100 | @Override 101 | protected int getPickerViewId() { 102 | return R.id.phone_options_picker; 103 | } 104 | 105 | @Override 106 | protected int getResLayoutId() { 107 | return R.layout.pickerview_phone_options; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/TVDatePickerDialog.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | /** 9 | * 电视日期选择器 10 | * Created by sunjian on 2016/12/22. 11 | */ 12 | 13 | public class TVDatePickerDialog extends BaseDatePickerDialog { 14 | 15 | private TextView mBtnOK; 16 | 17 | public static TVDatePickerDialog newInstance() { 18 | return newInstance(null); 19 | } 20 | 21 | public static TVDatePickerDialog newInstance(Bundle bundle) { 22 | TVDatePickerDialog dialog = new TVDatePickerDialog(); 23 | dialog.setArguments(bundle == null ? new Bundle() : bundle); 24 | return dialog; 25 | } 26 | 27 | //设置背景为透明样式,显示圆角 28 | @Override 29 | public void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setStyle(STYLE_NORMAL, R.style.CircleDialogStyle); 32 | } 33 | 34 | @Override 35 | protected int getPickerViewId() { 36 | return R.id.tv_time_picker; 37 | } 38 | 39 | @Override 40 | protected int getResLayoutId() { 41 | return R.layout.pickerview_tv_date; 42 | } 43 | 44 | @Override 45 | protected void initView(final View v) { 46 | mBtnOK = (TextView) v.findViewById(R.id.btnOK); 47 | 48 | mBtnOK.setOnClickListener(new View.OnClickListener() { 49 | @Override 50 | public void onClick(View view) { 51 | clickSubmit(v); 52 | } 53 | }); 54 | 55 | mBtnOK.setOnFocusChangeListener(new View.OnFocusChangeListener() { 56 | @Override 57 | public void onFocusChange(View view, boolean b) { 58 | mBtnOK.setTextColor(b ? Color.WHITE : Color.BLACK); 59 | } 60 | }); 61 | } 62 | 63 | @Override 64 | protected void requestFocus() { 65 | mWheelTime.requestFocus(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/TVOptionsPickerDialog.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | 6 | import java.util.ArrayList; 7 | 8 | /** 9 | * 手机选项选择器 10 | * Created by sunjian on 2016/12/23. 11 | */ 12 | 13 | public class TVOptionsPickerDialog extends BaseOptionsPickerDialog { 14 | 15 | private View mBtnOK; 16 | 17 | public static TVOptionsPickerDialog newInstance() { 18 | return newInstance(null); 19 | } 20 | 21 | public static TVOptionsPickerDialog newInstance(Bundle bundle) { 22 | return newInstance(bundle, null); 23 | } 24 | 25 | public static TVOptionsPickerDialog newInstance(Bundle bundle, ArrayList options1Items) { 26 | return newInstance(bundle, options1Items, null); 27 | } 28 | 29 | public static TVOptionsPickerDialog newInstance( 30 | Bundle bundle, ArrayList options1Items, ArrayList> options2Items) { 31 | return newInstance(bundle, options1Items, options2Items, null); 32 | } 33 | 34 | public static TVOptionsPickerDialog newInstance( 35 | ArrayList options1Items, ArrayList> options2Items, 36 | ArrayList>> options3Items) { 37 | return newInstance(null, options1Items, options2Items, options3Items); 38 | } 39 | 40 | public static TVOptionsPickerDialog newInstance( 41 | Bundle bundle, ArrayList options1Items, ArrayList> options2Items, 42 | ArrayList>> options3Items) { 43 | TVOptionsPickerDialog dialog = new TVOptionsPickerDialog<>(); 44 | dialog.setArguments(bundle == null ? new Bundle() : bundle); 45 | dialog.mOptions1Items = options1Items; 46 | dialog.mOptions2Items = options2Items; 47 | dialog.mOptions3Items = options3Items; 48 | return dialog; 49 | } 50 | 51 | //设置背景为透明样式,显示圆角 52 | @Override 53 | public void onCreate(Bundle savedInstanceState) { 54 | super.onCreate(savedInstanceState); 55 | setStyle(STYLE_NORMAL, R.style.CircleDialogStyle); 56 | } 57 | 58 | @Override 59 | protected void requestFocus() { 60 | mWheelOptions.requestFocus(); 61 | } 62 | 63 | @Override 64 | protected void initView(View v) { 65 | mBtnOK = v.findViewById(R.id.btnOK); 66 | mBtnOK.setOnClickListener(new View.OnClickListener() { 67 | @Override 68 | public void onClick(View v) { 69 | clickSubmit(v); 70 | } 71 | }); 72 | } 73 | 74 | @Override 75 | protected int getPickerViewId() { 76 | return R.id.tv_options_picker; 77 | } 78 | 79 | @Override 80 | protected int getResLayoutId() { 81 | return R.layout.pickerview_tv_options; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/adapter/ArrayWheelAdapter.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.adapter; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * The simple Array wheel adapter 7 | * @param the element type 8 | */ 9 | public class ArrayWheelAdapter implements WheelAdapter { 10 | 11 | /** The default items length */ 12 | public static final int DEFAULT_LENGTH = 4; 13 | 14 | // items 15 | private ArrayList items; 16 | // length 17 | private int length; 18 | 19 | /** 20 | * Constructor 21 | * @param items the items 22 | * @param length the max items length 23 | */ 24 | public ArrayWheelAdapter(ArrayList items, int length) { 25 | this.items = items; 26 | this.length = length; 27 | } 28 | 29 | /** 30 | * Contructor 31 | * @param items the items 32 | */ 33 | public ArrayWheelAdapter(ArrayList items) { 34 | this(items, DEFAULT_LENGTH); 35 | } 36 | 37 | @Override 38 | public Object getItem(int index) { 39 | if (index >= 0 && index < items.size()) { 40 | return items.get(index); 41 | } 42 | return ""; 43 | } 44 | 45 | @Override 46 | public int getItemsCount() { 47 | return items.size(); 48 | } 49 | 50 | @Override 51 | public int indexOf(Object o){ 52 | return items.indexOf(o); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/adapter/NumericWheelAdapter.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.adapter; 2 | 3 | 4 | /** 5 | * Numeric Wheel adapter. 6 | */ 7 | public class NumericWheelAdapter implements WheelAdapter { 8 | 9 | /** The default min value */ 10 | public static final int DEFAULT_MAX_VALUE = 9; 11 | 12 | /** The default max value */ 13 | private static final int DEFAULT_MIN_VALUE = 0; 14 | 15 | // Values 16 | private int minValue; 17 | private int maxValue; 18 | 19 | /** 20 | * Default constructor 21 | */ 22 | public NumericWheelAdapter() { 23 | this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE); 24 | } 25 | 26 | /** 27 | * Constructor 28 | * @param minValue the wheel min value 29 | * @param maxValue the wheel max value 30 | */ 31 | public NumericWheelAdapter(int minValue, int maxValue) { 32 | this.minValue = minValue; 33 | this.maxValue = maxValue; 34 | } 35 | 36 | @Override 37 | public Object getItem(int index) { 38 | if (index >= 0 && index < getItemsCount()) { 39 | int value = minValue + index; 40 | return value; 41 | } 42 | return 0; 43 | } 44 | 45 | @Override 46 | public int getItemsCount() { 47 | return maxValue - minValue + 1; 48 | } 49 | 50 | @Override 51 | public int indexOf(Object o){ 52 | return (int)o - minValue; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/adapter/WheelAdapter.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.adapter; 2 | 3 | public interface WheelAdapter { 4 | /** 5 | * Gets items count 6 | * @return the count of wheel items 7 | */ 8 | int getItemsCount(); 9 | 10 | /** 11 | * Gets a wheel item by index. 12 | * @param index the item index 13 | * @return the wheel item text or null 14 | */ 15 | T getItem(int index); 16 | 17 | /** 18 | * Gets maximum item length. It is used to determine the wheel width. 19 | * If -1 is returned there will be used the default wheel width. 20 | * @param o 21 | * @return the maximum item length or -1 22 | */ 23 | int indexOf(T o); 24 | } 25 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/adapter/WheelOptions.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.view.View; 6 | 7 | import com.sunjian.android_pickview_lib.listener.OnItemSelectedListener; 8 | import com.sunjian.android_pickview_lib.view.WheelView; 9 | 10 | import java.util.ArrayList; 11 | 12 | public class WheelOptions { 13 | private View view; 14 | private WheelView wv_option1; 15 | private WheelView wv_option2; 16 | private WheelView wv_option3; 17 | 18 | private ArrayList mOptions1Items; 19 | private ArrayList> mOptions2Items; 20 | private ArrayList>> mOptions3Items; 21 | 22 | private boolean linkage = false; 23 | private OnItemSelectedListener wheelListener_option1; 24 | private OnItemSelectedListener wheelListener_option2; 25 | 26 | private int textSize = 25; 27 | 28 | public View getView() { 29 | return view; 30 | } 31 | 32 | public void setView(View view) { 33 | this.view = view; 34 | } 35 | 36 | public WheelOptions(View view) { 37 | this(view, 25); 38 | } 39 | 40 | public WheelOptions(View view, int textSize) { 41 | this.view = view; 42 | this.textSize = textSize; 43 | setView(view); 44 | } 45 | 46 | public void setPicker(ArrayList optionsItems) { 47 | setPicker(optionsItems, null, null, false); 48 | } 49 | 50 | public void setPicker(ArrayList options1Items, 51 | ArrayList> options2Items, boolean linkage) { 52 | setPicker(options1Items, options2Items, null, linkage); 53 | } 54 | 55 | public void setPicker(ArrayList options1Items, 56 | ArrayList> options2Items, 57 | ArrayList>> options3Items, 58 | boolean linkage) { 59 | this.linkage = linkage; 60 | this.mOptions1Items = options1Items; 61 | this.mOptions2Items = options2Items; 62 | this.mOptions3Items = options3Items; 63 | int len = ArrayWheelAdapter.DEFAULT_LENGTH; 64 | if (this.mOptions3Items == null) 65 | len = 8; 66 | if (this.mOptions2Items == null) 67 | len = 12; 68 | 69 | Context context = view.getContext(); 70 | Resources resources = context.getResources(); 71 | String packageName = context.getPackageName(); 72 | int options1Id = resources.getIdentifier("options1", "id", packageName); 73 | // 选项1 74 | wv_option1 = (WheelView) view.findViewById(options1Id); 75 | wv_option1.setAdapter(new ArrayWheelAdapter(mOptions1Items, len));// 设置显示数据 76 | wv_option1.setCurrentItem(0);// 初始化时显示的数据 77 | 78 | int options2Id = resources.getIdentifier("options2", "id", packageName); 79 | // 选项2 80 | wv_option2 = (WheelView) view.findViewById(options2Id); 81 | if (mOptions2Items != null) 82 | wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items.get(0)));// 设置显示数据 83 | wv_option2.setCurrentItem(wv_option1.getCurrentItem());// 初始化时显示的数据 84 | 85 | int options3Id = resources.getIdentifier("options3", "id", packageName); 86 | // 选项3 87 | wv_option3 = (WheelView) view.findViewById(options3Id); 88 | if (mOptions3Items != null) 89 | wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items.get(0) 90 | .get(0)));// 设置显示数据 91 | wv_option3.setCurrentItem(wv_option3.getCurrentItem());// 初始化时显示的数据 92 | 93 | wv_option1.setTextSize(textSize); 94 | wv_option2.setTextSize(textSize); 95 | wv_option3.setTextSize(textSize); 96 | 97 | if (this.mOptions2Items == null) 98 | wv_option2.setVisibility(View.GONE); 99 | if (this.mOptions3Items == null) 100 | wv_option3.setVisibility(View.GONE); 101 | 102 | // 联动监听器 103 | wheelListener_option1 = new OnItemSelectedListener() { 104 | 105 | @Override 106 | public void onItemSelected(int index) { 107 | int opt2Select = 0; 108 | if (mOptions2Items != null) { 109 | opt2Select = wv_option2.getCurrentItem();//上一个opt2的选中位置 110 | //新opt2的位置,判断如果旧位置没有超过数据范围,则沿用旧位置,否则选中最后一项 111 | opt2Select = opt2Select >= mOptions2Items.get(index).size() - 1 ? mOptions2Items.get(index).size() - 1 : opt2Select; 112 | 113 | wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items 114 | .get(index))); 115 | wv_option2.setCurrentItem(opt2Select); 116 | } 117 | if (mOptions3Items != null) { 118 | wheelListener_option2.onItemSelected(opt2Select); 119 | } 120 | } 121 | }; 122 | wheelListener_option2 = new OnItemSelectedListener() { 123 | 124 | @Override 125 | public void onItemSelected(int index) { 126 | if (mOptions3Items != null) { 127 | int opt1Select = wv_option1.getCurrentItem(); 128 | opt1Select = opt1Select >= mOptions3Items.size() - 1 ? mOptions3Items.size() - 1 : opt1Select; 129 | index = index >= mOptions2Items.get(opt1Select).size() - 1 ? mOptions2Items.get(opt1Select).size() - 1 : index; 130 | int opt3 = wv_option3.getCurrentItem();//上一个opt3的选中位置 131 | //新opt3的位置,判断如果旧位置没有超过数据范围,则沿用旧位置,否则选中最后一项 132 | opt3 = opt3 >= mOptions3Items.get(opt1Select).get(index).size() - 1 ? mOptions3Items.get(opt1Select).get(index).size() - 1 : opt3; 133 | 134 | wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items 135 | .get(wv_option1.getCurrentItem()).get( 136 | index))); 137 | wv_option3.setCurrentItem(opt3); 138 | 139 | } 140 | } 141 | }; 142 | 143 | // // 添加联动监听 144 | if (options2Items != null && linkage) 145 | wv_option1.setOnItemSelectedListener(wheelListener_option1); 146 | if (options3Items != null && linkage) 147 | wv_option2.setOnItemSelectedListener(wheelListener_option2); 148 | } 149 | 150 | /** 151 | * 设置选项的单位 152 | * 153 | * @param label1 单位 154 | * @param label2 单位 155 | * @param label3 单位 156 | */ 157 | public void setLabels(String label1, String label2, String label3) { 158 | if (label1 != null) 159 | wv_option1.setLabel(label1); 160 | if (label2 != null) 161 | wv_option2.setLabel(label2); 162 | if (label3 != null) 163 | wv_option3.setLabel(label3); 164 | } 165 | 166 | //获取焦点 167 | public void requestFocus() { 168 | wv_option1.requestFocus(); 169 | } 170 | 171 | /** 172 | * 设置是否循环滚动 173 | * 174 | * @param cyclic 是否循环 175 | */ 176 | public void setCyclic(boolean cyclic) { 177 | wv_option1.setCyclic(cyclic); 178 | wv_option2.setCyclic(cyclic); 179 | wv_option3.setCyclic(cyclic); 180 | } 181 | 182 | /** 183 | * 分别设置第一二三级是否循环滚动 184 | * 185 | * @param cyclic1,cyclic2,cyclic3 是否循环 186 | */ 187 | public void setCyclic(boolean cyclic1, boolean cyclic2, boolean cyclic3) { 188 | wv_option1.setCyclic(cyclic1); 189 | wv_option2.setCyclic(cyclic2); 190 | wv_option3.setCyclic(cyclic3); 191 | } 192 | 193 | /** 194 | * 设置第一级是否循环滚动 195 | * 196 | * @param cyclic 是否循环 197 | */ 198 | public void setOption1Cyclic(boolean cyclic) { 199 | wv_option1.setCyclic(cyclic); 200 | } 201 | 202 | 203 | /** 204 | * 设置第二级是否循环滚动 205 | * 206 | * @param cyclic 是否循环 207 | */ 208 | public void setOption2Cyclic(boolean cyclic) { 209 | wv_option2.setCyclic(cyclic); 210 | } 211 | 212 | /** 213 | * 设置第三级是否循环滚动 214 | * 215 | * @param cyclic 是否循环 216 | */ 217 | public void setOption3Cyclic(boolean cyclic) { 218 | wv_option3.setCyclic(cyclic); 219 | } 220 | 221 | /** 222 | * 返回当前选中的结果对应的位置数组 因为支持三级联动效果,分三个级别索引,0,1,2 223 | * 224 | * @return 索引数组 225 | */ 226 | public int[] getCurrentItems() { 227 | int[] currentItems = new int[3]; 228 | currentItems[0] = wv_option1.getCurrentItem(); 229 | currentItems[1] = wv_option2.getCurrentItem(); 230 | currentItems[2] = wv_option3.getCurrentItem(); 231 | return currentItems; 232 | } 233 | 234 | public void setCurrentItems(int option1, int option2, int option3) { 235 | if (linkage) { 236 | itemSelected(option1, option2, option3); 237 | } 238 | wv_option1.setCurrentItem(option1); 239 | wv_option2.setCurrentItem(option2); 240 | wv_option3.setCurrentItem(option3); 241 | } 242 | 243 | private void itemSelected(int opt1Select, int opt2Select, int opt3Select) { 244 | if (mOptions2Items != null) { 245 | wv_option2.setAdapter(new ArrayWheelAdapter(mOptions2Items 246 | .get(opt1Select))); 247 | wv_option2.setCurrentItem(opt2Select); 248 | } 249 | if (mOptions3Items != null) { 250 | wv_option3.setAdapter(new ArrayWheelAdapter(mOptions3Items 251 | .get(opt1Select).get( 252 | opt2Select))); 253 | wv_option3.setCurrentItem(opt3Select); 254 | } 255 | } 256 | 257 | 258 | } 259 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/adapter/WheelTime.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.support.annotation.IntDef; 6 | import android.support.annotation.IntRange; 7 | import android.support.annotation.NonNull; 8 | import android.view.View; 9 | 10 | import com.sunjian.android_pickview_lib.listener.OnItemSelectedListener; 11 | import com.sunjian.android_pickview_lib.view.WheelView; 12 | 13 | import java.lang.annotation.Retention; 14 | import java.lang.annotation.RetentionPolicy; 15 | import java.text.DateFormat; 16 | import java.text.SimpleDateFormat; 17 | import java.util.Arrays; 18 | import java.util.Calendar; 19 | import java.util.Date; 20 | import java.util.List; 21 | import java.util.Locale; 22 | 23 | 24 | public class WheelTime { 25 | 26 | public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA); 27 | private View view; 28 | private WheelView wv_year; 29 | private WheelView wv_month; 30 | private WheelView wv_day; 31 | private WheelView wv_hours; 32 | private WheelView wv_mins; 33 | 34 | // private Type type; 35 | private static final int DEFULT_START_YEAR = 1990; 36 | private static final int DEFULT_END_YEAR = 2100; 37 | private static final int DEFAULT_START_MONTH = 1; 38 | private static final int DEFAULT_END_MONTH = 12; 39 | private static final int DEFAULT_START_DAY = 1; 40 | private static final int DEFAULT_END_DAY = 31; 41 | 42 | 43 | private int startYear = DEFULT_START_YEAR; 44 | private int endYear = DEFULT_END_YEAR; 45 | private int startMonth = DEFAULT_START_MONTH; 46 | private int endMonth = DEFAULT_END_MONTH; 47 | private int startDay = DEFAULT_START_DAY; 48 | private int endDay = DEFAULT_END_DAY; //表示31天的 49 | private int currentYear; 50 | 51 | // 五种选择模式,年月日时分,年月日,时分,月日时分,年月 52 | public static final int ALL = 0; 53 | public static final int YEAR_MONTH_DAY = 1; 54 | public static final int HOURS_MINS = 2; 55 | public static final int MONTH_DAY_HOUR_MIN = 3; 56 | public static final int YEAR_MONTH = 4; 57 | 58 | @IntDef({ALL, YEAR_MONTH_DAY, HOURS_MINS, MONTH_DAY_HOUR_MIN, YEAR_MONTH}) 59 | @Retention(RetentionPolicy.SOURCE) 60 | public @interface DateType { 61 | } 62 | 63 | @DateType 64 | private int type; 65 | 66 | private int textSize = 6; 67 | 68 | public WheelTime(View view) { 69 | this(view, ALL, 6); 70 | } 71 | 72 | public WheelTime(View view, @DateType int type) { 73 | this(view, type, 6); 74 | } 75 | 76 | public WheelTime(View view, @DateType int type, int textSize) { 77 | this.view = view; 78 | this.type = type; 79 | this.textSize = textSize; 80 | setView(view); 81 | } 82 | 83 | public void setPicker(int year, int month, int day) { 84 | this.setPicker(year, month, day, 0, 0); 85 | } 86 | 87 | public void setPicker(int year, int month, int day, int h, int m) { 88 | // 添加大小月月份并将其转换为list,方便之后的判断 89 | String[] months_big = {"1", "3", "5", "7", "8", "10", "12"}; 90 | String[] months_little = {"4", "6", "9", "11"}; 91 | 92 | final List list_big = Arrays.asList(months_big); 93 | final List list_little = Arrays.asList(months_little); 94 | 95 | Context context = view.getContext(); 96 | Resources resources = context.getResources(); 97 | String packageName = context.getPackageName(); 98 | int yearId = resources.getIdentifier("year", "id", packageName); 99 | currentYear = year; 100 | // 年 101 | wv_year = (WheelView) view.findViewById(yearId); 102 | wv_year.setAdapter(new NumericWheelAdapter(startYear, endYear));// 设置"年"的显示数据 103 | wv_year.setCurrentItem(year - startYear);// 初始化时显示的数据 104 | 105 | int monthId = resources.getIdentifier("month", "id", packageName); 106 | // 月 107 | wv_month = (WheelView) view.findViewById(monthId); 108 | if (startYear == endYear) {//开始年等于终止年 109 | wv_month.setAdapter(new NumericWheelAdapter(startMonth, endMonth)); 110 | wv_month.setCurrentItem(month + 1 - startMonth); 111 | } else if (year == startYear) { 112 | //起始日期的月份控制 113 | wv_month.setAdapter(new NumericWheelAdapter(startMonth, 12)); 114 | wv_month.setCurrentItem(month + 1 - startMonth); 115 | } else if (year == endYear) { 116 | //终止日期的月份控制 117 | wv_month.setAdapter(new NumericWheelAdapter(1, endMonth)); 118 | wv_month.setCurrentItem(month); 119 | } else { 120 | wv_month.setAdapter(new NumericWheelAdapter(1, 12)); 121 | wv_month.setCurrentItem(month); 122 | } 123 | int dayId = resources.getIdentifier("day", "id", packageName); 124 | // 日 125 | wv_day = (WheelView) view.findViewById(dayId); 126 | if (startYear == endYear && startMonth == endMonth) { 127 | if (list_big.contains(String.valueOf(month + 1))) { 128 | if (endDay > 31) { 129 | endDay = 31; 130 | } 131 | wv_day.setAdapter(new NumericWheelAdapter(startDay, endDay)); 132 | } else if (list_little.contains(String.valueOf(month + 1))) { 133 | if (endDay > 30) { 134 | endDay = 30; 135 | } 136 | wv_day.setAdapter(new NumericWheelAdapter(startDay, endDay)); 137 | } else { 138 | // 闰年 139 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { 140 | if (endDay > 29) { 141 | endDay = 29; 142 | } 143 | wv_day.setAdapter(new NumericWheelAdapter(startDay, endDay)); 144 | } else { 145 | if (endDay > 28) { 146 | endDay = 28; 147 | } 148 | wv_day.setAdapter(new NumericWheelAdapter(startDay, endDay)); 149 | } 150 | } 151 | wv_day.setCurrentItem(day - startDay); 152 | } else if (year == startYear && month + 1 == startMonth) { 153 | // 起始日期的天数控制 154 | if (list_big.contains(String.valueOf(month + 1))) { 155 | 156 | wv_day.setAdapter(new NumericWheelAdapter(startDay, 31)); 157 | } else if (list_little.contains(String.valueOf(month + 1))) { 158 | 159 | wv_day.setAdapter(new NumericWheelAdapter(startDay, 30)); 160 | } else { 161 | // 闰年 162 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { 163 | 164 | wv_day.setAdapter(new NumericWheelAdapter(startDay, 29)); 165 | } else { 166 | 167 | wv_day.setAdapter(new NumericWheelAdapter(startDay, 28)); 168 | } 169 | } 170 | wv_day.setCurrentItem(day - startDay); 171 | } else if (year == endYear && month + 1 == endMonth) { 172 | // 终止日期的天数控制 173 | if (list_big.contains(String.valueOf(month + 1))) { 174 | if (endDay > 31) { 175 | endDay = 31; 176 | } 177 | wv_day.setAdapter(new NumericWheelAdapter(1, endDay)); 178 | } else if (list_little.contains(String.valueOf(month + 1))) { 179 | if (endDay > 30) { 180 | endDay = 30; 181 | } 182 | wv_day.setAdapter(new NumericWheelAdapter(1, endDay)); 183 | } else { 184 | // 闰年 185 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { 186 | if (endDay > 29) { 187 | endDay = 29; 188 | } 189 | wv_day.setAdapter(new NumericWheelAdapter(1, endDay)); 190 | } else { 191 | if (endDay > 28) { 192 | endDay = 28; 193 | } 194 | wv_day.setAdapter(new NumericWheelAdapter(1, endDay)); 195 | } 196 | } 197 | wv_day.setCurrentItem(day - 1); 198 | } else { 199 | // 判断大小月及是否闰年,用来确定"日"的数据 200 | if (list_big.contains(String.valueOf(month + 1))) { 201 | 202 | wv_day.setAdapter(new NumericWheelAdapter(1, 31)); 203 | } else if (list_little.contains(String.valueOf(month + 1))) { 204 | 205 | wv_day.setAdapter(new NumericWheelAdapter(1, 30)); 206 | } else { 207 | // 闰年 208 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { 209 | 210 | wv_day.setAdapter(new NumericWheelAdapter(1, 29)); 211 | } else { 212 | 213 | wv_day.setAdapter(new NumericWheelAdapter(1, 28)); 214 | } 215 | } 216 | wv_day.setCurrentItem(day - 1); 217 | } 218 | 219 | int hourId = resources.getIdentifier("hour", "id", packageName); 220 | wv_hours = (WheelView) view.findViewById(hourId); 221 | wv_hours.setAdapter(new NumericWheelAdapter(0, 23)); 222 | wv_hours.setCurrentItem(h); 223 | 224 | int minId = resources.getIdentifier("min", "id", packageName); 225 | wv_mins = (WheelView) view.findViewById(minId); 226 | wv_mins.setAdapter(new NumericWheelAdapter(0, 59)); 227 | wv_mins.setCurrentItem(m); 228 | 229 | // 添加"年"监听 230 | OnItemSelectedListener wheelListener_year = new OnItemSelectedListener() { 231 | @Override 232 | public void onItemSelected(int index) { 233 | int year_num = index + startYear; 234 | currentYear = year_num; 235 | int currentMonthItem = wv_month.getCurrentItem();//记录上一次的item位置 236 | // 判断大小月及是否闰年,用来确定"日"的数据 237 | if (startYear == endYear) { 238 | //重新设置月份 239 | wv_month.setAdapter(new NumericWheelAdapter(startMonth, endMonth)); 240 | 241 | if (currentMonthItem > wv_month.getAdapter().getItemsCount() - 1) { 242 | currentMonthItem = wv_month.getAdapter().getItemsCount() - 1; 243 | wv_month.setCurrentItem(currentMonthItem); 244 | } 245 | 246 | int monthNum = currentMonthItem + startMonth; 247 | 248 | if (startMonth == endMonth) { 249 | //重新设置日 250 | setReDay(year_num, monthNum, startDay, endDay, list_big, list_little); 251 | } else if (monthNum == startMonth) { 252 | //重新设置日 253 | setReDay(year_num, monthNum, startDay, 31, list_big, list_little); 254 | } else { 255 | //重新设置日 256 | setReDay(year_num, monthNum, 1, 31, list_big, list_little); 257 | } 258 | } else if (year_num == startYear) {//等于开始的年 259 | //重新设置月份 260 | wv_month.setAdapter(new NumericWheelAdapter(startMonth, 12)); 261 | 262 | if (currentMonthItem > wv_month.getAdapter().getItemsCount() - 1) { 263 | currentMonthItem = wv_month.getAdapter().getItemsCount() - 1; 264 | wv_month.setCurrentItem(currentMonthItem); 265 | } 266 | 267 | int month = currentMonthItem + startMonth; 268 | if (month == startMonth) { 269 | 270 | //重新设置日 271 | setReDay(year_num, month, startDay, 31, list_big, list_little); 272 | } else { 273 | //重新设置日 274 | 275 | setReDay(year_num, month, 1, 31, list_big, list_little); 276 | } 277 | 278 | } else if (year_num == endYear) { 279 | //重新设置月份 280 | wv_month.setAdapter(new NumericWheelAdapter(1, endMonth)); 281 | if (currentMonthItem > wv_month.getAdapter().getItemsCount() - 1) { 282 | currentMonthItem = wv_month.getAdapter().getItemsCount() - 1; 283 | wv_month.setCurrentItem(currentMonthItem); 284 | } 285 | int monthNum = currentMonthItem + 1; 286 | 287 | if (monthNum == endMonth) { 288 | //重新设置日 289 | setReDay(year_num, monthNum, 1, endDay, list_big, list_little); 290 | } else { 291 | //重新设置日 292 | setReDay(year_num, monthNum, 1, 31, list_big, list_little); 293 | } 294 | 295 | } else { 296 | //重新设置月份 297 | wv_month.setAdapter(new NumericWheelAdapter(1, 12)); 298 | //重新设置日 299 | setReDay(year_num, wv_month.getCurrentItem() + 1, 1, 31, list_big, list_little); 300 | 301 | } 302 | 303 | } 304 | }; 305 | // 添加"月"监听 306 | OnItemSelectedListener wheelListener_month = new OnItemSelectedListener() { 307 | @Override 308 | public void onItemSelected(int index) { 309 | int month_num = index + 1; 310 | 311 | if (startYear == endYear) { 312 | month_num = month_num + startMonth - 1; 313 | if (startMonth == endMonth) { 314 | //重新设置日 315 | setReDay(currentYear, month_num, startDay, endDay, list_big, list_little); 316 | } else if (startMonth == month_num) { 317 | 318 | //重新设置日 319 | setReDay(currentYear, month_num, startDay, 31, list_big, list_little); 320 | } else if (endMonth == month_num) { 321 | setReDay(currentYear, month_num, 1, endDay, list_big, list_little); 322 | } else { 323 | setReDay(currentYear, month_num, 1, 31, list_big, list_little); 324 | } 325 | } else if (currentYear == startYear) { 326 | month_num = month_num + startMonth - 1; 327 | if (month_num == startMonth) { 328 | //重新设置日 329 | setReDay(currentYear, month_num, startDay, 31, list_big, list_little); 330 | } else { 331 | //重新设置日 332 | setReDay(currentYear, month_num, 1, 31, list_big, list_little); 333 | } 334 | 335 | } else if (currentYear == endYear) { 336 | if (month_num == endMonth) { 337 | //重新设置日 338 | setReDay(currentYear, wv_month.getCurrentItem() + 1, 1, endDay, list_big, list_little); 339 | } else { 340 | setReDay(currentYear, wv_month.getCurrentItem() + 1, 1, 31, list_big, list_little); 341 | } 342 | 343 | } else { 344 | //重新设置日 345 | setReDay(currentYear, month_num, 1, 31, list_big, list_little); 346 | 347 | } 348 | 349 | 350 | } 351 | }; 352 | wv_year.setOnItemSelectedListener(wheelListener_year); 353 | wv_month.setOnItemSelectedListener(wheelListener_month); 354 | 355 | // 根据屏幕密度来指定选择器字体的大小(不同屏幕可能不同) 356 | switch (type) { 357 | case ALL: 358 | textSize = textSize * 3; 359 | break; 360 | case YEAR_MONTH_DAY: 361 | textSize = textSize * 4; 362 | wv_hours.setVisibility(View.GONE); 363 | wv_mins.setVisibility(View.GONE); 364 | break; 365 | case HOURS_MINS: 366 | textSize = textSize * 4; 367 | wv_year.setVisibility(View.GONE); 368 | wv_month.setVisibility(View.GONE); 369 | wv_day.setVisibility(View.GONE); 370 | break; 371 | case MONTH_DAY_HOUR_MIN: 372 | textSize = textSize * 3; 373 | wv_year.setVisibility(View.GONE); 374 | break; 375 | case YEAR_MONTH: 376 | textSize = textSize * 4; 377 | wv_day.setVisibility(View.GONE); 378 | wv_hours.setVisibility(View.GONE); 379 | wv_mins.setVisibility(View.GONE); 380 | } 381 | wv_day.setTextSize(textSize); 382 | wv_month.setTextSize(textSize); 383 | wv_year.setTextSize(textSize); 384 | wv_hours.setTextSize(textSize); 385 | wv_mins.setTextSize(textSize); 386 | 387 | } 388 | 389 | private void setReDay(int year_num, int monthNum, int startD, int endD, List list_big, List list_little) { 390 | int currentItem = wv_day.getCurrentItem(); 391 | 392 | int maxItem; 393 | if (list_big 394 | .contains(String.valueOf(monthNum))) { 395 | if (endD > 31) { 396 | endD = 31; 397 | } 398 | wv_day.setAdapter(new NumericWheelAdapter(startD, endD)); 399 | maxItem = endD; 400 | } else if (list_little.contains(String.valueOf(monthNum))) { 401 | if (endD > 30) { 402 | endD = 30; 403 | } 404 | wv_day.setAdapter(new NumericWheelAdapter(startD, endD)); 405 | maxItem = endD; 406 | } else { 407 | if ((year_num % 4 == 0 && year_num % 100 != 0) 408 | || year_num % 400 == 0) { 409 | if (endD > 29) { 410 | endD = 29; 411 | } 412 | wv_day.setAdapter(new NumericWheelAdapter(startD, endD)); 413 | maxItem = endD; 414 | } else { 415 | if (endD > 28) { 416 | endD = 28; 417 | } 418 | wv_day.setAdapter(new NumericWheelAdapter(startD, endD)); 419 | maxItem = endD; 420 | } 421 | } 422 | 423 | if (currentItem > wv_day.getAdapter().getItemsCount() - 1) { 424 | currentItem = wv_day.getAdapter().getItemsCount() - 1; 425 | wv_day.setCurrentItem(currentItem); 426 | } 427 | 428 | } 429 | 430 | //获取焦点 431 | public void requestFocus() { 432 | wv_year.requestFocus(); 433 | } 434 | 435 | /** 436 | * 设置是否循环滚动 437 | * 438 | * @param cyclic 439 | */ 440 | public void setCyclic(boolean cyclic) { 441 | wv_year.setCyclic(cyclic); 442 | wv_month.setCyclic(cyclic); 443 | wv_day.setCyclic(cyclic); 444 | wv_hours.setCyclic(cyclic); 445 | wv_mins.setCyclic(cyclic); 446 | } 447 | 448 | public String getTime() { 449 | StringBuilder sb = new StringBuilder(); 450 | if (currentYear == startYear) { 451 | if ((wv_month.getCurrentItem() + startMonth) == startMonth) { 452 | sb.append((wv_year.getCurrentItem() + startYear)).append("-") 453 | .append((wv_month.getCurrentItem() + startMonth)).append("-") 454 | .append((wv_day.getCurrentItem() + startDay)).append(" ") 455 | .append(wv_hours.getCurrentItem()).append(":") 456 | .append(wv_mins.getCurrentItem()); 457 | } else { 458 | sb.append((wv_year.getCurrentItem() + startYear)).append("-") 459 | .append((wv_month.getCurrentItem() + startMonth)).append("-") 460 | .append((wv_day.getCurrentItem() + 1)).append(" ") 461 | .append(wv_hours.getCurrentItem()).append(":") 462 | .append(wv_mins.getCurrentItem()); 463 | } 464 | } else { 465 | sb.append((wv_year.getCurrentItem() + startYear)).append("-") 466 | .append((wv_month.getCurrentItem() + 1)).append("-") 467 | .append((wv_day.getCurrentItem() + 1)).append(" ") 468 | .append(wv_hours.getCurrentItem()).append(":") 469 | .append(wv_mins.getCurrentItem()); 470 | } 471 | 472 | return sb.toString(); 473 | } 474 | 475 | public View getView() { 476 | return view; 477 | } 478 | 479 | public void setView(View view) { 480 | this.view = view; 481 | } 482 | 483 | public int getStartYear() { 484 | return startYear; 485 | } 486 | 487 | public void setStartYear(int startYear) { 488 | this.startYear = startYear; 489 | } 490 | 491 | public int getEndYear() { 492 | return endYear; 493 | } 494 | 495 | public void setEndYear(int endYear) { 496 | this.endYear = endYear; 497 | } 498 | 499 | public void setEndDate(@IntRange(from = 0) long endTime) { 500 | 501 | Calendar endDate = Calendar.getInstance(); 502 | endDate.setTime(new Date(endTime)); 503 | int year = endDate.get(Calendar.YEAR); 504 | int month = endDate.get(Calendar.MONTH) + 1; 505 | int day = endDate.get(Calendar.DAY_OF_MONTH); 506 | 507 | if (year > startYear) { 508 | this.endYear = year; 509 | this.endMonth = month; 510 | this.endDay = day; 511 | } else if (year == startYear) { 512 | if (month > startMonth) { 513 | this.endYear = year; 514 | this.endMonth = month; 515 | this.endDay = day; 516 | } else if (month == startMonth) { 517 | if (day > startDay) { 518 | this.endYear = year; 519 | this.endMonth = month; 520 | this.endDay = day; 521 | } 522 | } 523 | } 524 | } 525 | 526 | public void setEndDate(@NonNull Date endTime) { 527 | 528 | Calendar endDate = Calendar.getInstance(); 529 | endDate.setTime(endTime); 530 | int year = endDate.get(Calendar.YEAR); 531 | int month = endDate.get(Calendar.MONTH) + 1; 532 | int day = endDate.get(Calendar.DAY_OF_MONTH); 533 | 534 | if (year > startYear) { 535 | this.endYear = year; 536 | this.endMonth = month; 537 | this.endDay = day; 538 | } else if (year == startYear) { 539 | if (month > startMonth) { 540 | this.endYear = year; 541 | this.endMonth = month; 542 | this.endDay = day; 543 | } else if (month == startMonth) { 544 | if (day > startDay) { 545 | this.endYear = year; 546 | this.endMonth = month; 547 | this.endDay = day; 548 | } 549 | } 550 | } 551 | } 552 | 553 | public void setEndDate(@NonNull Calendar endDate) { 554 | 555 | int year = endDate.get(Calendar.YEAR); 556 | int month = endDate.get(Calendar.MONTH) + 1; 557 | int day = endDate.get(Calendar.DAY_OF_MONTH); 558 | 559 | if (year > startYear) { 560 | this.endYear = year; 561 | this.endMonth = month; 562 | this.endDay = day; 563 | } else if (year == startYear) { 564 | if (month > startMonth) { 565 | this.endYear = year; 566 | this.endMonth = month; 567 | this.endDay = day; 568 | } else if (month == startMonth) { 569 | if (day > startDay) { 570 | this.endYear = year; 571 | this.endMonth = month; 572 | this.endDay = day; 573 | } 574 | } 575 | } 576 | } 577 | 578 | public void setStartDate(@IntRange(from = 0) long startTime) { 579 | 580 | Calendar startDate = Calendar.getInstance(); 581 | startDate.setTime(new Date(startTime)); 582 | int year = startDate.get(Calendar.YEAR); 583 | int month = startDate.get(Calendar.MONTH) + 1; 584 | int day = startDate.get(Calendar.DAY_OF_MONTH); 585 | 586 | if (year < endYear) { 587 | this.startYear = year; 588 | this.startMonth = month; 589 | this.startDay = day; 590 | } else if (year == endYear) { 591 | if (month < endMonth) { 592 | this.startYear = year; 593 | this.startMonth = month; 594 | this.startDay = day; 595 | } else if (month == endMonth) { 596 | if (day < endDay) { 597 | this.startYear = year; 598 | this.startMonth = month; 599 | this.startDay = day; 600 | } 601 | } 602 | } 603 | } 604 | 605 | public void setStartDate(@NonNull Date startTime) { 606 | 607 | Calendar startDate = Calendar.getInstance(); 608 | startDate.setTime(startTime); 609 | int year = startDate.get(Calendar.YEAR); 610 | int month = startDate.get(Calendar.MONTH) + 1; 611 | int day = startDate.get(Calendar.DAY_OF_MONTH); 612 | 613 | if (year < endYear) { 614 | this.startYear = year; 615 | this.startMonth = month; 616 | this.startDay = day; 617 | } else if (year == endYear) { 618 | if (month < endMonth) { 619 | this.startYear = year; 620 | this.startMonth = month; 621 | this.startDay = day; 622 | } else if (month == endMonth) { 623 | if (day < endDay) { 624 | this.startYear = year; 625 | this.startMonth = month; 626 | this.startDay = day; 627 | } 628 | } 629 | } 630 | } 631 | 632 | public void setStartDate(@NonNull Calendar startDate) { 633 | 634 | int year = startDate.get(Calendar.YEAR); 635 | int month = startDate.get(Calendar.MONTH) + 1; 636 | int day = startDate.get(Calendar.DAY_OF_MONTH); 637 | 638 | if (year < endYear) { 639 | this.startYear = year; 640 | this.startMonth = month; 641 | this.startDay = day; 642 | } else if (year == endYear) { 643 | if (month < endMonth) { 644 | this.startYear = year; 645 | this.startMonth = month; 646 | this.startDay = day; 647 | } else if (month == endMonth) { 648 | if (day < endDay) { 649 | this.startYear = year; 650 | this.startMonth = month; 651 | this.startDay = day; 652 | } 653 | } 654 | } 655 | } 656 | 657 | public void setRangeDate(@NonNull Date startTime, @NonNull Date endTime) { 658 | 659 | if (startTime.getTime() < endTime.getTime()) { 660 | Calendar startDate = Calendar.getInstance(); 661 | startDate.setTime(startTime); 662 | 663 | Calendar endDate = Calendar.getInstance(); 664 | endDate.setTime(endTime); 665 | 666 | this.startYear = startDate.get(Calendar.YEAR); 667 | this.endYear = endDate.get(Calendar.YEAR); 668 | this.startMonth = startDate.get(Calendar.MONTH) + 1; 669 | this.endMonth = endDate.get(Calendar.MONTH) + 1; 670 | this.startDay = startDate.get(Calendar.DAY_OF_MONTH); 671 | this.endDay = endDate.get(Calendar.DAY_OF_MONTH); 672 | } 673 | } 674 | 675 | public void setRangeDate(@NonNull Calendar startDate, @NonNull Calendar endDate) { 676 | 677 | if (startDate.getTimeInMillis() < endDate.getTimeInMillis()) { 678 | this.startYear = startDate.get(Calendar.YEAR); 679 | this.endYear = endDate.get(Calendar.YEAR); 680 | this.startMonth = startDate.get(Calendar.MONTH) + 1; 681 | this.endMonth = endDate.get(Calendar.MONTH) + 1; 682 | this.startDay = startDate.get(Calendar.DAY_OF_MONTH); 683 | this.endDay = endDate.get(Calendar.DAY_OF_MONTH); 684 | } 685 | } 686 | 687 | public void setRangeDate(@IntRange(from = 0) long startTime, @IntRange(from = 0) long endTime) { 688 | 689 | if (startTime < endTime) { 690 | Calendar startDate = Calendar.getInstance(); 691 | startDate.setTime(new Date(startTime)); 692 | 693 | Calendar endDate = Calendar.getInstance(); 694 | endDate.setTime(new Date(endTime)); 695 | 696 | this.startYear = startDate.get(Calendar.YEAR); 697 | this.endYear = endDate.get(Calendar.YEAR); 698 | this.startMonth = startDate.get(Calendar.MONTH) + 1; 699 | this.endMonth = endDate.get(Calendar.MONTH) + 1; 700 | this.startDay = startDate.get(Calendar.DAY_OF_MONTH); 701 | this.endDay = endDate.get(Calendar.DAY_OF_MONTH); 702 | } 703 | } 704 | } 705 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/listener/OnItemSelectedListener.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.listener; 2 | 3 | 4 | public interface OnItemSelectedListener { 5 | void onItemSelected(int index); 6 | } 7 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/model/IPickerViewData.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.model; 2 | 3 | /** 4 | * Created by sunjian on 2016/7/13. 5 | */ 6 | public interface IPickerViewData { 7 | String getPickerViewText(); 8 | } 9 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/view/InertiaTimerTask.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.view; 2 | 3 | import java.util.TimerTask; 4 | 5 | final class InertiaTimerTask extends TimerTask { 6 | 7 | float a; 8 | final float velocityY; 9 | final WheelView loopView; 10 | 11 | InertiaTimerTask(WheelView 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.totalScrollY = loopView.totalScrollY - i; 38 | if (!loopView.isLoop) { 39 | float itemHeight = loopView.itemHeight; 40 | float top = (-loopView.initPosition) * itemHeight; 41 | float bottom = (loopView.getItemsCount() - 1 - loopView.initPosition) * itemHeight; 42 | if(loopView.totalScrollY - itemHeight*0.3 < top){ 43 | top = loopView.totalScrollY + i; 44 | } 45 | else if(loopView.totalScrollY + itemHeight*0.3 > bottom){ 46 | bottom = loopView.totalScrollY + i; 47 | } 48 | 49 | if (loopView.totalScrollY <= top){ 50 | a = 40F; 51 | loopView.totalScrollY = (int)top; 52 | } else if (loopView.totalScrollY >= bottom) { 53 | loopView.totalScrollY = (int)bottom; 54 | a = -40F; 55 | } 56 | } 57 | if (a < 0.0F) { 58 | a = a + 20F; 59 | } else { 60 | a = a - 20F; 61 | } 62 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/view/LoopViewGestureListener.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.view; 2 | 3 | import android.view.MotionEvent; 4 | 5 | final class LoopViewGestureListener extends android.view.GestureDetector.SimpleOnGestureListener { 6 | 7 | final WheelView loopView; 8 | 9 | LoopViewGestureListener(WheelView 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-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/view/MessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.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 WheelView loopview; 13 | 14 | MessageHandler(WheelView 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(WheelView.FLING); 27 | break; 28 | 29 | case WHAT_ITEM_SELECTED: 30 | loopview.onItemSelected(); 31 | break; 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/view/OnItemSelectedRunnable.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.view; 2 | 3 | final class OnItemSelectedRunnable implements Runnable { 4 | final WheelView loopView; 5 | 6 | OnItemSelectedRunnable(WheelView loopview) { 7 | loopView = loopview; 8 | } 9 | 10 | @Override 11 | public final void run() { 12 | loopView.onItemSelectedListener.onItemSelected(loopView.getCurrentItem()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/view/SmoothScrollTimerTask.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.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 WheelView loopView; 11 | 12 | SmoothScrollTimerTask(WheelView 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 | //把要滚动的范围细分成十小份,按是小份单位来重绘 25 | realOffset = (int) ((float) realTotalOffset * 0.1F); 26 | 27 | if (realOffset == 0) { 28 | if (realTotalOffset < 0) { 29 | realOffset = -1; 30 | } else { 31 | realOffset = 1; 32 | } 33 | } 34 | 35 | if (Math.abs(realTotalOffset) <= 1) { 36 | loopView.cancelFuture(); 37 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED); 38 | } else { 39 | loopView.totalScrollY = loopView.totalScrollY + realOffset; 40 | 41 | //这里如果不是循环模式,则点击空白位置需要回滚,不然就会出现选到-1 item的 情况 42 | if (!loopView.isLoop) { 43 | float itemHeight = loopView.itemHeight; 44 | float top = (float) (-loopView.initPosition) * itemHeight; 45 | float bottom = (float) (loopView.getItemsCount() - 1 - loopView.initPosition) * itemHeight; 46 | if (loopView.totalScrollY <= top||loopView.totalScrollY >= bottom) { 47 | loopView.totalScrollY = loopView.totalScrollY - realOffset; 48 | loopView.cancelFuture(); 49 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED); 50 | return; 51 | } 52 | } 53 | loopView.handler.sendEmptyMessage(MessageHandler.WHAT_INVALIDATE_LOOP_VIEW); 54 | realTotalOffset = realTotalOffset - realOffset; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/java/com/sunjian/android_pickview_lib/view/WheelView.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickview_lib.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Canvas; 7 | import android.graphics.Paint; 8 | import android.graphics.Rect; 9 | import android.graphics.Typeface; 10 | import android.os.Handler; 11 | import android.support.annotation.IntDef; 12 | import android.text.TextUtils; 13 | import android.util.AttributeSet; 14 | import android.view.GestureDetector; 15 | import android.view.Gravity; 16 | import android.view.KeyEvent; 17 | import android.view.MotionEvent; 18 | import android.view.View; 19 | 20 | import com.sunjian.android_pickview_lib.R; 21 | import com.sunjian.android_pickview_lib.adapter.WheelAdapter; 22 | import com.sunjian.android_pickview_lib.listener.OnItemSelectedListener; 23 | import com.sunjian.android_pickview_lib.model.IPickerViewData; 24 | 25 | import java.lang.annotation.Retention; 26 | import java.lang.annotation.RetentionPolicy; 27 | import java.util.concurrent.Executors; 28 | import java.util.concurrent.ScheduledExecutorService; 29 | import java.util.concurrent.ScheduledFuture; 30 | import java.util.concurrent.TimeUnit; 31 | 32 | /** 33 | * 3d滚轮控件 34 | */ 35 | public class WheelView extends View { 36 | 37 | // 点击,滑翔(滑到尽头),拖拽事件 38 | public static final int CLICK = 0; 39 | public static final int FLING = 1; 40 | public static final int DAGGLE = 2; 41 | 42 | @IntDef({CLICK, FLING, DAGGLE}) 43 | @Retention(RetentionPolicy.SOURCE) 44 | public @interface Action { 45 | } 46 | 47 | Context context; 48 | 49 | Handler handler; 50 | private GestureDetector gestureDetector; 51 | OnItemSelectedListener onItemSelectedListener; 52 | 53 | // Timer mTimer; 54 | ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor(); 55 | private ScheduledFuture mFuture; 56 | 57 | Paint paintOuterText; 58 | Paint paintCenterText; 59 | Paint paintIndicator; 60 | 61 | WheelAdapter adapter; 62 | 63 | private String label;//附加单位 64 | int textSize;//选项的文字大小 65 | boolean customTextSize;//自定义文字大小,为true则用于使setTextSize函数无效,只能通过xml修改 66 | int maxTextWidth; 67 | int maxTextHeight; 68 | float itemHeight;//每行高度 69 | 70 | int textColorOut; 71 | int textColorCenter; 72 | int textHighLightColorCenter; 73 | int dividerColor; 74 | boolean isRequestFocus = false; 75 | 76 | // 条目间距倍数 77 | static final float lineSpacingMultiplier = 1.4F; 78 | boolean isLoop; 79 | 80 | // 第一条线Y坐标值 81 | float firstLineY; 82 | //第二条线Y坐标 83 | float secondLineY; 84 | //中间Y坐标 85 | float centerY; 86 | 87 | //滚动总高度y值 88 | int totalScrollY; 89 | //初始化默认选中第几个 90 | int initPosition; 91 | //选中的Item是第几个 92 | private int selectedItem; 93 | int preCurrentIndex; 94 | //滚动偏移值,用于记录滚动了多少个item 95 | int change; 96 | 97 | // 显示几个条目 98 | int itemsVisible = 11; 99 | 100 | int measuredHeight; 101 | int measuredWidth; 102 | 103 | // 半圆周长 104 | int halfCircumference; 105 | // 半径 106 | int radius; 107 | 108 | private int mOffset = 0; 109 | private float previousY = 0; 110 | long startTime = 0; 111 | 112 | // 修改这个值可以改变滑行速度 113 | private static final int VELOCITYFLING = 5; 114 | int widthMeasureSpec; 115 | 116 | private int mGravity = Gravity.CENTER; 117 | private int drawCenterContentStart = 0;//中间选中文字开始绘制位置 118 | private int drawOutContentStart = 0;//非中间文字开始绘制位置 119 | private static final float SCALECONTENT = 0.8F;//非中间文字则用此控制高度,压扁形成3d错觉 120 | private static final float CENTERCONTENTOFFSET = 6;//中间文字文字居中需要此偏移值 121 | private static final int LABELOFFSET = 19;//单位偏移值 122 | 123 | private boolean isHasFocus; 124 | 125 | public WheelView(Context context) { 126 | this(context, null); 127 | } 128 | 129 | public WheelView(Context context, AttributeSet attrs) { 130 | super(context, attrs); 131 | Resources resources = getResources(); 132 | textColorOut = resources.getColor(R.color.pickerview_wheelview_textcolor_out); 133 | textColorCenter = resources.getColor(R.color.pickerview_wheelview_textcolor_center); 134 | textHighLightColorCenter = resources.getColor(R.color.pickerview_wheelview_textcolor_highlight_center); 135 | dividerColor = resources.getColor(R.color.pickerview_wheelview_textcolor_divider); 136 | //配合customTextSize使用,customTextSize为true才会发挥效果 137 | textSize = resources.getDimensionPixelSize(R.dimen.pickerview_textsize); 138 | customTextSize = resources.getBoolean(R.bool.pickerview_customTextSize); 139 | if (attrs != null) { 140 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WheelView, 0, 0); 141 | mGravity = a.getInt(R.styleable.WheelView_pickerdialog_gravity, mGravity); 142 | textColorOut = a.getColor(R.styleable.WheelView_pickerdialog_textColorOut, textColorOut); 143 | textColorCenter = a.getColor(R.styleable.WheelView_pickerdialog_textColorCenter, textColorCenter); 144 | textHighLightColorCenter = a.getColor(R.styleable.WheelView_pickerdialog_highLight_textColorCenter, textHighLightColorCenter); 145 | dividerColor = a.getColor(R.styleable.WheelView_pickerdialog_dividerColor, dividerColor); 146 | textSize = a.getDimensionPixelOffset(R.styleable.WheelView_pickerdialog_textSize, textSize); 147 | isRequestFocus = a.getBoolean(R.styleable.WheelView_pickerdialog_isRequestFocus, isRequestFocus); 148 | isLoop = a.getBoolean(R.styleable.WheelView_pickerdialog_isCyclic, isLoop); 149 | label = a.getString(R.styleable.WheelView_pickerdialog_lable); 150 | a.recycle(); 151 | } 152 | 153 | initLoopView(context); 154 | 155 | if (isRequestFocus) { 156 | setFocusable(true); 157 | setFocusableInTouchMode(true); 158 | setOnFocusChangeListener(new OnFocusChangeListener() { 159 | @Override 160 | public void onFocusChange(View view, boolean b) { 161 | isHasFocus = b; 162 | } 163 | }); 164 | } 165 | } 166 | 167 | private void initLoopView(Context context) { 168 | this.context = context; 169 | handler = new MessageHandler(this); 170 | gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this)); 171 | gestureDetector.setIsLongpressEnabled(false); 172 | 173 | totalScrollY = 0; 174 | initPosition = -1; 175 | 176 | initPaints(); 177 | 178 | } 179 | 180 | private void initPaints() { 181 | paintOuterText = new Paint(); 182 | paintOuterText.setColor(textColorOut); 183 | paintOuterText.setAntiAlias(true); 184 | paintOuterText.setTypeface(Typeface.MONOSPACE); 185 | paintOuterText.setTextSize(textSize); 186 | 187 | paintCenterText = new Paint(); 188 | paintCenterText.setColor(textColorCenter); 189 | paintCenterText.setAntiAlias(true); 190 | paintCenterText.setTextScaleX(1.1F); 191 | paintCenterText.setTypeface(Typeface.MONOSPACE); 192 | paintCenterText.setTextSize(textSize); 193 | 194 | paintIndicator = new Paint(); 195 | paintIndicator.setColor(dividerColor); 196 | paintIndicator.setAntiAlias(true); 197 | 198 | if (android.os.Build.VERSION.SDK_INT >= 11) { 199 | setLayerType(LAYER_TYPE_SOFTWARE, null); 200 | } 201 | } 202 | 203 | private void remeasure() { 204 | if (adapter == null) { 205 | return; 206 | } 207 | 208 | measureTextWidthHeight(); 209 | 210 | //最大Text的高度乘间距倍数得到 可见文字实际的总高度,半圆的周长 211 | halfCircumference = (int) (itemHeight * (itemsVisible - 1)); 212 | //整个圆的周长除以PI得到直径,这个直径用作控件的总高度 213 | measuredHeight = (int) ((halfCircumference * 2) / Math.PI); 214 | //求出半径 215 | radius = (int) (halfCircumference / Math.PI); 216 | //控件宽度,这里支持weight 217 | measuredWidth = MeasureSpec.getSize(widthMeasureSpec); 218 | //计算两条横线和控件中间点的Y位置 219 | firstLineY = (measuredHeight - itemHeight) / 2.0F; 220 | secondLineY = (measuredHeight + itemHeight) / 2.0F; 221 | centerY = (measuredHeight + maxTextHeight) / 2.0F - CENTERCONTENTOFFSET; 222 | //初始化显示的item的position,根据是否loop 223 | if (initPosition == -1) { 224 | if (isLoop) { 225 | initPosition = (adapter.getItemsCount() + 1) / 2; 226 | } else { 227 | initPosition = 0; 228 | } 229 | } 230 | 231 | preCurrentIndex = initPosition; 232 | } 233 | 234 | /** 235 | * 计算最大len的Text的宽高度 236 | */ 237 | private void measureTextWidthHeight() { 238 | Rect rect = new Rect(); 239 | for (int i = 0; i < adapter.getItemsCount(); i++) { 240 | String s1 = getContentText(adapter.getItem(i)); 241 | paintCenterText.getTextBounds(s1, 0, s1.length(), rect); 242 | int textWidth = rect.width(); 243 | if (textWidth > maxTextWidth) { 244 | maxTextWidth = textWidth; 245 | } 246 | paintCenterText.getTextBounds("\u661F\u671F", 0, 2, rect); // 星期 247 | int textHeight = rect.height(); 248 | if (textHeight > maxTextHeight) { 249 | maxTextHeight = textHeight; 250 | } 251 | } 252 | itemHeight = lineSpacingMultiplier * maxTextHeight; 253 | } 254 | 255 | void smoothScroll(@Action int action) { 256 | cancelFuture(); 257 | if (action == FLING || action == DAGGLE) { 258 | mOffset = (int) ((totalScrollY % itemHeight + itemHeight) % itemHeight); 259 | if ((float) mOffset > itemHeight / 2.0F) { 260 | mOffset = (int) (itemHeight - (float) mOffset); 261 | } else { 262 | mOffset = -mOffset; 263 | } 264 | } 265 | //停止的时候,位置有偏移,不是全部都能正确停止到中间位置的,这里把文字位置挪回中间去 266 | mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10, TimeUnit.MILLISECONDS); 267 | } 268 | 269 | protected final void scrollBy(float velocityY) { 270 | cancelFuture(); 271 | 272 | mFuture = mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, VELOCITYFLING, TimeUnit.MILLISECONDS); 273 | } 274 | 275 | public void cancelFuture() { 276 | if (mFuture != null && !mFuture.isCancelled()) { 277 | mFuture.cancel(true); 278 | mFuture = null; 279 | } 280 | } 281 | 282 | /** 283 | * 设置是否循环滚动 284 | * 285 | * @param cyclic 是否循环 286 | */ 287 | public final void setCyclic(boolean cyclic) { 288 | isLoop = cyclic; 289 | } 290 | 291 | public final void setTextSize(float size) { 292 | if (size > 0.0F && !customTextSize) { 293 | textSize = (int) (context.getResources().getDisplayMetrics().density * size); 294 | paintOuterText.setTextSize(textSize); 295 | paintCenterText.setTextSize(textSize); 296 | } 297 | } 298 | 299 | public final void setCurrentItem(int currentItem) { 300 | this.initPosition = currentItem; 301 | totalScrollY = 0;//回归顶部,不然重设setCurrentItem的话位置会偏移的,就会显示出不对位置的数据 302 | invalidate(); 303 | } 304 | 305 | public final void setOnItemSelectedListener(OnItemSelectedListener OnItemSelectedListener) { 306 | this.onItemSelectedListener = OnItemSelectedListener; 307 | } 308 | 309 | public final void setAdapter(WheelAdapter adapter) { 310 | this.adapter = adapter; 311 | remeasure(); 312 | invalidate(); 313 | } 314 | 315 | public final WheelAdapter getAdapter() { 316 | return adapter; 317 | } 318 | 319 | public final int getCurrentItem() { 320 | return selectedItem; 321 | } 322 | 323 | protected final void onItemSelected() { 324 | if (onItemSelectedListener != null) { 325 | postDelayed(new OnItemSelectedRunnable(this), 200L); 326 | } 327 | } 328 | 329 | @Override 330 | protected void onDraw(Canvas canvas) { 331 | if (adapter == null) { 332 | return; 333 | } 334 | //可见的item数组 335 | Object visibles[] = new Object[itemsVisible]; 336 | //滚动的Y值高度除去每行Item的高度,得到滚动了多少个item,即change数 337 | change = (int) (totalScrollY / itemHeight); 338 | try { 339 | //滚动中实际的预选中的item(即经过了中间位置的item) = 滑动前的位置 + 滑动相对位置 340 | preCurrentIndex = initPosition + change % adapter.getItemsCount(); 341 | } catch (ArithmeticException e) { 342 | System.out.println("出错了!adapter.getItemsCount() == 0,联动数据不匹配"); 343 | } 344 | if (!isLoop) {//不循环的情况 345 | if (preCurrentIndex < 0) { 346 | preCurrentIndex = 0; 347 | } 348 | if (preCurrentIndex > adapter.getItemsCount() - 1) { 349 | preCurrentIndex = adapter.getItemsCount() - 1; 350 | } 351 | } else {//循环 352 | if (preCurrentIndex < 0) {//举个例子:如果总数是5,preCurrentIndex = -1,那么preCurrentIndex按循环来说,其实是0的上面,也就是4的位置 353 | preCurrentIndex = adapter.getItemsCount() + preCurrentIndex; 354 | } 355 | if (preCurrentIndex > adapter.getItemsCount() - 1) {//同理上面,自己脑补一下 356 | preCurrentIndex = preCurrentIndex - adapter.getItemsCount(); 357 | } 358 | } 359 | 360 | //跟滚动流畅度有关,总滑动距离与每个item高度取余,即并不是一格格的滚动,每个item不一定滚到对应Rect里的,这个item对应格子的偏移值 361 | int itemHeightOffset = (int) (totalScrollY % itemHeight); 362 | // 设置数组中每个元素的值 363 | int counter = 0; 364 | while (counter < itemsVisible) { 365 | int index = preCurrentIndex - (itemsVisible / 2 - counter);//索引值,即当前在控件中间的item看作数据源的中间,计算出相对源数据源的index值 366 | 367 | //判断是否循环,如果是循环数据源也使用相对循环的position获取对应的item值,如果不是循环则超出数据源范围使用""空白字符串填充,在界面上形成空白无数据的item项 368 | if (isLoop) { 369 | index = getLoopMappingIndex(index); 370 | visibles[counter] = adapter.getItem(index); 371 | } else if (index < 0) { 372 | visibles[counter] = ""; 373 | } else if (index > adapter.getItemsCount() - 1) { 374 | visibles[counter] = ""; 375 | } else { 376 | visibles[counter] = adapter.getItem(index); 377 | } 378 | 379 | counter++; 380 | 381 | } 382 | 383 | //中间两条横线 384 | canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator); 385 | canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator); 386 | //单位的Label 387 | if (label != null) { 388 | int drawRightContentStart = measuredWidth - getTextWidth(paintCenterText, label); 389 | //靠右并留出空隙 390 | canvas.drawText(label, drawRightContentStart, centerY, paintCenterText); 391 | } 392 | counter = 0; 393 | while (counter < itemsVisible) { 394 | canvas.save(); 395 | // L(弧长)=α(弧度)* r(半径) (弧度制) 396 | // 求弧度--> (L * π ) / (π * r) (弧长X派/半圆周长) 397 | float itemHeight = maxTextHeight * lineSpacingMultiplier; 398 | double radian = ((itemHeight * counter - itemHeightOffset) * Math.PI) / halfCircumference; 399 | // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限 400 | float angle = (float) (90D - (radian / Math.PI) * 180D); 401 | // 九十度以上的不绘制 402 | if (angle >= 90F || angle <= -90F) { 403 | canvas.restore(); 404 | } else { 405 | 406 | 407 | String contentText = getContentText(visibles[counter]); 408 | int labelWidth = 0; 409 | if (!TextUtils.isEmpty(label)) { 410 | Rect rect = new Rect(); 411 | paintCenterText.getTextBounds(label, 0, label.length(), rect); 412 | labelWidth = rect.width() + LABELOFFSET; 413 | } 414 | //计算开始绘制的位置 415 | measuredCenterContentStart(contentText, labelWidth); 416 | measuredOutContentStart(contentText, labelWidth); 417 | float translateY = (float) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D); 418 | //根据Math.sin(radian)来更改canvas坐标系原点,然后缩放画布,使得文字高度进行缩放,形成弧形3d视觉差 419 | canvas.translate(0.0F, translateY); 420 | canvas.scale(1.0F, (float) Math.sin(radian)); 421 | if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) { 422 | // 条目经过第一条线 423 | canvas.save(); 424 | canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY); 425 | canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT); 426 | canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText); 427 | canvas.restore(); 428 | canvas.save(); 429 | canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight)); 430 | canvas.scale(1.0F, (float) Math.sin(radian) * 1F); 431 | canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText); 432 | canvas.restore(); 433 | } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) { 434 | // 条目经过第二条线 435 | canvas.save(); 436 | canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY); 437 | canvas.scale(1.0F, (float) Math.sin(radian) * 1.0F); 438 | canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText); 439 | canvas.restore(); 440 | canvas.save(); 441 | canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight)); 442 | canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT); 443 | canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText); 444 | canvas.restore(); 445 | } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) { 446 | // 中间条目 447 | canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight)); 448 | if (isHasFocus) { 449 | paintCenterText.setColor(textHighLightColorCenter); 450 | } else { 451 | paintCenterText.setColor(textColorCenter); 452 | } 453 | canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText); 454 | int preSelectedItem = adapter.indexOf(visibles[counter]); 455 | if (preSelectedItem != -1) { 456 | selectedItem = preSelectedItem; 457 | } 458 | paintCenterText.setColor(textColorCenter); 459 | } else { 460 | // 其他条目 461 | canvas.save(); 462 | canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight)); 463 | canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT); 464 | canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText); 465 | canvas.restore(); 466 | } 467 | canvas.restore(); 468 | } 469 | counter++; 470 | } 471 | } 472 | 473 | //递归计算出对应的index 474 | private int getLoopMappingIndex(int index) { 475 | if (index < 0) { 476 | index = index + adapter.getItemsCount(); 477 | index = getLoopMappingIndex(index); 478 | } else if (index > adapter.getItemsCount() - 1) { 479 | index = index - adapter.getItemsCount(); 480 | index = getLoopMappingIndex(index); 481 | } 482 | return index; 483 | } 484 | 485 | /** 486 | * 根据传进来的对象获取getPickerViewText()方法,来获取需要显示的值 487 | * 488 | * @param item 数据源的item 489 | * @return 对应显示的字符串 490 | */ 491 | private String getContentText(Object item) { 492 | if (item == null) { 493 | return ""; 494 | } else if (item instanceof IPickerViewData) { 495 | return ((IPickerViewData) item).getPickerViewText(); 496 | } 497 | return item.toString(); 498 | } 499 | 500 | private void measuredCenterContentStart(String content, int labelWidth) { 501 | Rect rect = new Rect(); 502 | paintCenterText.getTextBounds(content, 0, content.length(), rect); 503 | switch (mGravity) { 504 | case Gravity.CENTER: 505 | drawCenterContentStart = (int) ((measuredWidth - rect.width() - labelWidth) * 0.5); 506 | break; 507 | case Gravity.LEFT: 508 | drawCenterContentStart = 0; 509 | break; 510 | case Gravity.RIGHT: 511 | if (labelWidth == 0) { 512 | labelWidth = LABELOFFSET; 513 | } 514 | drawCenterContentStart = measuredWidth - rect.width() - labelWidth; 515 | break; 516 | } 517 | } 518 | 519 | private void measuredOutContentStart(String content, int labelWidth) { 520 | Rect rect = new Rect(); 521 | paintOuterText.getTextBounds(content, 0, content.length(), rect); 522 | switch (mGravity) { 523 | case Gravity.CENTER: 524 | drawOutContentStart = (int) ((measuredWidth - rect.width() - labelWidth) * 0.5); 525 | break; 526 | case Gravity.LEFT: 527 | drawOutContentStart = 0; 528 | break; 529 | case Gravity.RIGHT: 530 | if (labelWidth == 0) { 531 | labelWidth = LABELOFFSET; 532 | } 533 | drawOutContentStart = measuredWidth - rect.width() - labelWidth; 534 | break; 535 | } 536 | } 537 | 538 | @Override 539 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 540 | this.widthMeasureSpec = widthMeasureSpec; 541 | remeasure(); 542 | setMeasuredDimension(measuredWidth, measuredHeight); 543 | } 544 | 545 | @Override 546 | public boolean onTouchEvent(MotionEvent event) { 547 | boolean eventConsumed = gestureDetector.onTouchEvent(event); 548 | switch (event.getAction()) { 549 | case MotionEvent.ACTION_DOWN: 550 | startTime = System.currentTimeMillis(); 551 | cancelFuture(); 552 | previousY = event.getRawY(); 553 | break; 554 | 555 | case MotionEvent.ACTION_MOVE: 556 | float dy = previousY - event.getRawY(); 557 | previousY = event.getRawY(); 558 | totalScrollY = (int) (totalScrollY + dy); 559 | 560 | // 边界处理。 561 | if (!isLoop) { 562 | float top = -initPosition * itemHeight; 563 | float bottom = (adapter.getItemsCount() - 1 - initPosition) * itemHeight; 564 | if (totalScrollY - itemHeight * 0.3 < top) { 565 | top = totalScrollY - dy; 566 | } else if (totalScrollY + itemHeight * 0.3 > bottom) { 567 | bottom = totalScrollY - dy; 568 | } 569 | 570 | if (totalScrollY < top) { 571 | totalScrollY = (int) top; 572 | } else if (totalScrollY > bottom) { 573 | totalScrollY = (int) bottom; 574 | } 575 | } 576 | break; 577 | 578 | case MotionEvent.ACTION_UP: 579 | default: 580 | if (!eventConsumed) { 581 | float y = event.getY(); 582 | double l = Math.acos((radius - y) / radius) * radius; 583 | int circlePosition = (int) ((l + itemHeight / 2) / itemHeight); 584 | 585 | float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight; 586 | mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset); 587 | 588 | if ((System.currentTimeMillis() - startTime) > 120) { 589 | // 处理拖拽事件 590 | smoothScroll(DAGGLE); 591 | } else { 592 | // 处理条目点击事件 593 | smoothScroll(CLICK); 594 | } 595 | } 596 | break; 597 | } 598 | invalidate(); 599 | 600 | return true; 601 | } 602 | 603 | /** 604 | * 获取Item个数 605 | * 606 | * @return item个数 607 | */ 608 | public int getItemsCount() { 609 | return adapter != null ? adapter.getItemsCount() : 0; 610 | } 611 | 612 | /** 613 | * 附加在右边的单位字符串 614 | * 615 | * @param label 单位 616 | */ 617 | public void setLabel(String label) { 618 | this.label = label; 619 | } 620 | 621 | public void setGravity(int gravity) { 622 | this.mGravity = gravity; 623 | } 624 | 625 | public int getTextWidth(Paint paint, String str) { 626 | int iRet = 0; 627 | if (str != null && str.length() > 0) { 628 | int len = str.length(); 629 | float[] widths = new float[len]; 630 | paint.getTextWidths(str, widths); 631 | for (int j = 0; j < len; j++) { 632 | iRet += (int) Math.ceil(widths[j]); 633 | } 634 | } 635 | return iRet; 636 | } 637 | 638 | @Override 639 | public boolean onKeyDown(int keyCode, KeyEvent event) { 640 | if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { 641 | //下键 642 | int count = getItemsCount(); 643 | int nextItem = isLoop ? getLoopMappingIndex(getCurrentItem() + 1) : getCurrentItem() + 1; 644 | if (!(count == 0 || nextItem >= count)) { 645 | setCurrentItem(nextItem); 646 | handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED); 647 | } 648 | return true; 649 | } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { 650 | //上键 651 | int count = getItemsCount(); 652 | int preItem = isLoop ? getLoopMappingIndex(getCurrentItem() - 1) : getCurrentItem() - 1; 653 | if (!(count == 0 || preItem < 0)) { 654 | setCurrentItem(preItem); 655 | handler.sendEmptyMessage(MessageHandler.WHAT_ITEM_SELECTED); 656 | } 657 | return true; 658 | } 659 | return super.onKeyDown(keyCode, event); 660 | } 661 | 662 | private float dp2px(Context context, float dipValue) { 663 | final float scale = context.getResources().getDisplayMetrics().density; 664 | return dipValue * scale + 0.5f; 665 | } 666 | } -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/anim/dialog_push_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/anim/dialog_push_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/drawable/selector_pickerview_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/drawable/selector_pickerview_ok.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/drawable/shape_ok_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/drawable/shape_ok_unselected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/drawable/shape_tv_layout_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/layout/include_pickerview_topbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 29 | 30 | 41 | 42 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/layout/pickerview_phone_date.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 19 | 20 | 26 | 27 | 33 | 34 | 40 | 41 | 47 | 48 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/layout/pickerview_phone_options.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 19 | 20 | 26 | 27 | 33 | 34 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/layout/pickerview_tv_date.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 21 | 22 | 29 | 30 | 37 | 38 | 45 | 46 | 53 | 54 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/layout/pickerview_tv_options.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 20 | 21 | 27 | 28 | 34 | 35 | 46 | 47 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/values/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #057dff 4 | #c2daf5 5 | #f5f5f5 6 | #000000 7 | #a8a8a8 8 | #2a2a2a 9 | #d5d5d5 10 | 11 | #60000000 12 | 13 | #ed1941 14 | 15 | 16 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 44dp 4 | 5 | 10dp 6 | 7 | 30dp 8 | 9 | 20sp 10 | 21sp 11 | 12 | 20sp 13 | 14 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 取消 3 | 确定 4 | 5 | 6 | 7 | 8 | 9 | 10 | 确定 11 | 12 | -------------------------------------------------------------------------------- /android-pickerdialog/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 31 | 32 | //对话框弹出动画 33 | 34 | 38 | 39 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.2" 6 | defaultConfig { 7 | applicationId "com.sunjian.android_pickerview" 8 | minSdkVersion 14 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:26.1.0' 28 | testCompile 'junit:junit:4.12' 29 | // compile 'com.crazysunj:android-pickerdialog:1.1.1' 30 | compile project(':android-pickerdialog') 31 | } 32 | -------------------------------------------------------------------------------- /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 /Users/sunjian/Downloads/sdk/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/sunjian/android_pickerview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickerview; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.sunjian.android_pickerview", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/sunjian/android_pickerview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickerview; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.Toast; 7 | 8 | import com.sunjian.android_pickerview.bean.PickerViewData; 9 | import com.sunjian.android_pickerview.bean.ProvinceBean; 10 | import com.sunjian.android_pickview_lib.BaseDatePickerDialog; 11 | import com.sunjian.android_pickview_lib.BaseOptionsPickerDialog; 12 | import com.sunjian.android_pickview_lib.PhoneDatePickerDialog; 13 | import com.sunjian.android_pickview_lib.PhoneOptionsPickerDialog; 14 | import com.sunjian.android_pickview_lib.TVDatePickerDialog; 15 | import com.sunjian.android_pickview_lib.TVOptionsPickerDialog; 16 | import com.sunjian.android_pickview_lib.model.IPickerViewData; 17 | 18 | import java.text.SimpleDateFormat; 19 | import java.util.ArrayList; 20 | import java.util.Calendar; 21 | import java.util.Date; 22 | 23 | public class MainActivity extends AppCompatActivity { 24 | 25 | private ArrayList options1Items = new ArrayList<>(); 26 | private ArrayList> options2Items = new ArrayList<>(); 27 | private ArrayList>> options3Items = new ArrayList<>(); 28 | private PhoneDatePickerDialog phoneDatePickerDialog; 29 | private TVDatePickerDialog tvDatePickerDialog; 30 | private PhoneOptionsPickerDialog phoneDialog; 31 | private TVOptionsPickerDialog tvDialog; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_main); 37 | //选项1 38 | options1Items.add(new ProvinceBean(0, "广东", "广东省,以岭南东道、广南东路得名", "其他数据")); 39 | options1Items.add(new ProvinceBean(1, "湖南", "湖南省地处中国中部、长江中游,因大部分区域处于洞庭湖以南而得名湖南", "芒果TV")); 40 | options1Items.add(new ProvinceBean(3, "广西", "嗯~~", "")); 41 | 42 | //选项2 43 | ArrayList options2Items_01 = new ArrayList<>(); 44 | options2Items_01.add("广州"); 45 | options2Items_01.add("佛山"); 46 | options2Items_01.add("东莞"); 47 | options2Items_01.add("阳江"); 48 | options2Items_01.add("珠海"); 49 | ArrayList options2Items_02 = new ArrayList<>(); 50 | options2Items_02.add("长沙"); 51 | options2Items_02.add("岳阳"); 52 | ArrayList options2Items_03 = new ArrayList<>(); 53 | options2Items_03.add("桂林"); 54 | options2Items.add(options2Items_01); 55 | options2Items.add(options2Items_02); 56 | options2Items.add(options2Items_03); 57 | 58 | //选项3 59 | ArrayList> options3Items_01 = new ArrayList<>(); 60 | ArrayList> options3Items_02 = new ArrayList<>(); 61 | ArrayList> options3Items_03 = new ArrayList<>(); 62 | ArrayList options3Items_01_01 = new ArrayList<>(); 63 | options3Items_01_01.add(new PickerViewData("天河")); 64 | options3Items_01_01.add(new PickerViewData("黄埔")); 65 | options3Items_01_01.add(new PickerViewData("海珠")); 66 | options3Items_01_01.add(new PickerViewData("越秀")); 67 | options3Items_01.add(options3Items_01_01); 68 | ArrayList options3Items_01_02 = new ArrayList<>(); 69 | options3Items_01_02.add(new PickerViewData("南海")); 70 | options3Items_01_02.add(new PickerViewData("高明")); 71 | options3Items_01_02.add(new PickerViewData("禅城")); 72 | options3Items_01_02.add(new PickerViewData("桂城")); 73 | options3Items_01.add(options3Items_01_02); 74 | ArrayList options3Items_01_03 = new ArrayList<>(); 75 | options3Items_01_03.add(new PickerViewData("其他")); 76 | options3Items_01_03.add(new PickerViewData("常平")); 77 | options3Items_01_03.add(new PickerViewData("虎门")); 78 | options3Items_01.add(options3Items_01_03); 79 | ArrayList options3Items_01_04 = new ArrayList<>(); 80 | options3Items_01_04.add(new PickerViewData("其他的")); 81 | options3Items_01_04.add(new PickerViewData("其他的")); 82 | options3Items_01_04.add(new PickerViewData("其他的")); 83 | options3Items_01.add(options3Items_01_04); 84 | ArrayList options3Items_01_05 = new ArrayList<>(); 85 | 86 | options3Items_01_05.add(new PickerViewData("其他1")); 87 | options3Items_01_05.add(new PickerViewData("其他2")); 88 | options3Items_01.add(options3Items_01_05); 89 | 90 | ArrayList options3Items_02_01 = new ArrayList<>(); 91 | 92 | options3Items_02_01.add(new PickerViewData("长沙1")); 93 | options3Items_02_01.add(new PickerViewData("长沙2")); 94 | options3Items_02_01.add(new PickerViewData("长沙3")); 95 | options3Items_02_01.add(new PickerViewData("长沙4")); 96 | options3Items_02_01.add(new PickerViewData("长沙5")); 97 | 98 | 99 | options3Items_02.add(options3Items_02_01); 100 | ArrayList options3Items_02_02 = new ArrayList<>(); 101 | 102 | options3Items_02_02.add(new PickerViewData("岳阳")); 103 | options3Items_02_02.add(new PickerViewData("岳阳1")); 104 | options3Items_02_02.add(new PickerViewData("岳阳2")); 105 | options3Items_02_02.add(new PickerViewData("岳阳3")); 106 | options3Items_02_02.add(new PickerViewData("岳阳4")); 107 | options3Items_02_02.add(new PickerViewData("岳阳5")); 108 | 109 | options3Items_02.add(options3Items_02_02); 110 | ArrayList options3Items_03_01 = new ArrayList<>(); 111 | options3Items_03_01.add(new PickerViewData("好山水")); 112 | options3Items_03.add(options3Items_03_01); 113 | 114 | options3Items.add(options3Items_01); 115 | options3Items.add(options3Items_02); 116 | options3Items.add(options3Items_03); 117 | 118 | Bundle bundle = new Bundle(); 119 | bundle.putString(BaseOptionsPickerDialog.LABEL_FIRST, "省"); 120 | bundle.putString(BaseOptionsPickerDialog.LABEL_SECOND, "市"); 121 | bundle.putString(BaseOptionsPickerDialog.LABEL_THIRD, "区"); 122 | 123 | Bundle dBundle = new Bundle(); 124 | Calendar startCal = Calendar.getInstance(); 125 | Calendar endCal = Calendar.getInstance(); 126 | endCal.set(2030, 1, 13); 127 | // dBundle.putLong(PhoneDatePickerDialog.START_TIME_MILLIS, startCal.getTimeInMillis()); 128 | // dBundle.putLong(PhoneDatePickerDialog.END_TIME_MILLIS, endCal.getTimeInMillis()); 129 | // dBundle.putSerializable(PhoneDatePickerDialog.START_TIME_DATE, startCal.getTime()); 130 | // dBundle.putSerializable(PhoneDatePickerDialog.END_TIME_DATE, endCal.getTime()); 131 | dBundle.putSerializable(PhoneDatePickerDialog.START_TIME_CALENDAR, startCal); 132 | dBundle.putSerializable(PhoneDatePickerDialog.END_TIME_CALENDAR, endCal); 133 | phoneDatePickerDialog = PhoneDatePickerDialog.newInstance(dBundle); 134 | phoneDatePickerDialog.setOnTimeSelectListener(new BaseDatePickerDialog.OnTimeSelectListener() { 135 | @Override 136 | public void onTimeSelect(Date date) { 137 | Toast.makeText(MainActivity.this, getTime(date), Toast.LENGTH_SHORT).show(); 138 | } 139 | }); 140 | 141 | findViewById(R.id.tvPhoneTime).setOnClickListener(new View.OnClickListener() { 142 | @Override 143 | public void onClick(View v) { 144 | phoneDatePickerDialog.show(getFragmentManager(), "PhoneDatePickerDialog"); 145 | } 146 | }); 147 | 148 | Bundle dateBundle = new Bundle(); 149 | dateBundle.putInt(BaseDatePickerDialog.BASE_TEXT_SIZE, 4); 150 | 151 | tvDatePickerDialog = TVDatePickerDialog.newInstance(dateBundle); 152 | tvDatePickerDialog.setOnTimeSelectListener(new BaseDatePickerDialog.OnTimeSelectListener() { 153 | @Override 154 | public void onTimeSelect(Date date) { 155 | Toast.makeText(MainActivity.this, getTime(date), Toast.LENGTH_SHORT).show(); 156 | } 157 | }); 158 | 159 | findViewById(R.id.tvTime).setOnClickListener(new View.OnClickListener() { 160 | @Override 161 | public void onClick(View v) { 162 | tvDatePickerDialog.show(getFragmentManager(), "TVDatePickerDialog"); 163 | } 164 | }); 165 | 166 | phoneDialog = MyOptionsDialog.get(bundle, options1Items, options2Items, options3Items); 167 | phoneDialog.setOnoptionsSelectListener(new BaseOptionsPickerDialog.OnOptionsSelectListener() { 168 | @Override 169 | public void onOptionsSelect(int options1, int option2, int options3) { 170 | String address = options1Items.get(options1).getPickerViewText() 171 | + options2Items.get(options1).get(option2) 172 | + options3Items.get(options1).get(option2).get(options3).getPickerViewText(); 173 | Toast.makeText(MainActivity.this, "你选择的地址是:" + address, Toast.LENGTH_SHORT).show(); 174 | } 175 | }); 176 | findViewById(R.id.tvPhoneOptions).setOnClickListener(new View.OnClickListener() { 177 | @Override 178 | public void onClick(View v) { 179 | phoneDialog.show(getFragmentManager(), "PhoneOptionsPickerDialog"); 180 | } 181 | }); 182 | 183 | Bundle optionsBundle = new Bundle(); 184 | optionsBundle.putInt(BaseOptionsPickerDialog.TEXT_SIZE, 20); 185 | optionsBundle.putString(BaseOptionsPickerDialog.LABEL_FIRST, "省"); 186 | optionsBundle.putString(BaseOptionsPickerDialog.LABEL_SECOND, "市"); 187 | optionsBundle.putString(BaseOptionsPickerDialog.LABEL_THIRD, "区"); 188 | 189 | tvDialog = TVOptionsPickerDialog.newInstance(optionsBundle, options1Items, options2Items, options3Items); 190 | tvDialog.setOnoptionsSelectListener(new BaseOptionsPickerDialog.OnOptionsSelectListener() { 191 | @Override 192 | public void onOptionsSelect(int options1, int option2, int options3) { 193 | String address = options1Items.get(options1).getPickerViewText() 194 | + options2Items.get(options1).get(option2) 195 | + options3Items.get(options1).get(option2).get(options3).getPickerViewText(); 196 | Toast.makeText(MainActivity.this, "你选择的地址是:" + address, Toast.LENGTH_SHORT).show(); 197 | } 198 | }); 199 | findViewById(R.id.tvOptions).setOnClickListener(new View.OnClickListener() { 200 | @Override 201 | public void onClick(View v) { 202 | tvDialog.show(getFragmentManager(), "TVOptionsPickerDialog"); 203 | } 204 | }); 205 | } 206 | 207 | public static String getTime(Date date) { 208 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 209 | return format.format(date); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /app/src/main/java/com/sunjian/android_pickerview/MyOptionsDialog.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickerview; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | 6 | import com.sunjian.android_pickerview.bean.ProvinceBean; 7 | import com.sunjian.android_pickview_lib.PhoneOptionsPickerDialog; 8 | import com.sunjian.android_pickview_lib.model.IPickerViewData; 9 | 10 | import java.util.ArrayList; 11 | 12 | /** 13 | * description 14 | *

15 | * Created by sunjian on 2017/6/23. 16 | */ 17 | 18 | public class MyOptionsDialog extends PhoneOptionsPickerDialog { 19 | 20 | public static MyOptionsDialog get( 21 | Bundle bundle, ArrayList options1Items, ArrayList> options2Items, 22 | ArrayList>> options3Items) { 23 | 24 | MyOptionsDialog dialog = new MyOptionsDialog(); 25 | dialog.setArguments(bundle == null ? new Bundle() : bundle); 26 | dialog.mOptions1Items = options1Items; 27 | dialog.mOptions2Items = options2Items; 28 | dialog.mOptions3Items = options3Items; 29 | return dialog; 30 | } 31 | 32 | @Override 33 | protected void initView(View v) { 34 | super.initView(v); 35 | mTvTitle.setText("ABC"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/sunjian/android_pickerview/bean/PickerViewData.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickerview.bean; 2 | 3 | 4 | import com.sunjian.android_pickview_lib.model.IPickerViewData; 5 | 6 | /** 7 | * 8 | * @author sunjian 9 | * create at 2016/12/24 下午3:58 10 | */ 11 | public class PickerViewData implements IPickerViewData { 12 | private String content; 13 | 14 | public PickerViewData(String content) { 15 | this.content = content; 16 | } 17 | 18 | public void setContent(String content) { 19 | this.content = content; 20 | } 21 | 22 | @Override 23 | public String getPickerViewText() { 24 | return content; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/sunjian/android_pickerview/bean/ProvinceBean.java: -------------------------------------------------------------------------------- 1 | package com.sunjian.android_pickerview.bean; 2 | 3 | 4 | import com.sunjian.android_pickview_lib.model.IPickerViewData; 5 | 6 | /** 7 | * 8 | * @author sunjian 9 | * create at 2016/12/24 下午3:59 10 | */ 11 | public class ProvinceBean implements IPickerViewData { 12 | private long id; 13 | private String name; 14 | private String description; 15 | private String others; 16 | 17 | public ProvinceBean(long id,String name,String description,String others){ 18 | this.id = id; 19 | this.name = name; 20 | this.description = description; 21 | this.others = others; 22 | } 23 | 24 | public long getId() { 25 | return id; 26 | } 27 | 28 | public void setId(long id) { 29 | this.id = id; 30 | } 31 | 32 | public String getName() { 33 | return name; 34 | } 35 | 36 | public void setName(String name) { 37 | this.name = name; 38 | } 39 | 40 | public String getDescription() { 41 | return description; 42 | } 43 | 44 | public void setDescription(String description) { 45 | this.description = description; 46 | } 47 | 48 | public String getOthers() { 49 | return others; 50 | } 51 | 52 | public void setOthers(String others) { 53 | this.others = others; 54 | } 55 | 56 | //这个用来显示在PickerView上面的字符串,PickerView会通过IPickerViewData获取getPickerViewText方法显示出来。 57 | @Override 58 | public String getPickerViewText() { 59 | return name; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 |