diff --git a/android/src/main/java/com/henninghall/date_picker/DatePickerManager.java b/android/src/main/java/com/henninghall/date_picker/DatePickerManager.java index d095863..a28d33e 100644 --- a/android/src/main/java/com/henninghall/date_picker/DatePickerManager.java +++ b/android/src/main/java/com/henninghall/date_picker/DatePickerManager.java @@ -1,23 +1,22 @@ package com.henninghall.date_picker; import android.support.annotation.Nullable; -import android.util.Log; -import android.view.View; -import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.annotations.ReactProp; + import net.time4j.android.ApplicationStarter; import org.apache.commons.lang3.LocaleUtils; import java.util.Map; -public class DatePickerManager extends SimpleViewManager { +public class DatePickerManager extends SimpleViewManager { public static final String REACT_CLASS = "DatePickerManager"; public static ThemedReactContext context; + private double date; @Override public String getName() { @@ -44,13 +43,12 @@ public class DatePickerManager extends SimpleViewManager { @ReactProp(name = "date") public void setDate(PickerView view, @Nullable double date) { - view.setDate(Utils.unixToDate(date)); + this.date = date; } @ReactProp(name = "locale") public void setLocale(PickerView view, @Nullable String locale) { view.setLocale(LocaleUtils.toLocale(locale.replace('-','_'))); - view.requestLayout(); } @ReactProp(name = "minimumDate") @@ -79,6 +77,13 @@ public class DatePickerManager extends SimpleViewManager { if (interval > 1) view.setMinuteInterval(interval); } + @Override + protected void onAfterUpdateTransaction(PickerView view) { + super.onAfterUpdateTransaction(view); + view.updateDisplayValuesIfNeeded(); + view.setDate(Utils.unixToDate(date)); + } + public Map getExportedCustomBubblingEventTypeConstants() { return MapBuilder.builder() .put("dateChange", MapBuilder.of("phasedRegistrationNames", diff --git a/android/src/main/java/com/henninghall/date_picker/PickerView.java b/android/src/main/java/com/henninghall/date_picker/PickerView.java index 156946b..b004d82 100644 --- a/android/src/main/java/com/henninghall/date_picker/PickerView.java +++ b/android/src/main/java/com/henninghall/date_picker/PickerView.java @@ -9,6 +9,7 @@ import com.facebook.react.uimanager.events.RCTEventEmitter; import com.henninghall.date_picker.wheelFunctions.AnimateToDate; import com.henninghall.date_picker.wheelFunctions.Refresh; import com.henninghall.date_picker.wheelFunctions.SetDate; +import com.henninghall.date_picker.wheelFunctions.UpdateVisibility; import com.henninghall.date_picker.wheelFunctions.WheelFunction; import com.henninghall.date_picker.wheels.AmPmWheel; import com.henninghall.date_picker.wheels.DateWheel; @@ -51,6 +52,9 @@ public class PickerView extends RelativeLayout { public Date maxDate; public Date minDate; private WheelOrderUpdater wheelOrderUpdater; + public boolean isInitialized = false; + public boolean requireDisplayValueUpdate = true; + public PickerView() { super(DatePickerManager.context); @@ -81,9 +85,6 @@ public class PickerView extends RelativeLayout { public void onChange(Wheel wheel) { WritableMap event = Arguments.createMap(); try { - String pattern = dateFormat.toPattern(); - String dateString = getDateString(); - Date date = dateFormat.parse(getDateString()); if (minDate != null && date.before(minDate)) applyOnVisibleWheels(new AnimateToDate(minDate)); else if (maxDate != null && date.after(maxDate)) applyOnVisibleWheels(new AnimateToDate(maxDate)); @@ -97,6 +98,7 @@ public class PickerView extends RelativeLayout { } }; + private final Runnable measureAndLayout = new Runnable() { @Override public void run() { @@ -122,11 +124,13 @@ public class PickerView extends RelativeLayout { } public void setMinimumDate(Date date) { - minDate = DateUtils.truncate(date, Calendar.MINUTE); + minDate = Utils.getTruncatedDateOrNull(date); + requireDisplayValueUpdate = true; } public void setMaximumDate(Date date) { - maxDate = DateUtils.truncate(date, Calendar.MINUTE); + maxDate = Utils.getTruncatedDateOrNull(date); + requireDisplayValueUpdate = true; } public void setDate(Date date) { @@ -137,12 +141,12 @@ public class PickerView extends RelativeLayout { this.locale = locale; dateFormat = new SimpleDateFormat(getDateFormatTemplate(), Locale.US); wheelOrderUpdater.update(locale, mode); - applyOnAllWheels(new Refresh()); + requireDisplayValueUpdate = true; } public void setMinuteInterval(int interval) { this.minuteInterval = interval; - applyOnVisibleWheels(new Refresh()); + requireDisplayValueUpdate = true; } // Rounding cal to closest minute interval @@ -186,7 +190,7 @@ public class PickerView extends RelativeLayout { public void setMode(Mode mode) { this.mode = mode; dateFormat = new SimpleDateFormat(getDateFormatTemplate(), Locale.US); - applyOnAllWheels(new Refresh(false)); + applyOnAllWheels(new UpdateVisibility()); wheelOrderUpdater.update(locale, mode); } @@ -218,4 +222,10 @@ public class PickerView extends RelativeLayout { return onWheelChangeListener; } + public void updateDisplayValuesIfNeeded() { + if(requireDisplayValueUpdate) { + applyOnAllWheels(new Refresh()); + requireDisplayValueUpdate = false; + } + } } diff --git a/android/src/main/java/com/henninghall/date_picker/Utils.java b/android/src/main/java/com/henninghall/date_picker/Utils.java index a38b082..4db69d3 100644 --- a/android/src/main/java/com/henninghall/date_picker/Utils.java +++ b/android/src/main/java/com/henninghall/date_picker/Utils.java @@ -1,11 +1,13 @@ package com.henninghall.date_picker; +import android.text.format.DateUtils; import android.util.TypedValue; import android.view.View; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; import java.util.Locale; @@ -21,7 +23,7 @@ public class Utils { } public static Date unixToDate(double date) { - return new Date((long)date); + return date > 0 ? new Date((long)date) : null; } public static int getWheelHeight(View pickerView) { @@ -37,5 +39,17 @@ public class Utils { pattern = pattern.replaceAll("[.]", "/"); pattern = pattern.replaceAll("-", "/"); return pattern; -} + } + + public static boolean isToday(Calendar cal){ + return DateUtils.isToday(cal.getTimeInMillis()); + } + + public static Date getTruncatedDateOrNull(Date date) { + try { + return org.apache.commons.lang3.time.DateUtils.truncate(date, Calendar.MINUTE); + } catch (Exception e){ + return null; + } + } } diff --git a/android/src/main/java/com/henninghall/date_picker/wheelFunctions/Refresh.java b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/Refresh.java index 1c8fb25..70c07b2 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheelFunctions/Refresh.java +++ b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/Refresh.java @@ -4,19 +4,9 @@ import com.henninghall.date_picker.wheels.Wheel; public class Refresh implements WheelFunction { - private final boolean keepOldValue; - - public Refresh() { - this.keepOldValue = true; - } - - public Refresh(boolean keepOldValue){ - this.keepOldValue = keepOldValue; - } - @Override public void apply(Wheel wheel) { - wheel.refresh(keepOldValue); + wheel.refresh(); } } diff --git a/android/src/main/java/com/henninghall/date_picker/wheelFunctions/UpdateVisibility.java b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/UpdateVisibility.java new file mode 100644 index 0000000..63f3dd2 --- /dev/null +++ b/android/src/main/java/com/henninghall/date_picker/wheelFunctions/UpdateVisibility.java @@ -0,0 +1,13 @@ +package com.henninghall.date_picker.wheelFunctions; + +import com.henninghall.date_picker.wheels.Wheel; + +public class UpdateVisibility implements WheelFunction { + + @Override + public void apply(Wheel wheel) { + wheel.updateVisibility(); + } +} + + diff --git a/android/src/main/java/com/henninghall/date_picker/wheels/DayWheel.java b/android/src/main/java/com/henninghall/date_picker/wheels/DayWheel.java index 106987b..e2bf5ff 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheels/DayWheel.java +++ b/android/src/main/java/com/henninghall/date_picker/wheels/DayWheel.java @@ -3,12 +3,9 @@ package com.henninghall.date_picker.wheels; import com.henninghall.date_picker.Mode; import com.henninghall.date_picker.PickerView; import com.henninghall.date_picker.Utils; -import com.henninghall.date_picker.WheelChangeListener; +import java.text.SimpleDateFormat; import java.util.Calendar; -import java.util.Locale; - -import cn.carbswang.android.numberpickerview.library.NumberPickerView; public class DayWheel extends Wheel { @@ -16,34 +13,71 @@ public class DayWheel extends Wheel { public DayWheel(PickerView pickerView, int id) { super(pickerView, id); } + private static int defaultNumberOfDays = Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_YEAR); @Override void init() { + Calendar cal = getStartCal(); + Calendar endCal = getEndCal(); + + int i = 0; + do { + displayValues.add(getDisplayValue(cal)); + values.add(getValueFormat(cal)); + cal.add(Calendar.DATE, 1); + i++; + } + while (!isSameDay(cal,endCal)); - int min = 0; - int max = 10000; // bug - Calendar cal = pickerView.getInitialDate(); - cal.add(Calendar.DAY_OF_MONTH, -max/2); + picker.setMaxValue(0); + picker.setDisplayedValues(displayValues.toArray(new String[0])); + picker.setMinValue(0); + picker.setMaxValue(i-1); + } - for(int i=0; i<=(max-min); i++){ - values.add(format.format(cal.getTime())); + private Calendar getStartCal(){ + Calendar cal = pickerView.getInitialDate(); + if (pickerView.minDate != null) { + cal.setTime(pickerView.minDate); + } + else cal.add(Calendar.DATE, -defaultNumberOfDays / 2); + return cal; + } - // Print "today" if date is today - if(i == max/2){ - String todayString = Utils.printToday(pickerView.locale); - String todayStringCapitalized = todayString .substring(0, 1).toUpperCase() + todayString.substring(1); - displayValues.add(todayStringCapitalized); - } - else displayValues.add(displayFormat.format(cal.getTime()).substring(3)); - cal.add(Calendar.DAY_OF_MONTH, 1); + private Calendar getEndCal(){ + Calendar cal = Calendar.getInstance(); + if (pickerView.maxDate != null) { + cal.setTime(pickerView.maxDate); } + else cal.add(Calendar.DATE, defaultNumberOfDays / 2); + return cal; + } - picker.setDisplayedValues(displayValues.toArray(new String[0])); + private boolean isSameDay(Calendar c1, Calendar c2) { + SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy", pickerView.locale); + return (sdf.format(c1.getTime()).equals(sdf.format(c2.getTime()))); + } + + private String getDisplayValue(Calendar cal){ + return Utils.isToday(cal) ? getTodayString() : getDateString(cal); + } + + private String getValueFormat(Calendar cal){ + return format.format(cal.getTime()); + } - picker.setMinValue(min); - picker.setMaxValue(max); - picker.setValue(5000); + private String getDateString(Calendar cal){ + return displayFormat.format(cal.getTime()).substring(3); + } + + private String getTodayString(){ + String todayString = Utils.printToday(pickerView.locale); + return capitalize(todayString); + } + + private String capitalize(String s){ + return s.substring(0, 1).toUpperCase() + s.substring(1); } @Override @@ -56,5 +90,4 @@ public class DayWheel extends Wheel { return "yy EEE d MMM"; } - } diff --git a/android/src/main/java/com/henninghall/date_picker/wheels/Wheel.java b/android/src/main/java/com/henninghall/date_picker/wheels/Wheel.java index c15d4df..a77490d 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheels/Wheel.java +++ b/android/src/main/java/com/henninghall/date_picker/wheels/Wheel.java @@ -32,8 +32,8 @@ public abstract class Wheel { this.id = id; this.self = this; this.pickerView = pickerView; - this.picker = (NumberPickerView) pickerView.findViewById(id); - refresh(false); + this.picker = pickerView.findViewById(id); + clearValues(); picker.setOnValueChangedListener(new NumberPickerView.OnValueChangeListener() { @Override public void onValueChange(NumberPickerView picker, int oldVal, int newVal) { @@ -42,6 +42,13 @@ public abstract class Wheel { }); } + private void clearValues(){ + this.displayFormat = new SimpleDateFormat(getFormatTemplate(), pickerView.locale); + this.format = new SimpleDateFormat(getFormatTemplate(), LocaleUtils.toLocale("en_US")); + this.values = new ArrayList<>(); + this.displayValues= new ArrayList<>(); + } + public int getIndexOfDate(Date date){ return values.indexOf(format.format(date)); } @@ -66,37 +73,22 @@ public abstract class Wheel { public void setValue(Date date) { this.userSetValue = format.format(date); int index = getIndexOfDate(date); - if(index > -1) { - // Set value directly during initializing - // After init, always smooth scroll to value + if(index > -1) { + // Set value directly during initializing. After init, always smooth scroll to value if(picker.getValue() == 0) picker.setValue(index); else picker.smoothScrollToValue(index); } } - public void refresh(boolean keepOldValue) { - this.displayFormat = new SimpleDateFormat(getFormatTemplate(), pickerView.locale); - this.format = new SimpleDateFormat(getFormatTemplate(), LocaleUtils.toLocale("en_US")); - this.values = new ArrayList<>(); - this.displayValues= new ArrayList<>(); - int oldValue = picker.getValue(); - - if (visible()) { - add(); - init(); - if(keepOldValue) picker.setValue(oldValue); - - } - else remove(); - + public void refresh() { + clearValues(); + init(); } - private void remove() { - picker.setVisibility(View.GONE); - } - private void add() { - picker.setVisibility(View.VISIBLE); + public void updateVisibility(){ + int visibility = visible() ? View.VISIBLE: View.GONE; + picker.setVisibility(visibility); } } diff --git a/android/src/main/java/com/henninghall/date_picker/wheels/YearWheel.java b/android/src/main/java/com/henninghall/date_picker/wheels/YearWheel.java index bf3e3fd..f4e099d 100644 --- a/android/src/main/java/com/henninghall/date_picker/wheels/YearWheel.java +++ b/android/src/main/java/com/henninghall/date_picker/wheels/YearWheel.java @@ -12,7 +12,7 @@ public class YearWheel extends Wheel public YearWheel(final PickerView pickerView, final int id) { super(pickerView, id); - this.defaultStartYear = 0; + this.defaultStartYear = 1900; this.defaultEndYear = 2100; } @@ -27,6 +27,7 @@ public class YearWheel extends Wheel displayValues.add(String.valueOf(startYear + i)); } + picker.setMaxValue(0); picker.setDisplayedValues(displayValues.toArray(new String[0])); picker.setMinValue(0); picker.setMaxValue(max); @@ -36,14 +37,14 @@ public class YearWheel extends Wheel if (this.pickerView.maxDate == null) { return this.defaultEndYear; } - final Calendar cal = Calendar.getInstance(); + final Calendar cal = pickerView.getInitialDate(); cal.setTime(this.pickerView.maxDate); return cal.get(Calendar.YEAR); } private int getStartYear() { if (this.pickerView.minDate != null) { - final Calendar cal = Calendar.getInstance(); + final Calendar cal = pickerView.getInitialDate(); cal.setTime(this.pickerView.minDate); return cal.get(Calendar.YEAR); } diff --git a/android/src/main/res/layout/datepicker_view.xml b/android/src/main/res/layout/datepicker_view.xml index 591980c..c5f8af6 100644 --- a/android/src/main/res/layout/datepicker_view.xml +++ b/android/src/main/res/layout/datepicker_view.xml @@ -24,7 +24,7 @@ app:npv_RespondChangeOnDetached="false" app:npv_TextSizeNormal="18sp" app:npv_TextSizeSelected="21sp" - app:npv_WrapSelectorWheel="true" + app:npv_WrapSelectorWheel="false" app:npv_TextColorSelected="#000000" app:npv_TextColorNormal="#aaaaaa" app:npv_DividerColor="#cccccc" @@ -66,7 +66,7 @@ app:npv_RespondChangeOnDetached="false" app:npv_TextSizeNormal="18sp" app:npv_TextSizeSelected="21sp" - app:npv_WrapSelectorWheel="true" + app:npv_WrapSelectorWheel="false" app:npv_TextColorSelected="#000000" app:npv_TextColorNormal="#aaaaaa" app:npv_DividerColor="#cccccc" diff --git a/example/src/examples/Advanced.js b/example/src/examples/Advanced.js index b5dfce0..82ac18a 100644 --- a/example/src/examples/Advanced.js +++ b/example/src/examples/Advanced.js @@ -5,6 +5,7 @@ import DeviceInfo from 'react-native-device-info'; import DateChange from '../propPickers/DateChange'; import FadeToColor from '../propPickers/FadeToColor'; import LocalePicker from '../propPickers/LocalePicker'; +import MinMaxDateChange from '../propPickers/MinMaxDateChange'; import ModePicker from '../propPickers/ModePicker'; import TextColor from '../propPickers/TextColor'; import PropSlider from '../PropSlider'; @@ -14,6 +15,10 @@ Date.prototype.addHours = function (h) { return this; } +export const defaultMinDate = new Date().addHours(-24 * 5); +export const defaultMaxDate = new Date().addHours(24 * 5); + +export const readableDate = date => date ? date.toISOString().substr(0, 19).replace('T', ' ') : 'undefined' export default class Advanced extends Component { @@ -24,6 +29,8 @@ export default class Advanced extends Component { selectedProp: 'mode', locale: DeviceInfo.getDeviceLocale(), mode: 'datetime', + minDate: defaultMinDate, + maxDate: defaultMaxDate, } render() { @@ -34,13 +41,13 @@ export default class Advanced extends Component { onDateChange={this.setDate} locale={this.state.locale} minuteInterval={1} - minimumDate={new Date()} - maximumDate={(new Date()).addHours(24 * 5 * 1000)} + minimumDate={this.state.minDate} + maximumDate={this.state.maxDate} fadeToColor={this.props.backgroundColor} textColor={this.state.textColor} mode={this.state.mode} /> - Picker date: {this.state.chosenDate.toISOString()} + Picker date: {readableDate(this.state.chosenDate)} Change prop: @@ -69,8 +76,17 @@ export default class Advanced extends Component { this.setState({ chosenDate })} /> }, { name: 'minuteInterval' }, - { name: 'minDate' }, - { name: 'maxDate' }, + { + name: 'minDate', component: + this.setState({ minDate })} + defaultDate={defaultMinDate} + /> + }, + { + name: 'maxDate', component: + this.setState({ maxDate })} + defaultDate={defaultMaxDate} /> + }, { name: 'fadeToColor', component: this.props.setBackground(randomColor())} /> diff --git a/example/src/propPickers/MinMaxDateChange.js b/example/src/propPickers/MinMaxDateChange.js new file mode 100644 index 0000000..099ccbb --- /dev/null +++ b/example/src/propPickers/MinMaxDateChange.js @@ -0,0 +1,43 @@ +import React, { Component } from 'react'; +import { Text, Button, View, StyleSheet } from 'react-native'; +import { readableDate } from '../examples/Advanced' + +export default class extends Component { + + render() { + const { onChange, defaultDate } = this.props; + return ( + + {readableDate(this.props.value)} + + {this.renderButton('-1 hour', -1)} + {this.renderButton('+1 hour', 1)} + + + {this.renderButton('-1 day', -24)} + {this.renderButton('+1 day', 24)} + + + + + + + ) + } + + renderButton = (title, hourDiff) => + +} + + +const PropButton = ({ title, value, onChange }) =>