diff --git a/materialwheelview/src/main/java/io/blackbox_vision/wheelview/view/DatePickerWheelView.java b/materialwheelview/src/main/java/io/blackbox_vision/wheelview/view/DatePickerWheelView.java index ca29953..221b54a 100644 --- a/materialwheelview/src/main/java/io/blackbox_vision/wheelview/view/DatePickerWheelView.java +++ b/materialwheelview/src/main/java/io/blackbox_vision/wheelview/view/DatePickerWheelView.java @@ -26,6 +26,7 @@ public final class DatePickerWheelView extends LinearLayout { private static final String TAG = DatePickerWheelView.class.getSimpleName(); + private static final String LONG_MONTH_FORMAT = "MMMM"; private static final String SHORT_MONTH_FORMAT = "MMM"; private static final String YEAR_FORMAT = "yyyy"; private static final String MONTH_FORMAT = "MM"; @@ -49,6 +50,7 @@ public final class DatePickerWheelView extends LinearLayout { private boolean showDayMonthYear; private boolean showShortMonths; + private boolean showLongMonths; @NonNull private final List years = new ArrayList<>(); @@ -72,6 +74,8 @@ public final class DatePickerWheelView extends LinearLayout { private Locale locale = Locale.getDefault(); private View rootView; + private String fontTypeface; + private boolean canLoop; public DatePickerWheelView(Context context) { this(context, null, 0); @@ -105,6 +109,7 @@ private void takeStyles(@NonNull AttributeSet attrs) { showDayMonthYear = array.getBoolean(R.styleable.DatePickerWheelView_datePickerWheelViewShowDayMonthYear, false); showShortMonths = array.getBoolean(R.styleable.DatePickerWheelView_datePickerWheelViewShowShortMonths, false); + showLongMonths = array.getBoolean(R.styleable.DatePickerWheelView_datePickerWheelViewShowLongMonths, false); textSize = array.getDimension(R.styleable.DatePickerWheelView_datePickerWheelViewTextSize, 16F); @@ -112,6 +117,8 @@ private void takeStyles(@NonNull AttributeSet attrs) { maxYear = array.getInt(R.styleable.DatePickerWheelView_datePickerWheelViewMaxYear, 2100); initialDate = array.getString(R.styleable.DatePickerWheelView_datePickerWheelViewInitialDate); + fontTypeface = array.getString(R.styleable.DatePickerWheelView_datePickerWheelViewFontTypeface); + canLoop = array.getBoolean(R.styleable.DatePickerWheelView_datePickerWheelViewIsLoopEnabled, false); } } finally { if (null != array) { @@ -144,6 +151,10 @@ private void drawDatePickerWheelView() { monthSpinner.setTextSize(textSize); daySpinner.setTextSize(textSize); + yearSpinner.setIsLoopEnabled(canLoop); + monthSpinner.setIsLoopEnabled(canLoop); + daySpinner.setIsLoopEnabled(canLoop); + yearSpinner.addOnLoopScrollListener(this::updateYearPosition); yearSpinner.addOnLoopScrollListener(this::onDateSelected); @@ -158,10 +169,13 @@ private void drawDatePickerWheelView() { drawDayPickerView(); setInitialPositions(); + setFontTypeface(fontTypeface); } private void updateYearPosition(@NonNull Object item, int position) { this.yearPos = position; + // Leap years have 29 days in February + drawDayPickerView(); invalidate(); } @@ -182,7 +196,8 @@ private void onDateSelected(@NonNull Object item, int position) { calendar.set(Calendar.YEAR, Integer.valueOf(years.get(yearPos))); calendar.set(Calendar.MONTH, monthPos); - calendar.set(Calendar.DAY_OF_MONTH, Integer.valueOf(days.get(dayPos))); + // Prevent an IndexOutOfBoundsException + calendar.set(Calendar.DAY_OF_MONTH, Integer.valueOf(days.get(Math.min(days.size() - 1, dayPos)))); final int year = calendar.get(Calendar.YEAR); final int month = calendar.get(Calendar.MONTH); @@ -196,8 +211,14 @@ private void setInitialPositions() { final Calendar calendar = DateUtils.parseDateString(initialDate); yearPos = years.indexOf(String.valueOf(calendar.get(Calendar.YEAR))); - monthPos = months.indexOf(String.valueOf(calendar.get(Calendar.MONTH) + 1)); - dayPos = days.indexOf(String.valueOf(calendar.get(Calendar.DAY_OF_MONTH))); + String dateFormat = showShortMonths ? SHORT_MONTH_FORMAT : MONTH_FORMAT; + dateFormat = showLongMonths ? LONG_MONTH_FORMAT : dateFormat; + String formattedMonth = formatDate(calendar, locale, dateFormat); + formattedMonth = showShortMonths ? formattedMonth.toUpperCase(locale) : formattedMonth; + // Fixed: months array was accessed without custom format + monthPos = months.indexOf(formattedMonth); + // Fixed: days was accessed without "dd" + dayPos = days.indexOf(formatDate(calendar, locale, DAY_FORMAT)); yearSpinner.setInitialPosition(yearPos); monthSpinner.setInitialPosition(monthPos); @@ -223,7 +244,9 @@ private void drawMonthPickerView() { deleteAll(months); calendar = Calendar.getInstance(locale); - final String dateFormat = showShortMonths ? SHORT_MONTH_FORMAT : MONTH_FORMAT; + String dateFormat = showShortMonths ? SHORT_MONTH_FORMAT : MONTH_FORMAT; + // Support long months + dateFormat = showLongMonths ? LONG_MONTH_FORMAT : dateFormat; for (int j = 0; j <= calendar.getActualMaximum(Calendar.MONTH); j++) { calendar.set(Calendar.YEAR, Integer.valueOf(years.get(yearPos))); @@ -246,6 +269,20 @@ private void drawDayPickerView() { calendar = Calendar.getInstance(locale); + // Fixed: dayPos while changing to a shorter month + Calendar cl = Calendar.getInstance(locale); + cl.set(Calendar.YEAR, year); + cl.set(Calendar.DAY_OF_MONTH, 1); + cl.set(Calendar.MONTH, monthPos); + int actualMaximum = cl.getActualMaximum(Calendar.DAY_OF_MONTH); + + if (dayPos > actualMaximum - 1) { + int delta = dayPos - actualMaximum + 1; + dayPos = delta - 1; + daySpinner.forceScroll(delta); + } + /// + for (int i = 0; i < calendar.getActualMaximum(Calendar.DAY_OF_MONTH); i++) { calendar.set(Calendar.YEAR, year); calendar.set(Calendar.MONTH, monthPos); @@ -289,6 +326,7 @@ public DatePickerWheelView setMaxYear(int maxYear) { } this.maxYear = maxYear; + drawYearPickerView(); invalidate(); return this; } @@ -312,6 +350,13 @@ public DatePickerWheelView setShowShortMonths(boolean showShortMonths) { return this; } + public DatePickerWheelView setShowLongMonths(boolean showLongMonths) { + this.showLongMonths = showLongMonths; + drawMonthPickerView(); + invalidate(); + return this; + } + public DatePickerWheelView setTextSize(float textSize) { this.textSize = textSize; invalidate(); @@ -342,4 +387,11 @@ public DatePickerWheelView setInitialDate(String initialDate) { invalidate(); return this; } + + public DatePickerWheelView setFontTypeface(String fontTypeface) { + yearSpinner.setFontTypeface(fontTypeface); + monthSpinner.setFontTypeface(fontTypeface); + daySpinner.setFontTypeface(fontTypeface); + return this; + } } diff --git a/materialwheelview/src/main/java/io/blackbox_vision/wheelview/view/WheelView.java b/materialwheelview/src/main/java/io/blackbox_vision/wheelview/view/WheelView.java index 5aa9368..64d1c52 100644 --- a/materialwheelview/src/main/java/io/blackbox_vision/wheelview/view/WheelView.java +++ b/materialwheelview/src/main/java/io/blackbox_vision/wheelview/view/WheelView.java @@ -46,7 +46,7 @@ public final class WheelView extends View { private final List listeners = new ArrayList<>(); @NonNull - private final SimpleOnGestureListener onGestureListener = new WheelViewGestureListener(); + private final SimpleOnGestureListener onGestureListener = new WheelViewGestureListener(); private GestureDetector gestureDetector; private final Paint topBottomTextPaint = new Paint(); //paint that draw top and bottom text @@ -83,6 +83,8 @@ public final class WheelView extends View { private int circularRadius; private int widgetWidth; + private String fontTypeface; + public Handler handler = new Handler(msg -> { switch (msg.what) { case MSG_INVALIDATE: @@ -162,20 +164,27 @@ private void initData() { throw new IllegalArgumentException("items list must not be null!"); } + Typeface typeface; + if (fontTypeface != null) { + typeface = Typeface.createFromAsset(getContext().getAssets(), fontTypeface); + } else { + typeface = Typeface.MONOSPACE; + } + topBottomTextPaint.setColor(overflowTextColor); topBottomTextPaint.setAntiAlias(true); - topBottomTextPaint.setTypeface(Typeface.MONOSPACE); + topBottomTextPaint.setTypeface(typeface); topBottomTextPaint.setTextSize(textSize); centerTextPaint.setColor(contentTextColor); centerTextPaint.setAntiAlias(true); centerTextPaint.setTextScaleX(1.05F); - centerTextPaint.setTypeface(Typeface.MONOSPACE); + centerTextPaint.setTypeface(typeface); centerTextPaint.setTextSize(textSize); centerLinePaint.setColor(lineColor); centerLinePaint.setAntiAlias(true); - centerLinePaint.setTypeface(Typeface.MONOSPACE); + centerLinePaint.setTypeface(typeface); centerLinePaint.setTextSize(textSize); measureTextWidthHeight(); @@ -203,7 +212,8 @@ private void measureTextWidthHeight() { final Rect rect = new Rect(); for (int i = 0; i < items.size(); i++) { - final String s1 = (String) items.get(i); + // support lowerCase month names + final String s1 = (String) items.get(i) + "j"; centerTextPaint.getTextBounds(s1, 0, s1.length(), rect); @@ -405,7 +415,7 @@ private void itemSelected() { } private void onItemSelected() { - for (OnLoopScrollListener onLoopScrollListener: listeners) { + for (OnLoopScrollListener onLoopScrollListener : listeners) { onLoopScrollListener.onLoopScrollFinish(items.get(selectedIndex), items.indexOf(items.get(selectedIndex))); } } @@ -429,6 +439,15 @@ private void startSmoothScrollTo(float velocityY) { scheduledFuture = executorService.scheduleWithFixedDelay(new FlingRunnable(velocityY), 0, velocityFling, TimeUnit.MILLISECONDS); } + /** + * This fixes the item amount difference + * + * @param delta Number of items + */ + public void forceScroll(int delta) { + totalScrollY += delta * itemHeight; + } + class WheelViewGestureListener extends SimpleOnGestureListener { @Override @@ -528,7 +547,7 @@ class FlingRunnable implements Runnable { public void run() { if (velocity == Integer.MAX_VALUE) { if (Math.abs(velocityY) > 2000F) { - velocity = (velocityY > 0.0F) ? 2000F : - 2000F; + velocity = (velocityY > 0.0F) ? 2000F : -2000F; } else { velocity = velocityY; } @@ -562,6 +581,19 @@ public void run() { } } + public String getFontTypeface() { + return fontTypeface; + } + + public void setFontTypeface(String fontTypeface) { + this.fontTypeface = fontTypeface; + initData(); + } + + public int getContentTextColor() { + return contentTextColor; + } + public interface OnLoopScrollListener { void onLoopScrollFinish(@NonNull Object item, int position); diff --git a/materialwheelview/src/main/res/values/attrs.xml b/materialwheelview/src/main/res/values/attrs.xml index c26ff9f..9207aee 100644 --- a/materialwheelview/src/main/res/values/attrs.xml +++ b/materialwheelview/src/main/res/values/attrs.xml @@ -18,6 +18,9 @@ + + + \ No newline at end of file