├── .gitignore ├── .idea ├── gradle.xml └── runConfigurations.xml ├── README.md ├── SpinnerDatePickerExample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tsongkha │ │ └── spinnerdatepickerexample │ │ ├── MainActivityTest.java │ │ └── NumberPickers.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── tsongkha │ │ └── spinnerdatepickerexample │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── SpinnerDatePickerLib ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── tsongkha │ │ └── spinnerdatepicker │ │ ├── CommonDateUtils.java │ │ ├── DatePicker.java │ │ ├── DatePickerDialog.java │ │ ├── DateUtils.java │ │ ├── ICU.java │ │ ├── NumberPickers.java │ │ ├── OnDateChangedListener.java │ │ ├── SpinnerDatePickerDialogBuilder.java │ │ └── TwoDigitFormatter.java │ └── res │ ├── layout │ ├── date_picker.xml │ ├── date_picker_container.xml │ ├── date_picker_dialog.xml │ ├── date_picker_dialog_container.xml │ ├── number_picker_day_month.xml │ └── number_picker_year.xml │ └── values │ └── strings.xml ├── build.gradle ├── github ├── material_design_datepicker_example.png └── spinner_datepicker_example.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | /SpinnerDatePickerExample/google-services.json 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | gradle.properties -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![API](https://img.shields.io/badge/API-16%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=18) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-SpinnerDatePicker-red.svg?style=plastic)](https://android-arsenal.com/details/1/6319) [![Release](https://jitpack.io/v/drawers/SpinnerDatePicker.svg)](https://jitpack.io/#drawers/SpinnerDatePicker) 2 | 3 | ~~Spinner DatePicker~~ 4 | ----- 5 | 6 | # Deprecation 7 | 8 | This repo is no longer being maintained due to time constraints. #sorrynotsorry 9 | 10 | If you wish to transfer ownership then please contact the author via issues. 11 | 12 | ## Summary 13 | 14 | The old "spinner" style DatePicker for newer devices. 15 | 16 | ![ScreenShot](https://i.imgur.com/TMiivVq.png) 17 | 18 | ## Motivation 19 | 20 | The default Material Design DatePicker has poor usability for choosing a date of birth. It seems it is hard for users to find the "year" button and they will often simply swipe left or right through the months in order to find their date of birth. 21 | 22 | ![MaterialDesign](https://i.imgur.com/8lmZhbd.png?1) 23 | 24 | The previous Holo DatePicker with sliding NumberPickers is much more suitable for this use case however it is no longer available for Marshmallow devices and up. 25 | 26 | This library is heavily based on the latest [Android Open Source Project](https://source.android.com/) DatePicker (source code [here](http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/android/widget/DatePickerSpinnerDelegate.java)) with the addition of being able to style the NumberPickers (the dials/spinners in the DatePicker). 27 | 28 | ## Adding styles 29 | 30 | The DatePicker is the simple aggregation of three NumberPickers. You can style the DatePicker easily with a NumberPicker style (in styles.xml in the values folder): 31 | 32 | 37 | 38 | where `colorControlNormal` is the color of the horizontal bars (dividers) in the `NumberPicker`. See [this StackOverflow question](https://stackoverflow.com/q/20148671/5241933) 39 | 40 | And then: 41 | 42 | new SpinnerDatePickerDialogBuilder() 43 | .context(MainActivity.this) 44 | .callback(MainActivity.this) 45 | .spinnerTheme(R.style.NumberPickerStyle) 46 | .showTitle(true) 47 | .customTitle("My custom title") 48 | .showDaySpinner(true) 49 | .defaultDate(2017, 0, 1) 50 | .maxDate(2020, 0, 1) 51 | .minDate(2000, 0, 1) 52 | .build() 53 | .show(); 54 | 55 | The example project should make it clear - get it by cloning the repo. 56 | 57 | Note that full support is only for API >= 18. API < 18 you'll get the DatePicker but there is no easy way to style it correctly. 58 | 59 | ## Usage in a project 60 | 61 | Add the following to your **project** level `build.gradle`: 62 | 63 | ```gradle 64 | allprojects { 65 | repositories { 66 | maven { url "https://jitpack.io" } 67 | } 68 | } 69 | ``` 70 | 71 | Add this to your **app level** `build.gradle`: 72 | 73 | ```gradle 74 | dependencies { 75 | compile 'com.github.drawers:SpinnerDatePicker:2.0.1' 76 | } 77 | ``` 78 | 79 | Philosophy 80 | ========== 81 | 82 | The aim of this project is to produce a lightweight and robust DatePicker with an API similar to that of the standard Android DatePicker. Hence the library has no external dependencies and no fancy features. Espresso automated UI testing is performed on the sample project using Firebase test lab. 83 | 84 | Contributing 85 | ============ 86 | 87 | Please open an issue first before making a pull request. Pull requests should be accompanied by tests if possible. 88 | 89 | License 90 | ======= 91 | 92 | Copyright 2017 AOSP, David Rawson 93 | 94 | Licensed under the Apache License, Version 2.0 (the "License"); 95 | you may not use this file except in compliance with the License. 96 | You may obtain a copy of the License at 97 | 98 | http://www.apache.org/licenses/LICENSE-2.0 99 | 100 | Unless required by applicable law or agreed to in writing, software 101 | distributed under the License is distributed on an "AS IS" BASIS, 102 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 103 | See the License for the specific language governing permissions and 104 | limitations under the License. 105 | -------------------------------------------------------------------------------- /SpinnerDatePickerExample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /SpinnerDatePickerExample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.tsongkha.spinnerdatepickerexample" 7 | minSdkVersion 16 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0.1" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | androidTestCompile 'androidx.test.espresso:espresso-core:3.1.1' 23 | androidTestCompile 'androidx.test:rules:1.1.1' 24 | 25 | compile project(':SpinnerDatePickerLib') 26 | compile 'androidx.appcompat:appcompat:1.0.0' 27 | compile 'androidx.constraintlayout:constraintlayout:1.1.2' 28 | compile 'com.google.android.material:material:1.0.0' 29 | } 30 | -------------------------------------------------------------------------------- /SpinnerDatePickerExample/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/rawsond/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /SpinnerDatePickerExample/src/androidTest/java/com/tsongkha/spinnerdatepickerexample/MainActivityTest.java: -------------------------------------------------------------------------------- 1 | package com.tsongkha.spinnerdatepickerexample; 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers; 4 | import androidx.test.rule.ActivityTestRule; 5 | import androidx.test.runner.AndroidJUnit4; 6 | 7 | import com.tsongkha.spinnerdatepicker.SpinnerDatePickerDialogBuilder; 8 | 9 | import org.junit.Before; 10 | import org.junit.Rule; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | import static androidx.test.espresso.Espresso.onView; 15 | import static androidx.test.espresso.action.ViewActions.click; 16 | import static androidx.test.espresso.assertion.ViewAssertions.matches; 17 | import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; 18 | import static androidx.test.espresso.matcher.ViewMatchers.withClassName; 19 | import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; 20 | import static androidx.test.espresso.matcher.ViewMatchers.withId; 21 | import static androidx.test.espresso.matcher.ViewMatchers.withText; 22 | import static org.hamcrest.Matchers.containsString; 23 | 24 | 25 | @RunWith(AndroidJUnit4.class) 26 | public class MainActivityTest { 27 | 28 | public static final int SCROLL_UP = 40; 29 | public static final int SCROLL_DOWN = -40; 30 | 31 | @Rule 32 | public ActivityTestRule mainActivityActivityTestRule = new ActivityTestRule<>( 33 | MainActivity.class, true, false); 34 | 35 | MainActivity mainActivity; 36 | 37 | @Before 38 | public void setUp() throws Exception { 39 | mainActivity = mainActivityActivityTestRule.launchActivity(null); 40 | } 41 | 42 | @Test 43 | public void testDefaultDatePickerDialogDisplays() { 44 | //act 45 | onView(withId(R.id.set_date_button)).perform(click()); 46 | 47 | //assert 48 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.datePickerContainer)) 49 | .check(matches(isCompletelyDisplayed())); 50 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.parent)) 51 | .check(matches(isCompletelyDisplayed())); 52 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.year)) 53 | .check(NumberPickers.isDisplayed("1980")); 54 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.month)) 55 | .check(NumberPickers.isDisplayed("Jan")); 56 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.day)) 57 | .check(NumberPickers.isDisplayed("1")); 58 | onView(withClassName(containsString("DialogTitle"))).check( 59 | matches(withText("January 1, 1980"))); 60 | } 61 | 62 | @Test 63 | public void testDaySpinner() throws Exception { 64 | //act 65 | mainActivity.runOnUiThread(new Runnable() { 66 | @Override 67 | public void run() { 68 | mainActivity.showDate(1980, 0, 1, R.style.DatePickerSpinner); 69 | } 70 | }); 71 | 72 | //assert 73 | onView(withId(R.id.day)).perform(NumberPickers.scroll(SCROLL_DOWN)).check( 74 | NumberPickers.isDisplayed("2")); 75 | onView(withId(R.id.day)).perform(NumberPickers.scroll(SCROLL_UP)).check( 76 | NumberPickers.isDisplayed("1")); 77 | onView(withId(R.id.day)).perform(NumberPickers.scroll(SCROLL_UP)).check( 78 | NumberPickers.isDisplayed("31")); 79 | onView(withClassName(containsString("DialogTitle"))).check( 80 | matches(withText("December 31, 1979"))); 81 | } 82 | 83 | @Test 84 | public void testDaySpinnerNotShown() throws Exception { 85 | //act 86 | mainActivity.runOnUiThread(new Runnable() { 87 | @Override 88 | public void run() { 89 | new SpinnerDatePickerDialogBuilder() 90 | .context(mainActivity) 91 | .showDaySpinner(false) 92 | .spinnerTheme(R.style.DatePickerSpinner) 93 | .build() 94 | .show(); 95 | } 96 | }); 97 | 98 | //assert 99 | onView(withId(R.id.day)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); 100 | onView(withId(R.id.month)).perform(NumberPickers.scroll(SCROLL_UP)).check( 101 | NumberPickers.isDisplayed("Dec")); 102 | } 103 | 104 | @Test 105 | public void testMonthSpinner() throws Exception { 106 | //act 107 | mainActivity.runOnUiThread(new Runnable() { 108 | @Override 109 | public void run() { 110 | mainActivity.showDate(1980, 0, 1, R.style.DatePickerSpinner); 111 | } 112 | }); 113 | 114 | //assert 115 | onView(withId(R.id.month)).perform(NumberPickers.scroll(SCROLL_DOWN)).check( 116 | NumberPickers.isDisplayed("Feb")); 117 | onView(withId(R.id.month)).perform(NumberPickers.scroll(SCROLL_UP)).check( 118 | NumberPickers.isDisplayed("Jan")); 119 | onView(withId(R.id.month)).perform(NumberPickers.scroll(SCROLL_UP)).check( 120 | NumberPickers.isDisplayed("Dec")); 121 | onView(withClassName(containsString("DialogTitle"))).check( 122 | matches(withText("December 1, 1979"))); 123 | } 124 | 125 | @Test 126 | public void testYearSpinner() throws Exception { 127 | //act 128 | mainActivity.runOnUiThread(new Runnable() { 129 | @Override 130 | public void run() { 131 | mainActivity.showDate(1980, 0, 1, R.style.DatePickerSpinner); 132 | } 133 | }); 134 | 135 | //assert 136 | onView(withId(R.id.year)).perform(NumberPickers.scroll(SCROLL_UP)).check( 137 | NumberPickers.isDisplayed("1979")); 138 | onView(withId(R.id.year)).perform(NumberPickers.scroll(SCROLL_DOWN)).check( 139 | NumberPickers.isDisplayed("1980")); 140 | onView(withId(R.id.year)).perform(NumberPickers.scroll(SCROLL_DOWN)).check( 141 | NumberPickers.isDisplayed("1981")); 142 | onView(withClassName(containsString("DialogTitle"))).check( 143 | matches(withText("January 1, 1981"))); 144 | } 145 | 146 | @Test 147 | public void testCustomDate() throws Exception { 148 | //act 149 | mainActivity.runOnUiThread(new Runnable() { 150 | @Override 151 | public void run() { 152 | mainActivity.showDate(1970, 11, 31, R.style.DatePickerSpinner); 153 | } 154 | }); 155 | 156 | //assert 157 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.datePickerContainer)) 158 | .check(matches(isCompletelyDisplayed())); 159 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.parent)) 160 | .check(matches(isCompletelyDisplayed())); 161 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.year)) 162 | .check(NumberPickers.isDisplayed("1970")); 163 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.month)) 164 | .check(NumberPickers.isDisplayed("Dec")); 165 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.day)) 166 | .check(NumberPickers.isDisplayed("31")); 167 | onView(withClassName(containsString("DialogTitle"))).check( 168 | matches(withText("December 31, 1970"))); 169 | } 170 | 171 | @Test 172 | public void testDaysInMonthDecreaseViaMonthChange() throws Exception { 173 | mainActivity.runOnUiThread(new Runnable() { 174 | @Override 175 | public void run() { 176 | mainActivity.showDate(1980, 0, 31, R.style.DatePickerSpinner); 177 | } 178 | }); 179 | 180 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.month)) 181 | .perform(NumberPickers.scroll(SCROLL_DOWN)); 182 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.day)) 183 | .check(NumberPickers.isDisplayed("29")); 184 | onView(withClassName(containsString("DialogTitle"))).check( 185 | matches(withText("February 29, 1980"))); 186 | } 187 | 188 | @Test 189 | public void testDaysInMonthDecreaseViaYearChange() throws Exception { 190 | mainActivity.runOnUiThread(new Runnable() { 191 | @Override 192 | public void run() { 193 | mainActivity.showDate(1980, 1, 29, R.style.DatePickerSpinner); 194 | } 195 | }); 196 | 197 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.year)) 198 | .perform(NumberPickers.scroll(SCROLL_DOWN)); 199 | onView(withId(com.tsongkha.spinnerdatepicker.R.id.day)) 200 | .check(NumberPickers.isDisplayed("1")); 201 | onView(withClassName(containsString("DialogTitle"))).check( 202 | matches(withText("March 1, 1981"))); 203 | } 204 | 205 | @Test 206 | public void testTitleNotShown() throws Exception { 207 | //act 208 | mainActivity.runOnUiThread(new Runnable() { 209 | @Override 210 | public void run() { 211 | new SpinnerDatePickerDialogBuilder() 212 | .context(mainActivity) 213 | .showTitle(false) 214 | .spinnerTheme(R.style.DatePickerSpinner) 215 | .build() 216 | .show(); 217 | } 218 | }); 219 | 220 | //assert 221 | onView(withClassName(containsString("DialogTitle"))).check( 222 | matches(withText(" "))); 223 | } 224 | 225 | @Test 226 | public void testIsCustomTitleShown() throws Exception { 227 | //act 228 | mainActivity.runOnUiThread(new Runnable() { 229 | @Override 230 | public void run() { 231 | new SpinnerDatePickerDialogBuilder() 232 | .context(mainActivity) 233 | .showTitle(true) 234 | .customTitle("My custom title") 235 | .spinnerTheme(R.style.DatePickerSpinner) 236 | .build() 237 | .show(); 238 | } 239 | }); 240 | 241 | //assert 242 | onView(withClassName(containsString("DialogTitle"))).check( 243 | matches(withText("My custom title"))); 244 | } 245 | } -------------------------------------------------------------------------------- /SpinnerDatePickerExample/src/androidTest/java/com/tsongkha/spinnerdatepickerexample/NumberPickers.java: -------------------------------------------------------------------------------- 1 | package com.tsongkha.spinnerdatepickerexample; 2 | 3 | import androidx.test.espresso.NoMatchingViewException; 4 | import androidx.test.espresso.UiController; 5 | import androidx.test.espresso.ViewAction; 6 | import androidx.test.espresso.ViewAssertion; 7 | import androidx.test.espresso.action.ViewActions; 8 | import androidx.test.espresso.matcher.ViewMatchers; 9 | import android.view.View; 10 | import android.widget.EditText; 11 | import android.widget.NumberPicker; 12 | 13 | import org.hamcrest.Matcher; 14 | 15 | import java.lang.reflect.Field; 16 | 17 | import static androidx.test.espresso.Espresso.onView; 18 | import static junit.framework.Assert.assertEquals; 19 | 20 | /** 21 | * Created by rawsond on 27/08/17. 22 | */ 23 | 24 | class NumberPickers { 25 | 26 | @Deprecated 27 | public static ViewAction setNumber(final int num) { 28 | return new ViewAction() { 29 | @Override 30 | public void perform(UiController uiController, View view) { 31 | NumberPicker np = (NumberPicker) view; 32 | np.setValue(num); 33 | getOnValueChangeListener(np).onValueChange(np, num, num); 34 | } 35 | 36 | @Override 37 | public String getDescription() { 38 | return "Set the passed number into the NumberPicker"; 39 | } 40 | 41 | @Override 42 | public Matcher getConstraints() { 43 | return ViewMatchers.isAssignableFrom(NumberPicker.class); 44 | } 45 | }; 46 | } 47 | 48 | public static ViewAction typeInNumberPicker(final String value) { 49 | return new ViewAction() { 50 | @Override 51 | public void perform(UiController uiController, View view) { 52 | NumberPicker np = (NumberPicker) view; 53 | EditText et = com.tsongkha.spinnerdatepicker.NumberPickers.findEditText(np); 54 | ViewActions.typeText(value).perform(uiController, et); 55 | ViewActions.closeSoftKeyboard(); 56 | } 57 | 58 | @Override 59 | public String getDescription() { 60 | return "Set the passed number into the NumberPicker"; 61 | } 62 | 63 | @Override 64 | public Matcher getConstraints() { 65 | return ViewMatchers.isAssignableFrom(NumberPicker.class); 66 | } 67 | }; 68 | } 69 | 70 | public static ViewAction scroll(final int yOffsetInDp) { 71 | return new ViewAction() { 72 | @Override 73 | public void perform(UiController uiController, View view) { 74 | NumberPicker np = (NumberPicker) view; 75 | int yOffsetInPx = (int) (yOffsetInDp * view.getResources().getDisplayMetrics().density); 76 | np.scrollBy(0, yOffsetInPx); 77 | } 78 | 79 | @Override 80 | public String getDescription() { 81 | return "Scroll up or down a given yOffset in dp"; 82 | } 83 | 84 | @Override 85 | public Matcher getConstraints() { 86 | return ViewMatchers.isAssignableFrom(NumberPicker.class); 87 | } 88 | }; 89 | } 90 | 91 | public static ViewAssertion isDisplayed(final String expectedPick) { 92 | return new ViewAssertion() { 93 | @Override 94 | public void check(View view, NoMatchingViewException noViewFoundException) { 95 | NumberPicker numberPicker = (NumberPicker) view; 96 | String[] displayedValues = numberPicker.getDisplayedValues(); 97 | String actualPicked = null; 98 | if (displayedValues == null) { 99 | actualPicked = Integer.toString(numberPicker.getValue()); 100 | } else { 101 | actualPicked = numberPicker.getDisplayedValues()[numberPicker.getValue()]; 102 | } 103 | assertEquals(expectedPick, actualPicked); 104 | } 105 | }; 106 | } 107 | 108 | public static NumberPicker.OnValueChangeListener getOnValueChangeListener(NumberPicker numberPicker) { 109 | try { 110 | Field onValueChangedListener = NumberPicker.class.getDeclaredField( 111 | "mOnValueChangeListener"); 112 | onValueChangedListener.setAccessible(true); 113 | return (NumberPicker.OnValueChangeListener) onValueChangedListener.get(numberPicker); 114 | } catch (NoSuchFieldException | IllegalAccessException e) { 115 | e.printStackTrace(); 116 | return null; 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /SpinnerDatePickerExample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /SpinnerDatePickerExample/src/main/java/com/tsongkha/spinnerdatepickerexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.tsongkha.spinnerdatepickerexample; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.Nullable; 5 | import androidx.annotation.VisibleForTesting; 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.TextView; 10 | 11 | import com.tsongkha.spinnerdatepicker.DatePicker; 12 | import com.tsongkha.spinnerdatepicker.DatePickerDialog; 13 | import com.tsongkha.spinnerdatepicker.SpinnerDatePickerDialogBuilder; 14 | 15 | import java.text.SimpleDateFormat; 16 | import java.util.Calendar; 17 | import java.util.GregorianCalendar; 18 | import java.util.Locale; 19 | 20 | /** 21 | * Created by rawsond on 25/08/17. 22 | */ 23 | 24 | public class MainActivity extends AppCompatActivity implements DatePickerDialog.OnDateSetListener , DatePickerDialog.OnDateCancelListener{ 25 | 26 | TextView dateTextView; 27 | Button dateButton; 28 | SimpleDateFormat simpleDateFormat; 29 | 30 | @Override 31 | protected void onCreate(@Nullable Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_main); 34 | dateButton = (Button) findViewById(R.id.set_date_button); 35 | dateTextView = (TextView) findViewById(R.id.date_textview); 36 | simpleDateFormat = new SimpleDateFormat("dd MM yyyy", Locale.US); 37 | dateButton.setOnClickListener(new View.OnClickListener() { 38 | @Override 39 | public void onClick(View view) { 40 | showDate(1980, 0, 1, R.style.DatePickerSpinner); 41 | } 42 | }); 43 | } 44 | 45 | @Override 46 | public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { 47 | Calendar calendar = new GregorianCalendar(year, monthOfYear, dayOfMonth); 48 | dateTextView.setText(simpleDateFormat.format(calendar.getTime())); 49 | } 50 | 51 | @Override 52 | public void onCancelled(DatePicker view) { 53 | dateTextView.setText(R.string.cancelled); 54 | } 55 | 56 | 57 | @VisibleForTesting 58 | void showDate(int year, int monthOfYear, int dayOfMonth, int spinnerTheme) { 59 | new SpinnerDatePickerDialogBuilder() 60 | .context(MainActivity.this) 61 | .callback(MainActivity.this) 62 | .onCancel(MainActivity.this) 63 | .spinnerTheme(spinnerTheme) 64 | .defaultDate(year, monthOfYear, dayOfMonth) 65 | .build() 66 | .show(); 67 | } 68 | 69 | 70 | } -------------------------------------------------------------------------------- /SpinnerDatePickerExample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 |