├── .gitignore ├── LICENSE.txt ├── README-cn.md ├── README.md ├── TimePickerDialog ├── .gitignore ├── bintray.gradle ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── jzxiang │ │ └── pickerview │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── jzxiang │ │ └── pickerview │ │ ├── TimePickerDialog.java │ │ ├── TimeWheel.java │ │ ├── adapters │ │ ├── AbstractWheelAdapter.java │ │ ├── AbstractWheelTextAdapter.java │ │ ├── ArrayWheelAdapter.java │ │ ├── NumericWheelAdapter.java │ │ └── WheelViewAdapter.java │ │ ├── config │ │ ├── DefaultConfig.java │ │ └── PickerConfig.java │ │ ├── data │ │ ├── Type.java │ │ ├── WheelCalendar.java │ │ └── source │ │ │ ├── TimeDataSource.java │ │ │ └── TimeRepository.java │ │ ├── listener │ │ └── OnDateSetListener.java │ │ ├── utils │ │ ├── PickerContants.java │ │ └── Utils.java │ │ └── wheel │ │ ├── ItemsRange.java │ │ ├── OnWheelChangedListener.java │ │ ├── OnWheelClickedListener.java │ │ ├── OnWheelScrollListener.java │ │ ├── WheelRecycle.java │ │ ├── WheelScroller.java │ │ └── WheelView.java │ └── res │ ├── anim │ ├── slide_in_bottom.xml │ └── slide_out_bottom.xml │ ├── drawable │ ├── timepicker_divider_line.xml │ ├── timepicker_sel_text_item.xml │ ├── wheel_bg.xml │ └── wheel_val.xml │ ├── layout │ ├── timepicker_layout.xml │ ├── timepicker_line.xml │ └── timepicker_toolbar.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── style.xml ├── build.gradle ├── change_log.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── preview └── timepickerdialog_demo.gif ├── sample-debug.apk ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wheel │ │ └── pickerview │ │ └── sample │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── jzxiang │ │ └── pickerview │ │ └── sample │ │ └── MainActivity.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 └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | /gradle.properties 6 | .DS_Store 7 | /build 8 | /captures 9 | ### Android ### 10 | # Built application files 11 | *.ap_ 12 | # Files for the Dalvik VM 13 | *.dex 14 | 15 | # Java class files 16 | *.class 17 | 18 | # Generated files 19 | bin/ 20 | gen/ 21 | 22 | # Gradle files 23 | .gradle/ 24 | build/ 25 | 26 | # Local configuration file (sdk path, etc) 27 | local.properties 28 | 29 | # Proguard folder generated by Eclipse 30 | proguard/ 31 | 32 | # Log Files 33 | *.log 34 | 35 | # Android Studio Navigation editor temp files 36 | .navigation/ 37 | 38 | ### Android Patch ### 39 | gen-external-apklibs 40 | 41 | # Created by https://www.gitignore.io/api/intellij 42 | 43 | ### Intellij ### 44 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 45 | 46 | *.iml 47 | 48 | ## Directory-based project format: 49 | .idea/ 50 | # if you remove the above rule, at least ignore the following: 51 | 52 | # User-specific stuff: 53 | # .idea/workspace.xml 54 | # .idea/tasks.xml 55 | # .idea/dictionaries 56 | 57 | # Sensitive or high-churn files: 58 | # .idea/dataSources.ids 59 | # .idea/dataSources.xml 60 | # .idea/sqlDataSources.xml 61 | # .idea/dynamic.xml 62 | # .idea/uiDesigner.xml 63 | 64 | # Gradle: 65 | # .idea/gradle.xml 66 | # .idea/libraries 67 | 68 | # Mongo Explorer plugin: 69 | # .idea/mongoSettings.xml 70 | 71 | ## File-based project format: 72 | *.ipr 73 | *.iws 74 | 75 | ## Plugin-specific files: 76 | 77 | # IntelliJ 78 | /out/ 79 | 80 | # mpeltonen/sbt-idea plugin 81 | .idea_modules/ 82 | 83 | # JIRA plugin 84 | atlassian-ide-plugin.xml 85 | 86 | # Crashlytics plugin (for Android Studio and IntelliJ) 87 | com_crashlytics_export_strings.xml 88 | crashlytics.properties 89 | crashlytics-build.properties 90 | TimePickerDialog/libs/ 91 | sample/libs/ 92 | sample/src/main/res/drawable/ 93 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README-cn.md: -------------------------------------------------------------------------------- 1 | ##### 欢迎关注我 2 | GitHub: https://github.com/JZXiang 3 | 4 | --- 5 | 6 | # TimePickerDialog 7 | 8 | Android时间选择器,支持年月日时分,年月日,年月,月日时分,时分格式,可以设置最小时间和最大时间(精确到分) 9 | 10 | [APK下载](https://github.com/JZXiang/PickerView/raw/master/sample-debug.apk) 11 | 12 | 13 | ## 使用gradle 依赖: 14 | ```java 15 | compile 'com.jzxiang.pickerview:TimePickerDialog:1.0.1' 16 | ``` 17 | ## Demo 图片 18 | ![](https://github.com/JZXiang/PickerView/raw/master/preview/timepickerdialog_demo.gif) 19 | 20 | ## 示例配置 21 | ```java 22 | mDialogAll = new TimePickerDialog.Builder() 23 | .setCallBack(this) 24 | .setCancelStringId("Cancel") 25 | .setSureStringId("Sure") 26 | .setTitleStringId("TimePicker") 27 | .setYearText("Year") 28 | .setMonthText("Month") 29 | .setDayText("Day") 30 | .setHourText("Hour") 31 | .setMinuteText("Minute") 32 | .setCyclic(false) 33 | .setMinMillseconds(System.currentTimeMillis()) 34 | .setMaxMillseconds(System.currentTimeMillis() + tenYears) 35 | .setCurrentMillseconds(System.currentTimeMillis()) 36 | .setThemeColor(getResources().getColor(R.color.timepicker_dialog_bg)) 37 | .setType(Type.ALL) 38 | .setWheelItemTextNormalColor(getResources().getColor(R.color.timetimepicker_default_text_color)) 39 | .setWheelItemTextSelectorColor(getResources().getColor(R.color.timepicker_toolbar_bg)) 40 | .setWheelItemTextSize(12) 41 | .build(); 42 | 43 | ``` 44 | ## 感谢 45 | [android-wheel](https://github.com/maarek/android-wheel) 46 | 47 | License 48 | ------- 49 | 50 | Copyright 2016 JZXiang 51 | 52 | Licensed under the Apache License, Version 2.0 (the "License"); 53 | you may not use this file except in compliance with the License. 54 | You may obtain a copy of the License at 55 | 56 | http://www.apache.org/licenses/LICENSE-2.0 57 | 58 | Unless required by applicable law or agreed to in writing, software 59 | distributed under the License is distributed on an "AS IS" BASIS, 60 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 61 | See the License for the specific language governing permissions and 62 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##### Welcome to follow me on GitHub 2 | GitHub: https://github.com/JZXiang 3 | 4 | --- 5 | 6 | [中文版文档](https://github.com/JZXiang/TimePickerDialog/blob/master/README-cn.md) 7 | # TimePickerDialog 8 | An Android time picker library. 9 | Easy to use. 10 | Support five types. 11 | Able to set the minimun and maximum millseconds. 12 | 13 | [ChangeLog](https://github.com/JZXiang/TimePickerDialog/blob/master/change_log.md) 14 | [Download APK](https://github.com/JZXiang/PickerView/raw/master/sample-debug.apk) 15 | 16 | ## gradle, latest version: 17 | ```java 18 | compile 'com.jzxiang.pickerview:TimePickerDialog:1.0.1' 19 | ``` 20 | ## Demo picture 21 | ![](https://github.com/JZXiang/PickerView/raw/master/preview/timepickerdialog_demo.gif) 22 | 23 | ## An example configuration 24 | ```java 25 | mDialogAll = new TimePickerDialog.Builder() 26 | .setCallBack(this) 27 | .setCancelStringId("Cancel") 28 | .setSureStringId("Sure") 29 | .setTitleStringId("TimePicker") 30 | .setYearText("Year") 31 | .setMonthText("Month") 32 | .setDayText("Day") 33 | .setHourText("Hour") 34 | .setMinuteText("Minute") 35 | .setCyclic(false) 36 | .setMinMillseconds(System.currentTimeMillis()) 37 | .setMaxMillseconds(System.currentTimeMillis() + tenYears) 38 | .setCurrentMillseconds(System.currentTimeMillis()) 39 | .setThemeColor(getResources().getColor(R.color.timepicker_dialog_bg)) 40 | .setType(Type.ALL) 41 | .setWheelItemTextNormalColor(getResources().getColor(R.color.timetimepicker_default_text_color)) 42 | .setWheelItemTextSelectorColor(getResources().getColor(R.color.timepicker_toolbar_bg)) 43 | .setWheelItemTextSize(12) 44 | .build(); 45 | 46 | ``` 47 | ## Thanks 48 | [android-wheel](https://github.com/maarek/android-wheel) 49 | 50 | License 51 | ------- 52 | 53 | Copyright 2016 JZXiang 54 | 55 | Licensed under the Apache License, Version 2.0 (the "License"); 56 | you may not use this file except in compliance with the License. 57 | You may obtain a copy of the License at 58 | 59 | http://www.apache.org/licenses/LICENSE-2.0 60 | 61 | Unless required by applicable law or agreed to in writing, software 62 | distributed under the License is distributed on an "AS IS" BASIS, 63 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 64 | See the License for the specific language governing permissions and 65 | limitations under the License. 66 | 67 | -------------------------------------------------------------------------------- /TimePickerDialog/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /TimePickerDialog/bintray.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.dcendents.android-maven' 2 | apply plugin: 'com.jfrog.bintray' 3 | 4 | group = PROJ_GROUP 5 | version = PROJ_VERSION 6 | project.archivesBaseName = PROJ_ARTIFACTID 7 | 8 | task sourcesJar(type: Jar) { 9 | from android.sourceSets.main.java.srcDirs 10 | classifier = 'sources' 11 | } 12 | 13 | task javadoc(type: Javadoc) { 14 | source = android.sourceSets.main.java.srcDirs 15 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 16 | } 17 | 18 | task javadocJar(type: Jar, dependsOn: javadoc) { 19 | classifier = 'javadoc' 20 | from javadoc.destinationDir 21 | } 22 | 23 | artifacts { 24 | archives javadocJar 25 | archives sourcesJar 26 | 27 | } 28 | 29 | javadoc { 30 | options{ 31 | encoding "UTF-8" 32 | charSet 'UTF-8' 33 | author true 34 | version true 35 | links "http://docs.oracle.com/javase/7/docs/api" 36 | } 37 | } 38 | 39 | install { 40 | repositories.mavenInstaller { 41 | // This generates POM.xml with proper parameters 42 | pom.artifactId = PROJ_ARTIFACTID 43 | pom { 44 | project { 45 | description PROJ_DESCRIPTION 46 | packaging 'aar' 47 | name PROJ_NAME 48 | url PROJ_WEBSITEURL 49 | licenses { 50 | license { 51 | name LICENSE_NAME 52 | url LICENSE_URL 53 | } 54 | } 55 | developers { 56 | developer { 57 | id DEVELOPER_ID 58 | name DEVELOPER_NAME 59 | email DEVELOPER_EMAIL 60 | } 61 | } 62 | scm { 63 | connection PROJ_VCSURL 64 | developerConnection PROJ_VCSURL 65 | url PROJ_WEBSITEURL 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | Properties properties = new Properties() 73 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 74 | 75 | bintray { 76 | user = properties.getProperty("bintray.user") 77 | key = properties.getProperty("bintray.apikey") 78 | 79 | 80 | configurations = ['archives'] 81 | publish = true 82 | 83 | pkg { 84 | repo = "maven" 85 | name = PROJ_NAME 86 | desc = PROJ_DESCRIPTION 87 | websiteUrl = PROJ_WEBSITEURL 88 | issueTrackerUrl = PROJ_ISSUETRACKERURL 89 | vcsUrl = PROJ_VCSURL 90 | licenses = ["Apache-2.0"] 91 | // publicDownloadNumbers = true 92 | publish = true 93 | } 94 | } -------------------------------------------------------------------------------- /TimePickerDialog/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 8 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(dir: 'libs', include: ['*.jar']) 23 | compile 'com.android.support:appcompat-v7:23.3.0' 24 | compile 'com.android.support:support-v4:23.3.0' 25 | } 26 | apply from: 'bintray.gradle' 27 | -------------------------------------------------------------------------------- /TimePickerDialog/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/jzxiang/Library/Android/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 | -------------------------------------------------------------------------------- /TimePickerDialog/src/androidTest/java/com/jzxiang/pickerview/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /TimePickerDialog/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/TimePickerDialog.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.app.DialogFragment; 9 | import android.view.Gravity; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.view.Window; 14 | import android.view.WindowManager; 15 | import android.widget.TextView; 16 | 17 | import com.jzxiang.pickerview.config.PickerConfig; 18 | import com.jzxiang.pickerview.data.Type; 19 | import com.jzxiang.pickerview.data.WheelCalendar; 20 | import com.jzxiang.pickerview.listener.OnDateSetListener; 21 | 22 | import java.util.Calendar; 23 | 24 | /** 25 | * Created by jzxiang on 16/4/19. 26 | */ 27 | public class TimePickerDialog extends DialogFragment implements View.OnClickListener { 28 | PickerConfig mPickerConfig; 29 | private TimeWheel mTimeWheel; 30 | private long mCurrentMillSeconds; 31 | 32 | private static TimePickerDialog newIntance(PickerConfig pickerConfig) { 33 | TimePickerDialog timePickerDialog = new TimePickerDialog(); 34 | timePickerDialog.initialize(pickerConfig); 35 | return timePickerDialog; 36 | } 37 | 38 | @Override 39 | public void onCreate(@Nullable Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | Activity activity = getActivity(); 42 | activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); 43 | 44 | } 45 | 46 | @Override 47 | public void onResume() { 48 | super.onResume(); 49 | int height = getResources().getDimensionPixelSize(R.dimen.picker_height); 50 | 51 | Window window = getDialog().getWindow(); 52 | window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, height);//Here! 53 | window.setGravity(Gravity.BOTTOM); 54 | } 55 | 56 | private void initialize(PickerConfig pickerConfig) { 57 | mPickerConfig = pickerConfig; 58 | } 59 | 60 | @NonNull 61 | @Override 62 | public Dialog onCreateDialog(Bundle savedInstanceState) { 63 | Dialog dialog = new Dialog(getActivity(), R.style.Dialog_NoTitle); 64 | dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); 65 | dialog.setCancelable(true); 66 | dialog.setCanceledOnTouchOutside(true); 67 | dialog.setContentView(initView()); 68 | return dialog; 69 | } 70 | 71 | View initView() { 72 | LayoutInflater inflater = LayoutInflater.from(getContext()); 73 | View view = inflater.inflate(R.layout.timepicker_layout, null); 74 | TextView cancel = (TextView) view.findViewById(R.id.tv_cancel); 75 | cancel.setOnClickListener(this); 76 | TextView sure = (TextView) view.findViewById(R.id.tv_sure); 77 | sure.setOnClickListener(this); 78 | TextView title = (TextView) view.findViewById(R.id.tv_title); 79 | View toolbar = view.findViewById(R.id.toolbar); 80 | 81 | title.setText(mPickerConfig.mTitleString); 82 | cancel.setText(mPickerConfig.mCancelString); 83 | sure.setText(mPickerConfig.mSureString); 84 | toolbar.setBackgroundColor(mPickerConfig.mThemeColor); 85 | 86 | mTimeWheel = new TimeWheel(view, mPickerConfig); 87 | return view; 88 | } 89 | 90 | @Override 91 | public void onClick(View v) { 92 | int i = v.getId(); 93 | if (i == R.id.tv_cancel) { 94 | dismiss(); 95 | } else if (i == R.id.tv_sure) { 96 | sureClicked(); 97 | } 98 | } 99 | 100 | /* 101 | * @desc This method returns the current milliseconds. If current milliseconds is not set, 102 | * this will return the system milliseconds. 103 | * @param none 104 | * @return long - the current milliseconds. 105 | */ 106 | public long getCurrentMillSeconds() { 107 | if (mCurrentMillSeconds == 0) 108 | return System.currentTimeMillis(); 109 | 110 | return mCurrentMillSeconds; 111 | } 112 | 113 | /* 114 | * @desc This method is called when onClick method is invoked by sure button. A Calendar instance is created and 115 | * initialized. 116 | * @param none 117 | * @return none 118 | */ 119 | void sureClicked() { 120 | Calendar calendar = Calendar.getInstance(); 121 | calendar.clear(); 122 | 123 | calendar.set(Calendar.YEAR, mTimeWheel.getCurrentYear()); 124 | calendar.set(Calendar.MONTH, mTimeWheel.getCurrentMonth() - 1); 125 | calendar.set(Calendar.DAY_OF_MONTH, mTimeWheel.getCurrentDay()); 126 | calendar.set(Calendar.HOUR_OF_DAY, mTimeWheel.getCurrentHour()); 127 | calendar.set(Calendar.MINUTE, mTimeWheel.getCurrentMinute()); 128 | 129 | mCurrentMillSeconds = calendar.getTimeInMillis(); 130 | if (mPickerConfig.mCallBack != null) { 131 | mPickerConfig.mCallBack.onDateSet(this, mCurrentMillSeconds); 132 | } 133 | dismiss(); 134 | } 135 | 136 | public static class Builder { 137 | PickerConfig mPickerConfig; 138 | 139 | public Builder() { 140 | mPickerConfig = new PickerConfig(); 141 | } 142 | 143 | public Builder setType(Type type) { 144 | mPickerConfig.mType = type; 145 | return this; 146 | } 147 | 148 | public Builder setThemeColor(int color) { 149 | mPickerConfig.mThemeColor = color; 150 | return this; 151 | } 152 | 153 | public Builder setCancelStringId(String left) { 154 | mPickerConfig.mCancelString = left; 155 | return this; 156 | } 157 | 158 | public Builder setSureStringId(String right) { 159 | mPickerConfig.mSureString = right; 160 | return this; 161 | } 162 | 163 | public Builder setTitleStringId(String title) { 164 | mPickerConfig.mTitleString = title; 165 | return this; 166 | } 167 | 168 | public Builder setToolBarTextColor(int color) { 169 | mPickerConfig.mToolBarTVColor = color; 170 | return this; 171 | } 172 | 173 | public Builder setWheelItemTextNormalColor(int color) { 174 | mPickerConfig.mWheelTVNormalColor = color; 175 | return this; 176 | } 177 | 178 | public Builder setWheelItemTextSelectorColor(int color) { 179 | mPickerConfig.mWheelTVSelectorColor = color; 180 | return this; 181 | } 182 | 183 | public Builder setWheelItemTextSize(int size) { 184 | mPickerConfig.mWheelTVSize = size; 185 | return this; 186 | } 187 | 188 | public Builder setCyclic(boolean cyclic) { 189 | mPickerConfig.cyclic = cyclic; 190 | return this; 191 | } 192 | 193 | public Builder setMinMillseconds(long millseconds) { 194 | mPickerConfig.mMinCalendar = new WheelCalendar(millseconds); 195 | return this; 196 | } 197 | 198 | public Builder setMaxMillseconds(long millseconds) { 199 | mPickerConfig.mMaxCalendar = new WheelCalendar(millseconds); 200 | return this; 201 | } 202 | 203 | public Builder setCurrentMillseconds(long millseconds) { 204 | mPickerConfig.mCurrentCalendar = new WheelCalendar(millseconds); 205 | return this; 206 | } 207 | 208 | public Builder setYearText(String year){ 209 | mPickerConfig.mYear = year; 210 | return this; 211 | } 212 | 213 | public Builder setMonthText(String month){ 214 | mPickerConfig.mMonth = month; 215 | return this; 216 | } 217 | 218 | public Builder setDayText(String day){ 219 | mPickerConfig.mDay = day; 220 | return this; 221 | } 222 | 223 | public Builder setHourText(String hour){ 224 | mPickerConfig.mHour = hour; 225 | return this; 226 | } 227 | 228 | public Builder setMinuteText(String minute){ 229 | mPickerConfig.mMinute = minute; 230 | return this; 231 | } 232 | 233 | public Builder setCallBack(OnDateSetListener listener) { 234 | mPickerConfig.mCallBack = listener; 235 | return this; 236 | } 237 | 238 | public TimePickerDialog build() { 239 | return newIntance(mPickerConfig); 240 | } 241 | 242 | } 243 | 244 | 245 | } 246 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/TimeWheel.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import com.jzxiang.pickerview.adapters.NumericWheelAdapter; 7 | import com.jzxiang.pickerview.config.PickerConfig; 8 | import com.jzxiang.pickerview.data.source.TimeRepository; 9 | import com.jzxiang.pickerview.utils.PickerContants; 10 | import com.jzxiang.pickerview.utils.Utils; 11 | import com.jzxiang.pickerview.wheel.OnWheelChangedListener; 12 | import com.jzxiang.pickerview.wheel.WheelView; 13 | 14 | import java.util.Calendar; 15 | 16 | /** 17 | * Created by jzxiang on 16/4/20. 18 | */ 19 | public class TimeWheel { 20 | Context mContext; 21 | 22 | WheelView year, month, day, hour, minute; 23 | NumericWheelAdapter mYearAdapter, mMonthAdapter, mDayAdapter, mHourAdapter, mMinuteAdapter; 24 | 25 | PickerConfig mPickerConfig; 26 | TimeRepository mRepository; 27 | OnWheelChangedListener yearListener = new OnWheelChangedListener() { 28 | @Override 29 | public void onChanged(WheelView wheel, int oldValue, int newValue) { 30 | updateMonths(); 31 | } 32 | }; 33 | OnWheelChangedListener monthListener = new OnWheelChangedListener() { 34 | @Override 35 | public void onChanged(WheelView wheel, int oldValue, int newValue) { 36 | updateDays(); 37 | } 38 | }; 39 | OnWheelChangedListener dayListener = new OnWheelChangedListener() { 40 | @Override 41 | public void onChanged(WheelView wheel, int oldValue, int newValue) { 42 | updateHours(); 43 | } 44 | }; 45 | OnWheelChangedListener minuteListener = new OnWheelChangedListener() { 46 | @Override 47 | public void onChanged(WheelView wheel, int oldValue, int newValue) { 48 | updateMinutes(); 49 | } 50 | }; 51 | 52 | public TimeWheel(View view, PickerConfig pickerConfig) { 53 | mPickerConfig = pickerConfig; 54 | 55 | mRepository = new TimeRepository(pickerConfig); 56 | mContext = view.getContext(); 57 | initialize(view); 58 | } 59 | 60 | public void initialize(View view) { 61 | initView(view); 62 | initYear(); 63 | initMonth(); 64 | initDay(); 65 | initHour(); 66 | initMinute(); 67 | } 68 | 69 | 70 | void initView(View view) { 71 | year = (WheelView) view.findViewById(R.id.year); 72 | month = (WheelView) view.findViewById(R.id.month); 73 | day = (WheelView) view.findViewById(R.id.day); 74 | hour = (WheelView) view.findViewById(R.id.hour); 75 | minute = (WheelView) view.findViewById(R.id.minute); 76 | 77 | switch (mPickerConfig.mType) { 78 | case ALL: 79 | 80 | break; 81 | case YEAR_MONTH_DAY: 82 | Utils.hideViews(hour, minute); 83 | break; 84 | case YEAR_MONTH: 85 | Utils.hideViews(day, hour, minute); 86 | break; 87 | case MONTH_DAY_HOUR_MIN: 88 | Utils.hideViews(year); 89 | break; 90 | case HOURS_MINS: 91 | Utils.hideViews(year, month, day); 92 | break; 93 | case YEAR: 94 | Utils.hideViews(month, day, hour, minute); 95 | break; 96 | } 97 | 98 | year.addChangingListener(yearListener); 99 | year.addChangingListener(monthListener); 100 | year.addChangingListener(dayListener); 101 | year.addChangingListener(minuteListener); 102 | month.addChangingListener(monthListener); 103 | month.addChangingListener(dayListener); 104 | month.addChangingListener(minuteListener); 105 | day.addChangingListener(dayListener); 106 | day.addChangingListener(minuteListener); 107 | hour.addChangingListener(minuteListener); 108 | } 109 | 110 | void initYear() { 111 | int minYear = mRepository.getMinYear(); 112 | int maxYear = mRepository.getMaxYear(); 113 | 114 | mYearAdapter = new NumericWheelAdapter(mContext, minYear, maxYear, PickerContants.FORMAT, mPickerConfig.mYear); 115 | mYearAdapter.setConfig(mPickerConfig); 116 | year.setViewAdapter(mYearAdapter); 117 | year.setCurrentItem(mRepository.getDefaultCalendar().year - minYear); 118 | } 119 | 120 | void initMonth() { 121 | updateMonths(); 122 | int curYear = getCurrentYear(); 123 | int minMonth = mRepository.getMinMonth(curYear); 124 | month.setCurrentItem(mRepository.getDefaultCalendar().month - minMonth); 125 | month.setCyclic(mPickerConfig.cyclic); 126 | } 127 | 128 | void initDay() { 129 | updateDays(); 130 | int curYear = getCurrentYear(); 131 | int curMonth = getCurrentMonth(); 132 | 133 | int minDay = mRepository.getMinDay(curYear, curMonth); 134 | day.setCurrentItem(mRepository.getDefaultCalendar().day - minDay); 135 | day.setCyclic(mPickerConfig.cyclic); 136 | } 137 | 138 | void initHour() { 139 | updateHours(); 140 | int curYear = getCurrentYear(); 141 | int curMonth = getCurrentMonth(); 142 | int curDay = getCurrentDay(); 143 | 144 | int minHour = mRepository.getMinHour(curYear, curMonth, curDay); 145 | hour.setCurrentItem(mRepository.getDefaultCalendar().hour - minHour); 146 | hour.setCyclic(mPickerConfig.cyclic); 147 | } 148 | 149 | void initMinute() { 150 | updateMinutes(); 151 | int curYear = getCurrentYear(); 152 | int curMonth = getCurrentMonth(); 153 | int curDay = getCurrentDay(); 154 | int curHour = getCurrentHour(); 155 | int minMinute = mRepository.getMinMinute(curYear, curMonth, curDay, curHour); 156 | 157 | minute.setCurrentItem(mRepository.getDefaultCalendar().minute - minMinute); 158 | minute.setCyclic(mPickerConfig.cyclic); 159 | 160 | } 161 | 162 | void updateMonths() { 163 | if (month.getVisibility() == View.GONE) 164 | return; 165 | 166 | int curYear = getCurrentYear(); 167 | int minMonth = mRepository.getMinMonth(curYear); 168 | int maxMonth = mRepository.getMaxMonth(curYear); 169 | mMonthAdapter = new NumericWheelAdapter(mContext, minMonth, maxMonth, PickerContants.FORMAT, mPickerConfig.mMonth); 170 | mMonthAdapter.setConfig(mPickerConfig); 171 | month.setViewAdapter(mMonthAdapter); 172 | 173 | if (mRepository.isMinYear(curYear)) { 174 | month.setCurrentItem(0, false); 175 | } 176 | } 177 | 178 | void updateDays() { 179 | if (day.getVisibility() == View.GONE) 180 | return; 181 | 182 | int curYear = getCurrentYear(); 183 | int curMonth = getCurrentMonth(); 184 | 185 | Calendar calendar = Calendar.getInstance(); 186 | calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + year.getCurrentItem()); 187 | calendar.set(Calendar.MONTH, curMonth); 188 | 189 | int maxDay = mRepository.getMaxDay(curYear, curMonth); 190 | int minDay = mRepository.getMinDay(curYear, curMonth); 191 | mDayAdapter = new NumericWheelAdapter(mContext, minDay, maxDay, PickerContants.FORMAT, mPickerConfig.mDay); 192 | mDayAdapter.setConfig(mPickerConfig); 193 | day.setViewAdapter(mDayAdapter); 194 | 195 | if (mRepository.isMinMonth(curYear, curMonth)) { 196 | day.setCurrentItem(0, true); 197 | } 198 | 199 | int dayCount = mDayAdapter.getItemsCount(); 200 | if (day.getCurrentItem() >= dayCount) { 201 | day.setCurrentItem(dayCount - 1, true); 202 | } 203 | } 204 | 205 | void updateHours() { 206 | if (hour.getVisibility() == View.GONE) 207 | return; 208 | 209 | int curYear = getCurrentYear(); 210 | int curMonth = getCurrentMonth(); 211 | int curDay = getCurrentDay(); 212 | 213 | int minHour = mRepository.getMinHour(curYear, curMonth, curDay); 214 | int maxHour = mRepository.getMaxHour(curYear, curMonth, curDay); 215 | 216 | mHourAdapter = new NumericWheelAdapter(mContext, minHour, maxHour, PickerContants.FORMAT, mPickerConfig.mHour); 217 | mHourAdapter.setConfig(mPickerConfig); 218 | hour.setViewAdapter(mHourAdapter); 219 | 220 | if (mRepository.isMinDay(curYear, curMonth, curDay)) 221 | hour.setCurrentItem(0, false); 222 | } 223 | 224 | void updateMinutes() { 225 | if (minute.getVisibility() == View.GONE) 226 | return; 227 | 228 | int curYear = getCurrentYear(); 229 | int curMonth = getCurrentMonth(); 230 | int curDay = getCurrentDay(); 231 | int curHour = getCurrentHour(); 232 | 233 | int minMinute = mRepository.getMinMinute(curYear, curMonth, curDay, curHour); 234 | int maxMinute = mRepository.getMaxMinute(curYear, curMonth, curDay, curHour); 235 | 236 | mMinuteAdapter = new NumericWheelAdapter(mContext, minMinute, maxMinute, PickerContants.FORMAT, mPickerConfig.mMinute); 237 | mMinuteAdapter.setConfig(mPickerConfig); 238 | minute.setViewAdapter(mMinuteAdapter); 239 | 240 | if (mRepository.isMinHour(curYear, curMonth, curDay, curHour)) 241 | minute.setCurrentItem(0, false); 242 | } 243 | 244 | public int getCurrentYear() { 245 | return year.getCurrentItem() + mRepository.getMinYear(); 246 | } 247 | 248 | public int getCurrentMonth() { 249 | int curYear = getCurrentYear(); 250 | return month.getCurrentItem() + +mRepository.getMinMonth(curYear); 251 | } 252 | 253 | public int getCurrentDay() { 254 | int curYear = getCurrentYear(); 255 | int curMonth = getCurrentMonth(); 256 | return day.getCurrentItem() + mRepository.getMinDay(curYear, curMonth); 257 | } 258 | 259 | public int getCurrentHour() { 260 | int curYear = getCurrentYear(); 261 | int curMonth = getCurrentMonth(); 262 | int curDay = getCurrentDay(); 263 | return hour.getCurrentItem() + mRepository.getMinHour(curYear, curMonth, curDay); 264 | } 265 | 266 | public int getCurrentMinute() { 267 | int curYear = getCurrentYear(); 268 | int curMonth = getCurrentMonth(); 269 | int curDay = getCurrentDay(); 270 | int curHour = getCurrentHour(); 271 | 272 | return minute.getCurrentItem() + mRepository.getMinMinute(curYear, curMonth, curDay, curHour); 273 | } 274 | 275 | 276 | } 277 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/adapters/AbstractWheelAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Yuri Kanivets 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.jzxiang.pickerview.adapters; 18 | 19 | import android.database.DataSetObserver; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | 23 | import java.util.LinkedList; 24 | import java.util.List; 25 | 26 | /** 27 | * Abstract Wheel adapter. 28 | */ 29 | public abstract class AbstractWheelAdapter implements WheelViewAdapter { 30 | // Observers 31 | private List datasetObservers; 32 | 33 | @Override 34 | public View getEmptyItem(View convertView, ViewGroup parent) { 35 | return null; 36 | } 37 | 38 | @Override 39 | public void registerDataSetObserver(DataSetObserver observer) { 40 | if (datasetObservers == null) { 41 | datasetObservers = new LinkedList(); 42 | } 43 | datasetObservers.add(observer); 44 | } 45 | 46 | @Override 47 | public void unregisterDataSetObserver(DataSetObserver observer) { 48 | if (datasetObservers != null) { 49 | datasetObservers.remove(observer); 50 | } 51 | } 52 | 53 | /** 54 | * Notifies observers about data changing 55 | */ 56 | protected void notifyDataChangedEvent() { 57 | if (datasetObservers != null) { 58 | for (DataSetObserver observer : datasetObservers) { 59 | observer.onChanged(); 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Notifies observers about invalidating data 66 | */ 67 | protected void notifyDataInvalidatedEvent() { 68 | if (datasetObservers != null) { 69 | for (DataSetObserver observer : datasetObservers) { 70 | observer.onInvalidated(); 71 | } 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/adapters/AbstractWheelTextAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Yuri Kanivets 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jzxiang.pickerview.adapters; 17 | 18 | import android.content.Context; 19 | import android.util.Log; 20 | import android.view.Gravity; 21 | import android.view.LayoutInflater; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | import android.widget.TextView; 25 | 26 | import com.jzxiang.pickerview.R; 27 | import com.jzxiang.pickerview.config.PickerConfig; 28 | 29 | /** 30 | * Abstract wheel adapter provides common functionality for adapters. 31 | */ 32 | public abstract class AbstractWheelTextAdapter extends AbstractWheelAdapter { 33 | 34 | /** 35 | * Text view resource. Used as a default view for adapter. 36 | */ 37 | public static final int TEXT_VIEW_ITEM_RESOURCE = -1; 38 | 39 | /** 40 | * Default text color 41 | */ 42 | public static final int LABEL_COLOR = 0xFF700070; 43 | /** 44 | * Default text size 45 | */ 46 | public static final int DEFAULT_TEXT_SIZE = 24; 47 | /** 48 | * No resource constant. 49 | */ 50 | protected static final int NO_RESOURCE = 0; 51 | // Current context 52 | protected Context context; 53 | // Layout inflater 54 | protected LayoutInflater inflater; 55 | // Items resources 56 | protected int itemResourceId; 57 | protected int itemTextResourceId; 58 | // Empty items resources 59 | protected int emptyItemResourceId; 60 | 61 | private int padding = 0; 62 | 63 | private PickerConfig mPickerConfig; 64 | 65 | /** 66 | * Constructor 67 | * 68 | * @param context the current context 69 | */ 70 | protected AbstractWheelTextAdapter(Context context) { 71 | this(context, TEXT_VIEW_ITEM_RESOURCE); 72 | } 73 | 74 | /** 75 | * Constructor 76 | * 77 | * @param context the current context 78 | * @param itemResource the resource ID for a layout file containing a TextView to use when instantiating items views 79 | */ 80 | protected AbstractWheelTextAdapter(Context context, int itemResource) { 81 | this(context, itemResource, NO_RESOURCE); 82 | } 83 | 84 | /** 85 | * Constructor 86 | * 87 | * @param context the current context 88 | * @param itemResource the resource ID for a layout file containing a TextView to use when instantiating items views 89 | * @param itemTextResource the resource ID for a text view in the item layout 90 | */ 91 | protected AbstractWheelTextAdapter(Context context, int itemResource, int itemTextResource) { 92 | this.context = context; 93 | itemResourceId = itemResource; 94 | itemTextResourceId = itemTextResource; 95 | padding = context.getResources().getDimensionPixelSize(R.dimen.textview_default_padding); 96 | 97 | inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 98 | } 99 | 100 | /** 101 | * Gets resource Id for items views 102 | * 103 | * @return the item resource Id 104 | */ 105 | public int getItemResource() { 106 | return itemResourceId; 107 | } 108 | 109 | /** 110 | * Sets resource Id for items views 111 | * 112 | * @param itemResourceId the resource Id to set 113 | */ 114 | public void setItemResource(int itemResourceId) { 115 | this.itemResourceId = itemResourceId; 116 | } 117 | 118 | /** 119 | * Gets resource Id for text view in item layout 120 | * 121 | * @return the item text resource Id 122 | */ 123 | public int getItemTextResource() { 124 | return itemTextResourceId; 125 | } 126 | 127 | /** 128 | * Sets resource Id for text view in item layout 129 | * 130 | * @param itemTextResourceId the item text resource Id to set 131 | */ 132 | public void setItemTextResource(int itemTextResourceId) { 133 | this.itemTextResourceId = itemTextResourceId; 134 | } 135 | 136 | /** 137 | * Gets resource Id for empty items views 138 | * 139 | * @return the empty item resource Id 140 | */ 141 | public int getEmptyItemResource() { 142 | return emptyItemResourceId; 143 | } 144 | 145 | /** 146 | * Sets resource Id for empty items views 147 | * 148 | * @param emptyItemResourceId the empty item resource Id to set 149 | */ 150 | public void setEmptyItemResource(int emptyItemResourceId) { 151 | this.emptyItemResourceId = emptyItemResourceId; 152 | } 153 | 154 | 155 | /** 156 | * Returns text for specified item 157 | * 158 | * @param index the item index 159 | * @return the text of specified items 160 | */ 161 | protected abstract CharSequence getItemText(int index); 162 | 163 | @Override 164 | public View getItem(int index, View convertView, ViewGroup parent) { 165 | if (index >= 0 && index < getItemsCount()) { 166 | if (convertView == null) { 167 | convertView = getView(itemResourceId, parent); 168 | } 169 | TextView textView = getTextView(convertView, itemTextResourceId); 170 | if (textView != null) { 171 | CharSequence text = getItemText(index); 172 | if (text == null) { 173 | text = ""; 174 | } 175 | textView.setText(text); 176 | if (itemResourceId == TEXT_VIEW_ITEM_RESOURCE) { 177 | configureTextView(textView); 178 | } 179 | } 180 | return convertView; 181 | } 182 | return null; 183 | } 184 | 185 | @Override 186 | public View getEmptyItem(View convertView, ViewGroup parent) { 187 | if (convertView == null) { 188 | convertView = getView(emptyItemResourceId, parent); 189 | } 190 | if (emptyItemResourceId == TEXT_VIEW_ITEM_RESOURCE && convertView instanceof TextView) { 191 | configureTextView((TextView) convertView); 192 | } 193 | 194 | return convertView; 195 | } 196 | 197 | 198 | /** 199 | * Configures text view. Is called for the TEXT_VIEW_ITEM_RESOURCE views. 200 | * 201 | * @param view the text view to be configured 202 | */ 203 | protected void configureTextView(TextView view) { 204 | if (mPickerConfig == null) 205 | mPickerConfig = new PickerConfig(); 206 | view.setTextColor(mPickerConfig.mWheelTVNormalColor); 207 | 208 | view.setGravity(Gravity.CENTER); 209 | view.setPadding(0, padding, 0, padding); 210 | view.setTextSize(mPickerConfig.mWheelTVSize); 211 | view.setLines(1); 212 | // view.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD); 213 | } 214 | 215 | /** 216 | * Loads a text view from view 217 | * 218 | * @param view the text view or layout containing it 219 | * @param textResource the text resource Id in layout 220 | * @return the loaded text view 221 | */ 222 | private TextView getTextView(View view, int textResource) { 223 | TextView text = null; 224 | try { 225 | if (textResource == NO_RESOURCE && view instanceof TextView) { 226 | text = (TextView) view; 227 | } else if (textResource != NO_RESOURCE) { 228 | text = (TextView) view.findViewById(textResource); 229 | } 230 | } catch (ClassCastException e) { 231 | Log.e("AbstractWheelAdapter", "You must supply a resource ID for a TextView"); 232 | throw new IllegalStateException( 233 | "AbstractWheelAdapter requires the resource ID to be a TextView", e); 234 | } 235 | 236 | return text; 237 | } 238 | 239 | /** 240 | * Loads view from resources 241 | * 242 | * @param resource the resource Id 243 | * @return the loaded view or null if resource is not set 244 | */ 245 | private View getView(int resource, ViewGroup parent) { 246 | switch (resource) { 247 | case NO_RESOURCE: 248 | return null; 249 | case TEXT_VIEW_ITEM_RESOURCE: 250 | return new TextView(context); 251 | default: 252 | return inflater.inflate(resource, parent, false); 253 | } 254 | } 255 | 256 | @Override 257 | public PickerConfig getConfig() { 258 | if (mPickerConfig == null) 259 | mPickerConfig = new PickerConfig(); 260 | return mPickerConfig; 261 | } 262 | 263 | @Override 264 | public void setConfig(PickerConfig config) { 265 | mPickerConfig = config; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/adapters/ArrayWheelAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Yuri Kanivets 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jzxiang.pickerview.adapters; 17 | 18 | import android.content.Context; 19 | 20 | /** 21 | * The simple Array wheel adapter 22 | * 23 | * @param the element type 24 | */ 25 | public class ArrayWheelAdapter extends AbstractWheelTextAdapter { 26 | 27 | // items 28 | private T items[]; 29 | 30 | /** 31 | * Constructor 32 | * 33 | * @param context the current context 34 | * @param items the items 35 | */ 36 | public ArrayWheelAdapter(Context context, T items[]) { 37 | super(context); 38 | 39 | //setEmptyItemResource(TEXT_VIEW_ITEM_RESOURCE); 40 | this.items = items; 41 | } 42 | 43 | @Override 44 | public CharSequence getItemText(int index) { 45 | if (index >= 0 && index < items.length) { 46 | T item = items[index]; 47 | if (item instanceof CharSequence) { 48 | return (CharSequence) item; 49 | } 50 | return item.toString(); 51 | } 52 | return null; 53 | } 54 | 55 | @Override 56 | public int getItemsCount() { 57 | return items.length; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/adapters/NumericWheelAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Yuri Kanivets 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.jzxiang.pickerview.adapters; 18 | 19 | import android.content.Context; 20 | import android.text.TextUtils; 21 | 22 | /** 23 | * Numeric Wheel adapter. 24 | */ 25 | public class NumericWheelAdapter extends AbstractWheelTextAdapter { 26 | 27 | /** 28 | * The default min value 29 | */ 30 | public static final int DEFAULT_MAX_VALUE = 9; 31 | 32 | /** 33 | * The default max value 34 | */ 35 | private static final int DEFAULT_MIN_VALUE = 0; 36 | 37 | // Values 38 | private int minValue; 39 | private int maxValue; 40 | 41 | // format 42 | private String format; 43 | //unit 44 | private String unit; 45 | 46 | 47 | /** 48 | * Constructor 49 | * 50 | * @param context the current context 51 | */ 52 | public NumericWheelAdapter(Context context) { 53 | this(context, DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE); 54 | } 55 | 56 | /** 57 | * Constructor 58 | * 59 | * @param context the current context 60 | * @param minValue the wheel min value 61 | * @param maxValue the wheel max value 62 | */ 63 | public NumericWheelAdapter(Context context, int minValue, int maxValue) { 64 | this(context, minValue, maxValue, null); 65 | } 66 | 67 | /** 68 | * Constructor 69 | * 70 | * @param context the current context 71 | * @param minValue the wheel min value 72 | * @param maxValue the wheel max value 73 | * @param format the format string 74 | */ 75 | public NumericWheelAdapter(Context context, int minValue, int maxValue, String format) { 76 | this(context, minValue, maxValue, format, null); 77 | } 78 | 79 | /** 80 | * Constructor 81 | * 82 | * @param context the current context 83 | * @param minValue the wheel min value 84 | * @param maxValue the wheel max value 85 | * @param format the format string 86 | * @param unit the wheel unit value 87 | */ 88 | public NumericWheelAdapter(Context context, int minValue, int maxValue, String format, String unit) { 89 | super(context); 90 | this.minValue = minValue; 91 | this.maxValue = maxValue; 92 | this.format = format; 93 | this.unit = unit; 94 | } 95 | 96 | @Override 97 | public CharSequence getItemText(int index) { 98 | if (index >= 0 && index < getItemsCount()) { 99 | int value = minValue + index; 100 | String text = !TextUtils.isEmpty(format) ? String.format(format, value) : Integer.toString(value); 101 | text = TextUtils.isEmpty(unit) ? text : text + unit; 102 | 103 | return text; 104 | } 105 | return null; 106 | } 107 | 108 | @Override 109 | public int getItemsCount() { 110 | return maxValue - minValue + 1; 111 | } 112 | 113 | 114 | } 115 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/adapters/WheelViewAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Yuri Kanivets 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.jzxiang.pickerview.adapters; 18 | 19 | import android.database.DataSetObserver; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | 23 | import com.jzxiang.pickerview.config.PickerConfig; 24 | 25 | /** 26 | * Wheel items adapter interface 27 | */ 28 | public interface WheelViewAdapter { 29 | /** 30 | * Gets items count 31 | * 32 | * @return the count of wheel items 33 | */ 34 | int getItemsCount(); 35 | 36 | /** 37 | * Get a View that displays the data at the specified position in the data set 38 | * 39 | * @param index the item index 40 | * @param convertView the old view to reuse if possible 41 | * @param parent the parent that this view will eventually be attached to 42 | * @return the wheel item View 43 | */ 44 | View getItem(int index, View convertView, ViewGroup parent); 45 | 46 | /** 47 | * Get a View that displays an empty wheel item placed before the first or after 48 | * the last wheel item. 49 | * 50 | * @param convertView the old view to reuse if possible 51 | * @param parent the parent that this view will eventually be attached to 52 | * @return the empty item View 53 | */ 54 | View getEmptyItem(View convertView, ViewGroup parent); 55 | 56 | /** 57 | * Register an observer that is called when changes happen to the data used by this adapter. 58 | * 59 | * @param observer the observer to be registered 60 | */ 61 | void registerDataSetObserver(DataSetObserver observer); 62 | 63 | /** 64 | * Unregister an observer that has previously been registered 65 | * 66 | * @param observer the observer to be unregistered 67 | */ 68 | void unregisterDataSetObserver(DataSetObserver observer); 69 | 70 | PickerConfig getConfig(); 71 | 72 | void setConfig(PickerConfig config); 73 | 74 | } 75 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/config/DefaultConfig.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.config; 2 | 3 | import com.jzxiang.pickerview.data.Type; 4 | 5 | /** 6 | * Created by jzxiang on 16/5/15. 7 | */ 8 | public class DefaultConfig { 9 | public static final Type TYPE = Type.ALL; 10 | public static final int COLOR = 0XFFE60012; 11 | public static final int TOOLBAR_TV_COLOR = 0xFFFFFFFF; 12 | public static final int TV_NORMAL_COLOR = 0xFF999999; 13 | public static final int TV_SELECTOR_COLOR = 0XFF404040; 14 | public static final int TV_SIZE = 12; 15 | public static final boolean CYCLIC = true; 16 | public static String CANCEL = "取消"; 17 | public static String SURE = "确定"; 18 | public static String TITLE = "TimePicker"; 19 | public static String YEAR = "年"; 20 | public static String MONTH = "月"; 21 | public static String DAY = "日"; 22 | public static String HOUR = "时"; 23 | public static String MINUTE = "分"; 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/config/PickerConfig.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.config; 2 | 3 | import com.jzxiang.pickerview.data.Type; 4 | import com.jzxiang.pickerview.data.WheelCalendar; 5 | import com.jzxiang.pickerview.listener.OnDateSetListener; 6 | 7 | 8 | /** 9 | * Created by jzxiang on 16/5/15. 10 | */ 11 | public class PickerConfig { 12 | 13 | public Type mType = DefaultConfig.TYPE; 14 | public int mThemeColor = DefaultConfig.COLOR; 15 | 16 | public String mCancelString = DefaultConfig.CANCEL; 17 | public String mSureString = DefaultConfig.SURE; 18 | public String mTitleString = DefaultConfig.TITLE; 19 | public int mToolBarTVColor = DefaultConfig.TOOLBAR_TV_COLOR; 20 | 21 | public int mWheelTVNormalColor = DefaultConfig.TV_NORMAL_COLOR; 22 | public int mWheelTVSelectorColor = DefaultConfig.TV_SELECTOR_COLOR; 23 | public int mWheelTVSize = DefaultConfig.TV_SIZE; 24 | public boolean cyclic = DefaultConfig.CYCLIC; 25 | 26 | public String mYear = DefaultConfig.YEAR; 27 | public String mMonth = DefaultConfig.MONTH; 28 | public String mDay = DefaultConfig.DAY; 29 | public String mHour = DefaultConfig.HOUR; 30 | public String mMinute = DefaultConfig.MINUTE; 31 | 32 | /** 33 | * The min timeMillseconds 34 | */ 35 | public WheelCalendar mMinCalendar = new WheelCalendar(0); 36 | 37 | /** 38 | * The max timeMillseconds 39 | */ 40 | public WheelCalendar mMaxCalendar = new WheelCalendar(0); 41 | 42 | /** 43 | * The default selector timeMillseconds 44 | */ 45 | public WheelCalendar mCurrentCalendar = new WheelCalendar(System.currentTimeMillis()); 46 | 47 | public OnDateSetListener mCallBack; 48 | } 49 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/data/Type.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.data; 2 | 3 | /** 4 | * Created by jzxiang on 16/4/21. 5 | */ 6 | public enum Type { 7 | // 五种选择模式,年月日时分,年月日,时分,月日时分,年月 8 | ALL, 9 | YEAR_MONTH_DAY, 10 | HOURS_MINS, 11 | MONTH_DAY_HOUR_MIN, 12 | YEAR_MONTH, 13 | YEAR 14 | } 15 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/data/WheelCalendar.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.data; 2 | 3 | import java.util.Calendar; 4 | 5 | /** 6 | * Created by jzxiang on 16/4/19. 7 | */ 8 | public class WheelCalendar { 9 | 10 | public int year, month, day, hour, minute; 11 | 12 | private boolean noRange; 13 | 14 | public WheelCalendar(long millseconds) { 15 | initData(millseconds); 16 | } 17 | 18 | private void initData(long millseconds) { 19 | if (millseconds == 0) { 20 | noRange = true; 21 | return; 22 | } 23 | Calendar calendar = Calendar.getInstance(); 24 | calendar.clear(); 25 | calendar.setTimeInMillis(millseconds); 26 | 27 | year = calendar.get(Calendar.YEAR); 28 | month = calendar.get(Calendar.MONTH) + 1; 29 | day = calendar.get(Calendar.DAY_OF_MONTH); 30 | hour = calendar.get(Calendar.HOUR_OF_DAY); 31 | minute = calendar.get(Calendar.MINUTE); 32 | } 33 | 34 | public boolean isNoRange() { 35 | return noRange; 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/data/source/TimeDataSource.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.data.source; 2 | 3 | import com.jzxiang.pickerview.data.WheelCalendar; 4 | 5 | /** 6 | * Created by jzxiang on 16/6/13. 7 | */ 8 | public interface TimeDataSource { 9 | 10 | int getMinYear(); 11 | 12 | int getMaxYear(); 13 | 14 | int getMinMonth(int currentYear); 15 | 16 | int getMaxMonth(int currentYear); 17 | 18 | int getMinDay(int year, int month); 19 | 20 | int getMaxDay(int year, int month); 21 | 22 | int getMinHour(int year, int month, int day); 23 | 24 | int getMaxHour(int year, int month, int day); 25 | 26 | int getMinMinute(int year, int month, int day, int hour); 27 | 28 | int getMaxMinute(int year, int month, int day, int hour); 29 | 30 | boolean isMinYear(int year); 31 | 32 | boolean isMinMonth(int year, int month); 33 | 34 | boolean isMinDay(int year, int month, int day); 35 | 36 | boolean isMinHour(int year, int month, int day, int hour); 37 | // 38 | // boolean isMaxYear(int year); 39 | // 40 | // boolean isMaxMonth(int year, int month); 41 | // 42 | // boolean isMaxDay(int year, int month, int day); 43 | // 44 | // boolean isMaxMinute(int year, int month, int day, int hour); 45 | 46 | WheelCalendar getDefaultCalendar(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/data/source/TimeRepository.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.data.source; 2 | 3 | import com.jzxiang.pickerview.config.PickerConfig; 4 | import com.jzxiang.pickerview.data.WheelCalendar; 5 | import com.jzxiang.pickerview.utils.PickerContants; 6 | import com.jzxiang.pickerview.utils.Utils; 7 | 8 | import java.util.Calendar; 9 | 10 | /** 11 | * Created by jzxiang on 16/6/13. 12 | */ 13 | public class TimeRepository implements TimeDataSource { 14 | PickerConfig mPickerConfig; 15 | WheelCalendar mCalendarMin, mCalendarMax; 16 | 17 | boolean mIsMinNoRange, mIsMaxNoRange; 18 | 19 | public TimeRepository(PickerConfig pickerConfig) { 20 | mPickerConfig = pickerConfig; 21 | mCalendarMin = pickerConfig.mMinCalendar; 22 | mCalendarMax = pickerConfig.mMaxCalendar; 23 | 24 | mIsMinNoRange = mCalendarMin.isNoRange(); 25 | mIsMaxNoRange = mCalendarMax.isNoRange(); 26 | } 27 | 28 | @Override 29 | public int getMinYear() { 30 | if (mIsMinNoRange) 31 | return PickerContants.DEFAULT_MIN_YEAR; 32 | else 33 | return mCalendarMin.year; 34 | } 35 | 36 | @Override 37 | public int getMaxYear() { 38 | if (mIsMaxNoRange) 39 | return getMinYear() + PickerContants.YEAR_COUNT; 40 | 41 | return mCalendarMax.year; 42 | } 43 | 44 | @Override 45 | public int getMinMonth(int year) { 46 | if (!mIsMinNoRange && Utils.isTimeEquals(mCalendarMin, year)) 47 | return mCalendarMin.month; 48 | 49 | return PickerContants.MIN_MONTH; 50 | } 51 | 52 | @Override 53 | public int getMaxMonth(int year) { 54 | if (!mIsMaxNoRange && Utils.isTimeEquals(mCalendarMax, year)) 55 | return mCalendarMax.month; 56 | 57 | return PickerContants.MAX_MONTH; 58 | } 59 | 60 | @Override 61 | public int getMinDay(int year, int month) { 62 | if (!mIsMinNoRange && Utils.isTimeEquals(mCalendarMin, year, month)) 63 | return mCalendarMin.day; 64 | 65 | return PickerContants.MIN_DAY; 66 | } 67 | 68 | @Override 69 | public int getMaxDay(int year, int month) { 70 | if (!mIsMaxNoRange && Utils.isTimeEquals(mCalendarMax, year, month)) 71 | return mCalendarMax.day; 72 | 73 | Calendar calendar = Calendar.getInstance(); 74 | calendar.set(Calendar.YEAR, year); 75 | calendar.set(Calendar.DATE, 1); 76 | calendar.set(Calendar.MONTH, month - 1); 77 | 78 | return calendar.getActualMaximum(Calendar.DAY_OF_MONTH); 79 | } 80 | 81 | @Override 82 | public int getMinHour(int year, int month, int day) { 83 | if (!mIsMinNoRange && Utils.isTimeEquals(mCalendarMin, year, month, day)) 84 | return mCalendarMin.hour; 85 | else 86 | return PickerContants.MIN_HOUR; 87 | } 88 | 89 | @Override 90 | public int getMaxHour(int year, int month, int day) { 91 | if (!mIsMaxNoRange && Utils.isTimeEquals(mCalendarMax, year, month, day)) 92 | return mCalendarMax.hour; 93 | 94 | return PickerContants.MAX_HOUR; 95 | } 96 | 97 | @Override 98 | public int getMinMinute(int year, int month, int day, int hour) { 99 | if (!mIsMinNoRange && Utils.isTimeEquals(mCalendarMin, year, month, day, hour)) 100 | return mCalendarMin.minute + 1; 101 | else 102 | return PickerContants.MIN_MINUTE; 103 | } 104 | 105 | @Override 106 | public int getMaxMinute(int year, int month, int day, int hour) { 107 | if (!mIsMaxNoRange && Utils.isTimeEquals(mCalendarMax, year, month, day, hour)) 108 | return mCalendarMax.minute; 109 | 110 | return PickerContants.MAX_MINUTE; 111 | } 112 | 113 | @Override 114 | public boolean isMinYear(int year) { 115 | return Utils.isTimeEquals(mCalendarMin, year); 116 | } 117 | 118 | @Override 119 | public boolean isMinMonth(int year, int month) { 120 | return Utils.isTimeEquals(mCalendarMin, year, month); 121 | } 122 | 123 | @Override 124 | public boolean isMinDay(int year, int month, int day) { 125 | return Utils.isTimeEquals(mCalendarMin, year, month, day); 126 | } 127 | 128 | @Override 129 | public boolean isMinHour(int year, int month, int day, int hour) { 130 | return Utils.isTimeEquals(mCalendarMin, year, month, day, hour); 131 | } 132 | 133 | 134 | @Override 135 | public WheelCalendar getDefaultCalendar() { 136 | return mPickerConfig.mCurrentCalendar; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/listener/OnDateSetListener.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.listener; 2 | 3 | import com.jzxiang.pickerview.TimePickerDialog; 4 | 5 | import java.util.Calendar; 6 | 7 | /** 8 | * Created by jzxiang on 16/4/20. 9 | */ 10 | public interface OnDateSetListener { 11 | 12 | void onDateSet(TimePickerDialog timePickerView, long millseconds); 13 | } 14 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/utils/PickerContants.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.utils; 2 | 3 | /** 4 | * Created by jzxiang on 16/4/20. 5 | */ 6 | public class PickerContants { 7 | public static final String FORMAT = "%02d"; 8 | 9 | public static final int DEFAULT_MIN_YEAR = 2015; 10 | public static final int YEAR_COUNT = 50; 11 | public static final int MIN_MONTH = 1; 12 | public static final int MAX_MONTH = 12; 13 | public static final int MIN_DAY = MIN_MONTH; 14 | public static final int MIN_HOUR = 0; 15 | public static final int MAX_HOUR = 23; 16 | public static final int MIN_MINUTE = 0; 17 | public static final int MAX_MINUTE = 59; 18 | } 19 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.utils; 2 | 3 | import android.view.View; 4 | 5 | import com.jzxiang.pickerview.data.WheelCalendar; 6 | 7 | /** 8 | * Created by jzxiang on 16/4/20. 9 | */ 10 | public class Utils { 11 | 12 | public static boolean isTimeEquals(WheelCalendar calendar, int... params) { 13 | switch (params.length) { 14 | case 1: 15 | return calendar.year == params[0]; 16 | case 2: 17 | return calendar.year == params[0] && 18 | calendar.month == params[1]; 19 | case 3: 20 | return calendar.year == params[0] && 21 | calendar.month == params[1] && 22 | calendar.day == params[2]; 23 | case 4: 24 | return calendar.year == params[0] && 25 | calendar.month == params[1] && 26 | calendar.day == params[2] && 27 | calendar.hour == params[3]; 28 | } 29 | return false; 30 | } 31 | 32 | public static void hideViews(View... views) { 33 | for (int i = 0; i < views.length; i++) { 34 | views[i].setVisibility(View.GONE); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/wheel/ItemsRange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Android Wheel Control. 3 | * https://code.google.com/p/android-wheel/ 4 | * 5 | * Copyright 2011 Yuri Kanivets 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package com.jzxiang.pickerview.wheel; 21 | 22 | /** 23 | * Range for visible items. 24 | */ 25 | public class ItemsRange { 26 | // First item number 27 | private int first; 28 | 29 | // Items count 30 | private int count; 31 | 32 | /** 33 | * Default constructor. Creates an empty range 34 | */ 35 | public ItemsRange() { 36 | this(0, 0); 37 | } 38 | 39 | /** 40 | * Constructor 41 | * 42 | * @param first the number of first item 43 | * @param count the count of items 44 | */ 45 | public ItemsRange(int first, int count) { 46 | this.first = first; 47 | this.count = count; 48 | } 49 | 50 | /** 51 | * Gets number of first item 52 | * 53 | * @return the number of the first item 54 | */ 55 | public int getFirst() { 56 | return first; 57 | } 58 | 59 | /** 60 | * Gets number of last item 61 | * 62 | * @return the number of last item 63 | */ 64 | public int getLast() { 65 | return getFirst() + getCount() - 1; 66 | } 67 | 68 | /** 69 | * Get items count 70 | * 71 | * @return the count of items 72 | */ 73 | public int getCount() { 74 | return count; 75 | } 76 | 77 | /** 78 | * Tests whether item is contained by range 79 | * 80 | * @param index the item number 81 | * @return true if item is contained 82 | */ 83 | public boolean contains(int index) { 84 | return index >= getFirst() && index <= getLast(); 85 | } 86 | } -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/wheel/OnWheelChangedListener.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.wheel; 2 | 3 | /** 4 | * Wheel changed listener interface. 5 | * The onChanged() method is called whenever current wheel positions is changed: 6 | * New Wheel position is set 7 | * Wheel view is scrolled 8 | */ 9 | public interface OnWheelChangedListener { 10 | /** 11 | * Callback method to be invoked when current item changed 12 | * 13 | * @param wheel the wheel view whose state has changed 14 | * @param oldValue the old value of current item 15 | * @param newValue the new value of current item 16 | */ 17 | void onChanged(WheelView wheel, int oldValue, int newValue); 18 | } 19 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/wheel/OnWheelClickedListener.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.wheel; 2 | 3 | /** 4 | * Wheel clicked listener interface. 5 | *

The onItemClicked() method is called whenever a wheel item is clicked 6 | * New Wheel position is set 7 | * Wheel view is scrolled 8 | */ 9 | public interface OnWheelClickedListener { 10 | /** 11 | * Callback method to be invoked when current item clicked 12 | * 13 | * @param wheel the wheel view 14 | * @param itemIndex the index of clicked item 15 | */ 16 | void onItemClicked(WheelView wheel, int itemIndex); 17 | } 18 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/wheel/OnWheelScrollListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Yuri Kanivets 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.jzxiang.pickerview.wheel; 18 | 19 | /** 20 | * Wheel scrolled listener interface. 21 | */ 22 | public interface OnWheelScrollListener { 23 | /** 24 | * Callback method to be invoked when scrolling started. 25 | * 26 | * @param wheel the wheel view whose state has changed. 27 | */ 28 | void onScrollingStarted(WheelView wheel); 29 | 30 | /** 31 | * Callback method to be invoked when scrolling ended. 32 | * 33 | * @param wheel the wheel view whose state has changed. 34 | */ 35 | void onScrollingFinished(WheelView wheel); 36 | } 37 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/wheel/WheelRecycle.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.wheel; 2 | 3 | import android.view.View; 4 | import android.widget.LinearLayout; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | /** 10 | * Recycle stores wheel items to reuse. 11 | */ 12 | public class WheelRecycle { 13 | // Cached items 14 | private List items; 15 | 16 | // Cached empty items 17 | private List emptyItems; 18 | 19 | // Wheel view 20 | private WheelView wheel; 21 | 22 | 23 | public WheelRecycle(WheelView wheel) { 24 | this.wheel = wheel; 25 | } 26 | 27 | public int recycleItems(LinearLayout layout, int firstItem, ItemsRange range, int currentItem) { 28 | int index = firstItem; 29 | for (int i = 0; i < layout.getChildCount(); ) { 30 | if (!range.contains(index)) { 31 | recycleView(layout.getChildAt(i), index, currentItem); 32 | layout.removeViewAt(i); 33 | if (i == 0) { // first item 34 | firstItem++; 35 | } 36 | } else { 37 | i++; // go to next item 38 | } 39 | index++; 40 | } 41 | return firstItem; 42 | } 43 | 44 | /** 45 | * Gets item view 46 | * 47 | * @return the cached view 48 | */ 49 | public View getItem() { 50 | return getCachedView(items); 51 | } 52 | 53 | /** 54 | * Gets empty item view 55 | * 56 | * @return the cached empty view 57 | */ 58 | public View getEmptyItem() { 59 | return getCachedView(emptyItems); 60 | } 61 | 62 | /** 63 | * Clears all views 64 | */ 65 | public void clearAll() { 66 | if (items != null) { 67 | items.clear(); 68 | } 69 | if (emptyItems != null) { 70 | emptyItems.clear(); 71 | } 72 | } 73 | 74 | /** 75 | * Adds view to specified cache. Creates a cache list if it is null. 76 | * 77 | * @param view the view to be cached 78 | * @param cache the cache list 79 | * @return the cache list 80 | */ 81 | private List addView(View view, List cache) { 82 | if (cache == null) { 83 | cache = new LinkedList(); 84 | } 85 | 86 | cache.add(view); 87 | return cache; 88 | } 89 | 90 | /** 91 | * Adds view to cache. Determines view type (item view or empty one) by index. 92 | * 93 | * @param view the view to be cached 94 | * @param index the index of view 95 | */ 96 | private void recycleView(View view, int index, int current) { 97 | int count = wheel.getViewAdapter().getItemsCount(); 98 | 99 | 100 | if ((index < 0 || index >= count) && !wheel.isCyclic()) { 101 | // empty view 102 | emptyItems = addView(view, emptyItems); 103 | } else { 104 | while (index < 0) { 105 | index = count + index; 106 | } 107 | index %= count; 108 | items = addView(view, items); 109 | } 110 | } 111 | 112 | /** 113 | * Gets view from specified cache. 114 | * 115 | * @param cache the cache 116 | * @return the first view from cache. 117 | */ 118 | private View getCachedView(List cache) { 119 | if (cache != null && cache.size() > 0) { 120 | View view = cache.get(0); 121 | cache.remove(0); 122 | return view; 123 | } 124 | return null; 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/wheel/WheelScroller.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.wheel; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.os.Message; 6 | import android.view.GestureDetector; 7 | import android.view.GestureDetector.SimpleOnGestureListener; 8 | import android.view.MotionEvent; 9 | import android.view.animation.Interpolator; 10 | import android.widget.Scroller; 11 | 12 | public class WheelScroller { 13 | 14 | public static final int MIN_DELTA_FOR_SCROLLING = 1; 15 | private static final int SCROLLING_DURATION = 400; 16 | // Messages 17 | private final int MESSAGE_SCROLL = 0; 18 | private final int MESSAGE_JUSTIFY = 1; 19 | private ScrollingListener listener; 20 | // Context 21 | private Context context; 22 | // Scrolling 23 | private GestureDetector gestureDetector; 24 | private Scroller scroller; 25 | private int lastScrollY; 26 | private float lastTouchedY; 27 | private boolean isScrollingPerformed; 28 | // animation handler 29 | private Handler animationHandler = new Handler() { 30 | public void handleMessage(Message msg) { 31 | scroller.computeScrollOffset(); 32 | int currY = scroller.getCurrY(); 33 | int delta = lastScrollY - currY; 34 | lastScrollY = currY; 35 | if (delta != 0) { 36 | listener.onScroll(delta); 37 | } 38 | 39 | // scrolling is not finished when it comes to final Y 40 | // so, finish it manually 41 | if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) { 42 | currY = scroller.getFinalY(); 43 | scroller.forceFinished(true); 44 | } 45 | if (!scroller.isFinished()) { 46 | animationHandler.sendEmptyMessage(msg.what); 47 | } else if (msg.what == MESSAGE_SCROLL) { 48 | justify(); 49 | } else { 50 | finishScrolling(); 51 | } 52 | } 53 | }; 54 | // gesture listener 55 | private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() { 56 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 57 | // Do scrolling in onTouchEvent() since onScroll() are not call immediately 58 | // when user touch and move the wheel 59 | return true; 60 | } 61 | 62 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 63 | lastScrollY = 0; 64 | final int maxY = 0x7FFFFFFF; 65 | final int minY = -maxY; 66 | scroller.fling(0, lastScrollY, 0, (int) -velocityY, 0, 0, minY, maxY); 67 | setNextMessage(MESSAGE_SCROLL); 68 | return true; 69 | } 70 | }; 71 | 72 | 73 | public WheelScroller(Context context, ScrollingListener listener) { 74 | gestureDetector = new GestureDetector(context, gestureListener); 75 | gestureDetector.setIsLongpressEnabled(false); 76 | 77 | scroller = new Scroller(context); 78 | 79 | this.listener = listener; 80 | this.context = context; 81 | } 82 | 83 | 84 | public void setInterpolator(Interpolator interpolator) { 85 | scroller.forceFinished(true); 86 | scroller = new Scroller(context, interpolator); 87 | } 88 | 89 | public void scroll(int distance, int time) { 90 | scroller.forceFinished(true); 91 | 92 | lastScrollY = 0; 93 | 94 | scroller.startScroll(0, 0, 0, distance, time != 0 ? time : SCROLLING_DURATION); 95 | setNextMessage(MESSAGE_SCROLL); 96 | 97 | startScrolling(); 98 | } 99 | 100 | /** 101 | * Stops scrolling 102 | */ 103 | public void stopScrolling() { 104 | scroller.forceFinished(true); 105 | } 106 | 107 | /** 108 | * Handles Touch event 109 | * 110 | * @param event the motion event 111 | * @return 112 | */ 113 | public boolean onTouchEvent(MotionEvent event) { 114 | switch (event.getAction()) { 115 | case MotionEvent.ACTION_DOWN: 116 | lastTouchedY = event.getY(); 117 | scroller.forceFinished(true); 118 | clearMessages(); 119 | break; 120 | 121 | case MotionEvent.ACTION_MOVE: 122 | // perform scrolling 123 | int distanceY = (int) (event.getY() - lastTouchedY); 124 | if (distanceY != 0) { 125 | startScrolling(); 126 | listener.onScroll(distanceY); 127 | lastTouchedY = event.getY(); 128 | } 129 | break; 130 | } 131 | 132 | if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) { 133 | justify(); 134 | } 135 | 136 | return true; 137 | } 138 | 139 | /** 140 | * Set next message to queue. Clears queue before. 141 | * 142 | * @param message the message to set 143 | */ 144 | private void setNextMessage(int message) { 145 | clearMessages(); 146 | animationHandler.sendEmptyMessage(message); 147 | } 148 | 149 | /** 150 | * Clears messages from queue 151 | */ 152 | private void clearMessages() { 153 | animationHandler.removeMessages(MESSAGE_SCROLL); 154 | animationHandler.removeMessages(MESSAGE_JUSTIFY); 155 | } 156 | 157 | /** 158 | * Justifies wheel 159 | */ 160 | private void justify() { 161 | listener.onJustify(); 162 | setNextMessage(MESSAGE_JUSTIFY); 163 | } 164 | 165 | /** 166 | * Starts scrolling 167 | */ 168 | private void startScrolling() { 169 | if (!isScrollingPerformed) { 170 | isScrollingPerformed = true; 171 | listener.onStarted(); 172 | } 173 | } 174 | 175 | /** 176 | * Finishes scrolling 177 | */ 178 | void finishScrolling() { 179 | if (isScrollingPerformed) { 180 | listener.onFinished(); 181 | isScrollingPerformed = false; 182 | } 183 | } 184 | 185 | public interface ScrollingListener { 186 | /** 187 | * Scrolling callback called when scrolling is performed. 188 | * 189 | * @param distance the distance to scroll 190 | */ 191 | void onScroll(int distance); 192 | 193 | /** 194 | * Starting callback called when scrolling is started 195 | */ 196 | void onStarted(); 197 | 198 | /** 199 | * Finishing callback called after justifying 200 | */ 201 | void onFinished(); 202 | 203 | /** 204 | * Justifying callback called to justify a view when scrolling is ended 205 | */ 206 | void onJustify(); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/java/com/jzxiang/pickerview/wheel/WheelView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Android Wheel Control. 3 | * https://code.google.com/p/android-wheel/ 4 | * 5 | * Copyright 2011 Yuri Kanivets 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package com.jzxiang.pickerview.wheel; 21 | 22 | import android.content.Context; 23 | import android.database.DataSetObserver; 24 | import android.graphics.Canvas; 25 | import android.graphics.Paint; 26 | import android.util.AttributeSet; 27 | import android.view.MotionEvent; 28 | import android.view.View; 29 | import android.view.ViewGroup.LayoutParams; 30 | import android.view.animation.Interpolator; 31 | import android.widget.LinearLayout; 32 | import android.widget.TextView; 33 | 34 | import com.jzxiang.pickerview.R; 35 | import com.jzxiang.pickerview.adapters.WheelViewAdapter; 36 | import com.jzxiang.pickerview.config.DefaultConfig; 37 | import com.jzxiang.pickerview.config.PickerConfig; 38 | 39 | import java.util.LinkedList; 40 | import java.util.List; 41 | 42 | 43 | /** 44 | * Numeric wheel view. 45 | * 46 | * @author Yuri Kanivets 47 | */ 48 | public class WheelView extends View { 49 | 50 | /** 51 | * Top and bottom items offset (to hide that) 52 | */ 53 | private static final int ITEM_OFFSET_PERCENT = 10; 54 | 55 | /** 56 | * Left and right padding value 57 | */ 58 | private static final int PADDING = 10; 59 | 60 | /** 61 | * Default count of visible items 62 | */ 63 | private static final int DEF_VISIBLE_ITEMS = 5; 64 | // Cyclic 65 | boolean isCyclic = false; 66 | int defaultColor, selectorColor; 67 | // Wheel Values 68 | private int currentItem = 0; 69 | // Count of visible items 70 | private int visibleItems = DEF_VISIBLE_ITEMS; 71 | // Item height 72 | private int itemHeight = 0; 73 | // Scrolling 74 | private WheelScroller scroller; 75 | private boolean isScrollingPerformed; 76 | private int scrollingOffset; 77 | // Items layout 78 | private LinearLayout itemsLayout; 79 | // The number of first item in layout 80 | private int firstItem; 81 | // View adapter 82 | private WheelViewAdapter viewAdapter; 83 | 84 | // Recycle 85 | private WheelRecycle recycle = new WheelRecycle(this); 86 | private Paint mPaintLineCenter, mPaintLineRight, mPaintRectCenter; 87 | private int mLineRightMar; 88 | // Listeners 89 | private List changingListeners = new LinkedList(); 90 | private List scrollingListeners = new LinkedList(); 91 | // Scrolling listener 92 | WheelScroller.ScrollingListener scrollingListener = new WheelScroller.ScrollingListener() { 93 | public void onStarted() { 94 | isScrollingPerformed = true; 95 | notifyScrollingListenersAboutStart(); 96 | } 97 | 98 | public void onScroll(int distance) { 99 | doScroll(distance); 100 | 101 | int height = getHeight(); 102 | if (scrollingOffset > height) { 103 | scrollingOffset = height; 104 | scroller.stopScrolling(); 105 | } else if (scrollingOffset < -height) { 106 | scrollingOffset = -height; 107 | scroller.stopScrolling(); 108 | } 109 | } 110 | 111 | public void onFinished() { 112 | if (isScrollingPerformed) { 113 | notifyScrollingListenersAboutEnd(); 114 | isScrollingPerformed = false; 115 | } 116 | 117 | scrollingOffset = 0; 118 | invalidate(); 119 | } 120 | 121 | public void onJustify() { 122 | if (Math.abs(scrollingOffset) > WheelScroller.MIN_DELTA_FOR_SCROLLING) { 123 | scroller.scroll(scrollingOffset, 0); 124 | } 125 | } 126 | }; 127 | private List clickingListeners = new LinkedList(); 128 | // Adapter listener 129 | private DataSetObserver dataObserver = new DataSetObserver() { 130 | @Override 131 | public void onChanged() { 132 | invalidateWheel(false); 133 | } 134 | 135 | @Override 136 | public void onInvalidated() { 137 | invalidateWheel(true); 138 | } 139 | }; 140 | 141 | /** 142 | * Constructor 143 | */ 144 | public WheelView(Context context, AttributeSet attrs, int defStyle) { 145 | super(context, attrs, defStyle); 146 | initData(context); 147 | } 148 | 149 | /** 150 | * Constructor 151 | */ 152 | public WheelView(Context context, AttributeSet attrs) { 153 | super(context, attrs); 154 | initData(context); 155 | } 156 | 157 | /** 158 | * Constructor 159 | */ 160 | public WheelView(Context context) { 161 | super(context); 162 | initData(context); 163 | } 164 | 165 | /** 166 | * Initializes class data 167 | * 168 | * @param context the context 169 | */ 170 | private void initData(Context context) { 171 | scroller = new WheelScroller(getContext(), scrollingListener); 172 | 173 | mPaintLineCenter = new Paint(); 174 | mPaintLineCenter.setColor(DefaultConfig.COLOR); 175 | mPaintLineCenter.setAntiAlias(true); 176 | mPaintLineCenter.setStrokeWidth(1); 177 | mPaintLineCenter.setStyle(Paint.Style.FILL); 178 | 179 | mPaintLineRight = new Paint(); 180 | mPaintLineRight.setColor(0xffe8e8e8); 181 | mPaintLineRight.setAntiAlias(true); 182 | // mPaintLineRight.setStrokeWidth(context.getResources().getDimensionPixelSize(R.dimen.picker_line_width)); 183 | mPaintLineRight.setStrokeWidth(1); 184 | mPaintLineRight.setStyle(Paint.Style.FILL); 185 | 186 | mPaintRectCenter = new Paint(); 187 | mPaintRectCenter.setColor(DefaultConfig.COLOR); 188 | mPaintRectCenter.setAlpha((int) (0.1 * 255)); 189 | mPaintRectCenter.setAntiAlias(true); 190 | mPaintRectCenter.setStyle(Paint.Style.FILL); 191 | 192 | mLineRightMar = context.getResources().getDimensionPixelSize(R.dimen.picker_line_mar); 193 | 194 | 195 | defaultColor = DefaultConfig.TV_NORMAL_COLOR; 196 | selectorColor = DefaultConfig.TV_SELECTOR_COLOR; 197 | 198 | } 199 | 200 | public void setConfig(PickerConfig config) { 201 | mPaintLineCenter.setColor(config.mThemeColor); 202 | 203 | mPaintRectCenter.setColor(config.mThemeColor); 204 | mPaintRectCenter.setAlpha((int) (0.1 * 255)); 205 | 206 | defaultColor = config.mWheelTVNormalColor; 207 | selectorColor = config.mWheelTVSelectorColor; 208 | } 209 | 210 | 211 | /** 212 | * Set the the specified scrolling interpolator 213 | * 214 | * @param interpolator the interpolator 215 | */ 216 | public void setInterpolator(Interpolator interpolator) { 217 | scroller.setInterpolator(interpolator); 218 | } 219 | 220 | /** 221 | * Gets count of visible items 222 | * 223 | * @return the count of visible items 224 | */ 225 | public int getVisibleItems() { 226 | return visibleItems; 227 | } 228 | 229 | /** 230 | * Sets the desired count of visible items. 231 | * Actual amount of visible items depends on wheel layout parameters. 232 | * To apply changes and rebuild view call measure(). 233 | * 234 | * @param count the desired count for visible items 235 | */ 236 | public void setVisibleItems(int count) { 237 | visibleItems = count; 238 | } 239 | 240 | /** 241 | * Gets view adapter 242 | * 243 | * @return the view adapter 244 | */ 245 | public WheelViewAdapter getViewAdapter() { 246 | return viewAdapter; 247 | } 248 | 249 | /** 250 | * Sets view adapter. Usually new adapters contain different views, so 251 | * it needs to rebuild view by calling measure(). 252 | * 253 | * @param viewAdapter the view adapter 254 | */ 255 | public void setViewAdapter(WheelViewAdapter viewAdapter) { 256 | if (this.viewAdapter != null) { 257 | this.viewAdapter.unregisterDataSetObserver(dataObserver); 258 | } 259 | this.viewAdapter = viewAdapter; 260 | if (this.viewAdapter != null) { 261 | this.viewAdapter.registerDataSetObserver(dataObserver); 262 | } 263 | 264 | setConfig(viewAdapter.getConfig()); 265 | invalidateWheel(true); 266 | } 267 | 268 | /** 269 | * Adds wheel changing listener 270 | * 271 | * @param listener the listener 272 | */ 273 | public void addChangingListener(OnWheelChangedListener listener) { 274 | changingListeners.add(listener); 275 | } 276 | 277 | /** 278 | * Removes wheel changing listener 279 | * 280 | * @param listener the listener 281 | */ 282 | public void removeChangingListener(OnWheelChangedListener listener) { 283 | changingListeners.remove(listener); 284 | } 285 | 286 | /** 287 | * Notifies changing listeners 288 | * 289 | * @param oldValue the old wheel value 290 | * @param newValue the new wheel value 291 | */ 292 | protected void notifyChangingListeners(int oldValue, int newValue) { 293 | for (OnWheelChangedListener listener : changingListeners) { 294 | listener.onChanged(this, oldValue, newValue); 295 | } 296 | 297 | if (oldValue < 0 || newValue < 0 || itemsLayout == null) 298 | return; 299 | 300 | View oldView = itemsLayout.getChildAt(oldValue - firstItem); 301 | View newView = itemsLayout.getChildAt(newValue - firstItem); 302 | 303 | refreshTextStatus(oldView, oldValue); 304 | refreshTextStatus(newView, newValue); 305 | 306 | } 307 | 308 | /** 309 | * Adds wheel scrolling listener 310 | * 311 | * @param listener the listener 312 | */ 313 | public void addScrollingListener(OnWheelScrollListener listener) { 314 | scrollingListeners.add(listener); 315 | } 316 | 317 | /** 318 | * Removes wheel scrolling listener 319 | * 320 | * @param listener the listener 321 | */ 322 | public void removeScrollingListener(OnWheelScrollListener listener) { 323 | scrollingListeners.remove(listener); 324 | } 325 | 326 | /** 327 | * Notifies listeners about starting scrolling 328 | */ 329 | protected void notifyScrollingListenersAboutStart() { 330 | for (OnWheelScrollListener listener : scrollingListeners) { 331 | listener.onScrollingStarted(this); 332 | } 333 | } 334 | 335 | /** 336 | * Notifies listeners about ending scrolling 337 | */ 338 | protected void notifyScrollingListenersAboutEnd() { 339 | for (OnWheelScrollListener listener : scrollingListeners) { 340 | listener.onScrollingFinished(this); 341 | } 342 | 343 | 344 | } 345 | 346 | /** 347 | * Adds wheel clicking listener 348 | * 349 | * @param listener the listener 350 | */ 351 | public void addClickingListener(OnWheelClickedListener listener) { 352 | clickingListeners.add(listener); 353 | } 354 | 355 | /** 356 | * Removes wheel clicking listener 357 | * 358 | * @param listener the listener 359 | */ 360 | public void removeClickingListener(OnWheelClickedListener listener) { 361 | clickingListeners.remove(listener); 362 | } 363 | 364 | /** 365 | * Notifies listeners about clicking 366 | */ 367 | protected void notifyClickListenersAboutClick(int item) { 368 | for (OnWheelClickedListener listener : clickingListeners) { 369 | listener.onItemClicked(this, item); 370 | } 371 | } 372 | 373 | /** 374 | * Gets current value 375 | * 376 | * @return the current value 377 | */ 378 | public int getCurrentItem() { 379 | return currentItem; 380 | } 381 | 382 | /** 383 | * Sets the current item w/o animation. Does nothing when index is wrong. 384 | * 385 | * @param index the item index 386 | */ 387 | public void setCurrentItem(int index) { 388 | setCurrentItem(index, false); 389 | } 390 | 391 | /** 392 | * Sets the current item. Does nothing when index is wrong. 393 | * 394 | * @param index the item index 395 | * @param animated the animation flag 396 | */ 397 | public void setCurrentItem(int index, boolean animated) { 398 | if (viewAdapter == null || viewAdapter.getItemsCount() == 0) { 399 | return; // throw? 400 | } 401 | 402 | int itemCount = viewAdapter.getItemsCount(); 403 | if (index < 0 || index >= itemCount) { 404 | if (isCyclic) { 405 | while (index < 0) { 406 | index += itemCount; 407 | } 408 | index %= itemCount; 409 | } else { 410 | return; // throw? 411 | } 412 | } 413 | 414 | 415 | if (index != currentItem) { 416 | if (animated) { 417 | int itemsToScroll = index - currentItem; 418 | if (isCyclic) { 419 | int scroll = itemCount + Math.min(index, currentItem) - Math.max(index, currentItem); 420 | if (scroll < Math.abs(itemsToScroll)) { 421 | itemsToScroll = itemsToScroll < 0 ? scroll : -scroll; 422 | } 423 | } 424 | scroll(itemsToScroll, 0); 425 | } else { 426 | scrollingOffset = 0; 427 | 428 | int old = currentItem; 429 | currentItem = index; 430 | 431 | notifyChangingListeners(old, currentItem); 432 | 433 | invalidate(); 434 | } 435 | 436 | 437 | } 438 | } 439 | 440 | /** 441 | * Tests if wheel is cyclic. That means before the 1st item there is shown the last one 442 | * 443 | * @return true if wheel is cyclic 444 | */ 445 | public boolean isCyclic() { 446 | return isCyclic; 447 | } 448 | 449 | /** 450 | * Set wheel cyclic flag 451 | * 452 | * @param isCyclic the flag to set 453 | */ 454 | public void setCyclic(boolean isCyclic) { 455 | this.isCyclic = isCyclic; 456 | invalidateWheel(false); 457 | } 458 | 459 | /** 460 | * Invalidates wheel 461 | * 462 | * @param clearCaches if true then cached views will be clear 463 | */ 464 | public void invalidateWheel(boolean clearCaches) { 465 | if (clearCaches) { 466 | recycle.clearAll(); 467 | if (itemsLayout != null) { 468 | itemsLayout.removeAllViews(); 469 | } 470 | scrollingOffset = 0; 471 | } else if (itemsLayout != null) { 472 | // cache all items 473 | recycle.recycleItems(itemsLayout, firstItem, new ItemsRange(), currentItem); 474 | } 475 | 476 | invalidate(); 477 | } 478 | 479 | /** 480 | * Initializes resources 481 | */ 482 | private void initResourcesIfNecessary() { 483 | setBackgroundResource(android.R.color.white); 484 | } 485 | 486 | /** 487 | * Calculates desired height for layout 488 | * 489 | * @param layout the source layout 490 | * @return the desired layout height 491 | */ 492 | private int getDesiredHeight(LinearLayout layout) { 493 | if (layout != null && layout.getChildAt(0) != null) { 494 | itemHeight = layout.getChildAt(0).getMeasuredHeight(); 495 | } 496 | 497 | int desired = itemHeight * visibleItems - itemHeight * ITEM_OFFSET_PERCENT / 50; 498 | 499 | return Math.max(desired, getSuggestedMinimumHeight()); 500 | } 501 | 502 | /** 503 | * Returns height of wheel item 504 | * 505 | * @return the item height 506 | */ 507 | private int getItemHeight() { 508 | if (itemHeight != 0) { 509 | return itemHeight; 510 | } 511 | 512 | if (itemsLayout != null && itemsLayout.getChildAt(0) != null) { 513 | itemHeight = itemsLayout.getChildAt(0).getHeight(); 514 | return itemHeight; 515 | } 516 | 517 | return getHeight() / visibleItems; 518 | } 519 | 520 | /** 521 | * Calculates control width and creates text layouts 522 | * 523 | * @param widthSize the input layout width 524 | * @param mode the layout mode 525 | * @return the calculated control width 526 | */ 527 | private int calculateLayoutWidth(int widthSize, int mode) { 528 | initResourcesIfNecessary(); 529 | 530 | // TODO: make it static 531 | itemsLayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 532 | itemsLayout.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.UNSPECIFIED), 533 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 534 | int width = itemsLayout.getMeasuredWidth(); 535 | 536 | if (mode == MeasureSpec.EXACTLY) { 537 | width = widthSize; 538 | } else { 539 | width += 2 * PADDING; 540 | 541 | // Check against our minimum width 542 | width = Math.max(width, getSuggestedMinimumWidth()); 543 | 544 | if (mode == MeasureSpec.AT_MOST && widthSize < width) { 545 | width = widthSize; 546 | } 547 | } 548 | 549 | itemsLayout.measure(MeasureSpec.makeMeasureSpec(width - 2 * PADDING, MeasureSpec.EXACTLY), 550 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 551 | 552 | return width; 553 | } 554 | 555 | @Override 556 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 557 | int widthMode = MeasureSpec.getMode(widthMeasureSpec); 558 | int heightMode = MeasureSpec.getMode(heightMeasureSpec); 559 | int widthSize = MeasureSpec.getSize(widthMeasureSpec); 560 | int heightSize = MeasureSpec.getSize(heightMeasureSpec); 561 | 562 | buildViewForMeasuring(); 563 | 564 | int width = calculateLayoutWidth(widthSize, widthMode); 565 | 566 | int height; 567 | if (heightMode == MeasureSpec.EXACTLY) { 568 | height = heightSize; 569 | } else { 570 | height = getDesiredHeight(itemsLayout); 571 | 572 | if (heightMode == MeasureSpec.AT_MOST) { 573 | height = Math.min(height, heightSize); 574 | } 575 | } 576 | 577 | setMeasuredDimension(width, height); 578 | } 579 | 580 | @Override 581 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 582 | layout(r - l, b - t); 583 | } 584 | 585 | /** 586 | * Sets layouts width and height 587 | * 588 | * @param width the layout width 589 | * @param height the layout height 590 | */ 591 | private void layout(int width, int height) { 592 | int itemsWidth = width - 2 * PADDING; 593 | 594 | itemsLayout.layout(0, 0, itemsWidth, height); 595 | } 596 | 597 | @Override 598 | protected void onDraw(Canvas canvas) { 599 | super.onDraw(canvas); 600 | if (viewAdapter != null && viewAdapter.getItemsCount() > 0) { 601 | updateView(); 602 | drawItems(canvas); 603 | drawCenterRect(canvas); 604 | } 605 | } 606 | 607 | 608 | /** 609 | * Draws items 610 | * 611 | * @param canvas the canvas for drawing 612 | */ 613 | private void drawItems(Canvas canvas) { 614 | canvas.save(); 615 | 616 | int top = (currentItem - firstItem) * getItemHeight() + (getItemHeight() - getHeight()) / 2; 617 | canvas.translate(PADDING, -top + scrollingOffset); 618 | 619 | itemsLayout.draw(canvas); 620 | 621 | canvas.restore(); 622 | } 623 | 624 | /** 625 | * Draws rect for current value 626 | * 627 | * @param canvas the canvas for drawing 628 | */ 629 | private void drawCenterRect(Canvas canvas) { 630 | int center = getHeight() / 2; 631 | int offset = (int) (getItemHeight() / 2 * 1.2); 632 | // centerDrawable.setBounds(0, center - offset, getWidth(), center + offset); 633 | // centerDrawable.draw(canvas); 634 | canvas.drawRect(0, center - offset, getWidth(), center + offset, mPaintRectCenter); 635 | 636 | canvas.drawLine(0, center - offset, getWidth(), center - offset, mPaintLineCenter); 637 | canvas.drawLine(0, center + offset, getWidth(), center + offset, mPaintLineCenter); 638 | 639 | int x = getWidth() - 1; 640 | canvas.drawLine(x, mLineRightMar, x, getHeight() - mLineRightMar, mPaintLineRight); 641 | } 642 | 643 | 644 | @Override 645 | public boolean onTouchEvent(MotionEvent event) { 646 | if (!isEnabled() || getViewAdapter() == null) { 647 | return true; 648 | } 649 | 650 | switch (event.getAction()) { 651 | case MotionEvent.ACTION_MOVE: 652 | if (getParent() != null) { 653 | getParent().requestDisallowInterceptTouchEvent(true); 654 | } 655 | break; 656 | 657 | case MotionEvent.ACTION_UP: 658 | if (!isScrollingPerformed) { 659 | int distance = (int) event.getY() - getHeight() / 2; 660 | if (distance > 0) { 661 | distance += getItemHeight() / 2; 662 | } else { 663 | distance -= getItemHeight() / 2; 664 | } 665 | int items = distance / getItemHeight(); 666 | if (items != 0 && isValidItemIndex(currentItem + items)) { 667 | notifyClickListenersAboutClick(currentItem + items); 668 | } 669 | } 670 | break; 671 | } 672 | 673 | return scroller.onTouchEvent(event); 674 | } 675 | 676 | /** 677 | * Scrolls the wheel 678 | * 679 | * @param delta the scrolling value 680 | */ 681 | private void doScroll(int delta) { 682 | scrollingOffset += delta; 683 | 684 | int itemHeight = getItemHeight(); 685 | int count = scrollingOffset / itemHeight; 686 | 687 | int pos = currentItem - count; 688 | int itemCount = viewAdapter.getItemsCount(); 689 | 690 | int fixPos = scrollingOffset % itemHeight; 691 | if (Math.abs(fixPos) <= itemHeight / 2) { 692 | fixPos = 0; 693 | } 694 | if (isCyclic && itemCount > 0) { 695 | if (fixPos > 0) { 696 | pos--; 697 | count++; 698 | } else if (fixPos < 0) { 699 | pos++; 700 | count--; 701 | } 702 | // fix position by rotating 703 | while (pos < 0) { 704 | pos += itemCount; 705 | } 706 | pos %= itemCount; 707 | } else { 708 | // 709 | if (pos < 0) { 710 | count = currentItem; 711 | pos = 0; 712 | } else if (pos >= itemCount) { 713 | count = currentItem - itemCount + 1; 714 | pos = itemCount - 1; 715 | } else if (pos > 0 && fixPos > 0) { 716 | pos--; 717 | count++; 718 | } else if (pos < itemCount - 1 && fixPos < 0) { 719 | pos++; 720 | count--; 721 | } 722 | } 723 | 724 | int offset = scrollingOffset; 725 | if (pos != currentItem) { 726 | setCurrentItem(pos, false); 727 | } else { 728 | invalidate(); 729 | } 730 | 731 | // update offset 732 | scrollingOffset = offset - count * itemHeight; 733 | if (scrollingOffset > getHeight()) { 734 | scrollingOffset = scrollingOffset % getHeight() + getHeight(); 735 | } 736 | } 737 | 738 | /** 739 | * Scroll the wheel 740 | * 741 | * @param itemsToScroll items to scroll 742 | * @param time scrolling duration 743 | */ 744 | public void scroll(int itemsToScroll, int time) { 745 | int distance = itemsToScroll * getItemHeight() - scrollingOffset; 746 | scroller.scroll(distance, time); 747 | } 748 | 749 | /** 750 | * Calculates range for wheel items 751 | * 752 | * @return the items range 753 | */ 754 | private ItemsRange getItemsRange() { 755 | if (getItemHeight() == 0) { 756 | return null; 757 | } 758 | 759 | int first = currentItem; 760 | int count = 1; 761 | 762 | while (count * getItemHeight() < getHeight()) { 763 | first--; 764 | count += 2; // top + bottom items 765 | } 766 | 767 | if (scrollingOffset != 0) { 768 | if (scrollingOffset > 0) { 769 | first--; 770 | } 771 | count++; 772 | 773 | // process empty items above the first or below the second 774 | int emptyItems = scrollingOffset / getItemHeight(); 775 | first -= emptyItems; 776 | count += Math.asin(emptyItems); 777 | } 778 | return new ItemsRange(first, count); 779 | } 780 | 781 | /** 782 | * Rebuilds wheel items if necessary. Caches all unused items. 783 | * 784 | * @return true if items are rebuilt 785 | */ 786 | private boolean rebuildItems() { 787 | boolean updated = false; 788 | ItemsRange range = getItemsRange(); 789 | if (itemsLayout != null) { 790 | int first = recycle.recycleItems(itemsLayout, firstItem, range, currentItem); 791 | updated = firstItem != first; 792 | firstItem = first; 793 | } else { 794 | createItemsLayout(); 795 | updated = true; 796 | } 797 | 798 | if (!updated) { 799 | updated = firstItem != range.getFirst() || itemsLayout.getChildCount() != range.getCount(); 800 | } 801 | 802 | if (firstItem > range.getFirst() && firstItem <= range.getLast()) { 803 | for (int i = firstItem - 1; i >= range.getFirst(); i--) { 804 | if (!addViewItem(i, true)) { 805 | break; 806 | } 807 | firstItem = i; 808 | } 809 | } else { 810 | firstItem = range.getFirst(); 811 | } 812 | 813 | int first = firstItem; 814 | for (int i = itemsLayout.getChildCount(); i < range.getCount(); i++) { 815 | if (!addViewItem(firstItem + i, false) && itemsLayout.getChildCount() == 0) { 816 | first++; 817 | } 818 | } 819 | firstItem = first; 820 | 821 | return updated; 822 | } 823 | 824 | /** 825 | * Updates view. Rebuilds items and label if necessary, recalculate items sizes. 826 | */ 827 | private void updateView() { 828 | if (rebuildItems()) { 829 | calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY); 830 | layout(getWidth(), getHeight()); 831 | } 832 | } 833 | 834 | /** 835 | * Creates item layouts if necessary 836 | */ 837 | private void createItemsLayout() { 838 | if (itemsLayout == null) { 839 | itemsLayout = new LinearLayout(getContext()); 840 | itemsLayout.setOrientation(LinearLayout.VERTICAL); 841 | } 842 | } 843 | 844 | /** 845 | * Builds view for measuring 846 | */ 847 | private void buildViewForMeasuring() { 848 | // clear all items 849 | if (itemsLayout != null) { 850 | recycle.recycleItems(itemsLayout, firstItem, new ItemsRange(), currentItem); 851 | } else { 852 | createItemsLayout(); 853 | } 854 | 855 | // add views 856 | int addItems = visibleItems / 2; 857 | for (int i = currentItem + addItems; i >= currentItem - addItems; i--) { 858 | if (addViewItem(i, true)) { 859 | firstItem = i; 860 | } 861 | } 862 | } 863 | 864 | /** 865 | * Adds view for item to items layout 866 | * 867 | * @param index the item index 868 | * @param first the flag indicates if view should be first 869 | * @return true if corresponding item exists and is added 870 | */ 871 | private boolean addViewItem(int index, boolean first) { 872 | View view = getItemView(index); 873 | refreshTextStatus(view, index); 874 | if (view != null) { 875 | if (first) { 876 | itemsLayout.addView(view, 0); 877 | } else { 878 | itemsLayout.addView(view); 879 | } 880 | 881 | return true; 882 | } 883 | 884 | return false; 885 | } 886 | 887 | void refreshTextStatus(View view, int index) { 888 | if (!(view instanceof TextView)) 889 | return; 890 | TextView textView = (TextView) view; 891 | if (index == currentItem) { 892 | textView.setTextColor(selectorColor); 893 | } else { 894 | textView.setTextColor(defaultColor); 895 | } 896 | } 897 | 898 | /** 899 | * Checks whether intem index is valid 900 | * 901 | * @param index the item index 902 | * @return true if item index is not out of bounds or the wheel is cyclic 903 | */ 904 | private boolean isValidItemIndex(int index) { 905 | return viewAdapter != null && viewAdapter.getItemsCount() > 0 && 906 | (isCyclic || index >= 0 && index < viewAdapter.getItemsCount()); 907 | } 908 | 909 | /** 910 | * Returns view for specified item 911 | * 912 | * @param index the item index 913 | * @return item view or empty view if index is out of bounds 914 | */ 915 | private View getItemView(int index) { 916 | if (viewAdapter == null || viewAdapter.getItemsCount() == 0) { 917 | return null; 918 | } 919 | int count = viewAdapter.getItemsCount(); 920 | if (!isValidItemIndex(index)) { 921 | return viewAdapter.getEmptyItem(recycle.getEmptyItem(), itemsLayout); 922 | } else { 923 | while (index < 0) { 924 | index = count + index; 925 | } 926 | } 927 | 928 | index %= count; 929 | 930 | View view = viewAdapter.getItem(index, recycle.getItem(), itemsLayout); 931 | 932 | 933 | return view; 934 | } 935 | 936 | /** 937 | * Stops scrolling 938 | */ 939 | public void stopScrolling() { 940 | scroller.stopScrolling(); 941 | } 942 | } 943 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/anim/slide_in_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 16 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/anim/slide_out_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 14 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/drawable/timepicker_divider_line.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/drawable/timepicker_sel_text_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/drawable/wheel_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 33 | 34 | 35 | 40 | 41 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/drawable/wheel_val.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/layout/timepicker_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 12 | 13 | 16 | 17 | 25 | 26 | 31 | 32 | 33 | 38 | 39 | 40 | 45 | 46 | 47 | 52 | 53 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/layout/timepicker_line.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/layout/timepicker_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 21 | 22 | 31 | 32 | 43 | 44 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #999999 4 | #e60012 5 | #80000000 6 | #e8e8e8 7 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12sp 4 | 40dp 5 | 190dp 6 | 5dp 7 | 1dp 8 | 15dp 9 | 230dp 10 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 确定 3 | 取消 4 | 选择时间 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /TimePickerDialog/src/main/res/values/style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:2.0.0' 8 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' 9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' 10 | } 11 | } 12 | 13 | 14 | allprojects { 15 | repositories { 16 | jcenter() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /change_log.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | # 1.0.0 3 | * Add the YEAR type; 4 | 5 | ### 0.9.9 6 | * Add the function to set the max timeMillseconds. 7 | ``` java 8 | setMaxMillseconds 9 | ``` 10 | 11 | 12 | ### 0.9.3 13 | * Add some functions to set the time unit. 14 | ``` java 15 | mDialogAll = new TimePickerDialog.Builder() 16 | .setCallBack(this) 17 | .setTitleStringId("TimePicker") 18 | .setYearText("Year") 19 | .setMonthText("Month") 20 | .setDayText("Day") 21 | .setHourText("Hour") 22 | .setMinuteText("Minute") 23 | ``` 24 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JZXiang/TimePickerDialog/66137c41010235db583565ca1e9d5e838b044fdb/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon May 16 17:24:28 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /preview/timepickerdialog_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JZXiang/TimePickerDialog/66137c41010235db583565ca1e9d5e838b044fdb/preview/timepickerdialog_demo.gif -------------------------------------------------------------------------------- /sample-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JZXiang/TimePickerDialog/66137c41010235db583565ca1e9d5e838b044fdb/sample-debug.apk -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.jzxiang.pickerview.sample" 9 | minSdkVersion 8 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 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(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:23.2.1' 25 | compile 'com.jzxiang.pickerview:TimePickerDialog:1.0.0' 26 | // compile project(':TimePickerDialog') 27 | } 28 | -------------------------------------------------------------------------------- /sample/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/jzxiang/Library/Android/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 | -------------------------------------------------------------------------------- /sample/src/androidTest/java/com/wheel/pickerview/sample/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.wheel.pickerview.sample; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sample/src/main/java/com/jzxiang/pickerview/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.jzxiang.pickerview.sample; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import com.jzxiang.pickerview.TimePickerDialog; 9 | import com.jzxiang.pickerview.data.Type; 10 | import com.jzxiang.pickerview.listener.OnDateSetListener; 11 | 12 | import java.text.SimpleDateFormat; 13 | import java.util.Date; 14 | 15 | public class MainActivity extends AppCompatActivity implements View.OnClickListener, OnDateSetListener { 16 | TimePickerDialog mDialogAll; 17 | TimePickerDialog mDialogYearMonth; 18 | TimePickerDialog mDialogYearMonthDay; 19 | TimePickerDialog mDialogMonthDayHourMinute; 20 | TimePickerDialog mDialogHourMinute; 21 | TextView mTvTime; 22 | 23 | SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | initView(); 30 | long tenYears = 10L * 365 * 1000 * 60 * 60 * 24L; 31 | mDialogAll = new TimePickerDialog.Builder() 32 | .setCallBack(this) 33 | .setCancelStringId("Cancel") 34 | .setSureStringId("Sure") 35 | .setTitleStringId("TimePicker") 36 | .setYearText("Year") 37 | .setMonthText("Month") 38 | .setDayText("Day") 39 | .setHourText("Hour") 40 | .setMinuteText("Minute") 41 | .setCyclic(false) 42 | .setMinMillseconds(System.currentTimeMillis()) 43 | .setMaxMillseconds(System.currentTimeMillis() + tenYears) 44 | .setCurrentMillseconds(System.currentTimeMillis()) 45 | .setThemeColor(getResources().getColor(R.color.timepicker_dialog_bg)) 46 | .setType(Type.ALL) 47 | .setWheelItemTextNormalColor(getResources().getColor(R.color.timetimepicker_default_text_color)) 48 | .setWheelItemTextSelectorColor(getResources().getColor(R.color.timepicker_toolbar_bg)) 49 | .setWheelItemTextSize(12) 50 | .build(); 51 | 52 | // mDialogAll = new TimePickerDialog.Builder() 53 | // .setMinMillseconds(System.currentTimeMillis()) 54 | // .setThemeColor(R.color.colorPrimary) 55 | // .setWheelItemTextSize(16) 56 | // .setCallBack(this) 57 | // .build(); 58 | mDialogYearMonth = new TimePickerDialog.Builder() 59 | .setType(Type.YEAR_MONTH) 60 | .setThemeColor(getResources().getColor(R.color.colorPrimary)) 61 | .setCallBack(this) 62 | .build(); 63 | mDialogYearMonthDay = new TimePickerDialog.Builder() 64 | .setType(Type.YEAR_MONTH_DAY) 65 | .setCallBack(this) 66 | .build(); 67 | mDialogMonthDayHourMinute = new TimePickerDialog.Builder() 68 | .setType(Type.MONTH_DAY_HOUR_MIN) 69 | .setCallBack(this) 70 | .build(); 71 | mDialogHourMinute = new TimePickerDialog.Builder() 72 | .setType(Type.HOURS_MINS) 73 | .setCallBack(this) 74 | .build(); 75 | } 76 | 77 | void initView() { 78 | findViewById(R.id.btn_all).setOnClickListener(this); 79 | findViewById(R.id.btn_year_month_day).setOnClickListener(this); 80 | findViewById(R.id.btn_year_month).setOnClickListener(this); 81 | findViewById(R.id.btn_month_day_hour_minute).setOnClickListener(this); 82 | findViewById(R.id.btn_hour_minute).setOnClickListener(this); 83 | 84 | mTvTime = (TextView) findViewById(R.id.tv_time); 85 | } 86 | 87 | @Override 88 | public void onClick(View v) { 89 | switch (v.getId()) { 90 | case R.id.btn_all: 91 | mDialogAll.show(getSupportFragmentManager(), "all"); 92 | break; 93 | case R.id.btn_year_month: 94 | mDialogYearMonth.show(getSupportFragmentManager(), "year_month"); 95 | break; 96 | case R.id.btn_year_month_day: 97 | mDialogYearMonthDay.show(getSupportFragmentManager(), "year_month_day"); 98 | break; 99 | case R.id.btn_month_day_hour_minute: 100 | mDialogMonthDayHourMinute.show(getSupportFragmentManager(), "month_day_hour_minute"); 101 | break; 102 | case R.id.btn_hour_minute: 103 | mDialogHourMinute.show(getSupportFragmentManager(), "hour_minute"); 104 | break; 105 | } 106 | } 107 | 108 | 109 | @Override 110 | public void onDateSet(TimePickerDialog timePickerDialog, long millseconds) { 111 | String text = getDateToString(millseconds); 112 | mTvTime.setText(text); 113 | } 114 | 115 | public String getDateToString(long time) { 116 | Date d = new Date(time); 117 | return sf.format(d); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 |