summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2015-03-23 13:13:25 -0700
committerAlan Viverette <alanv@google.com>2015-03-23 13:13:25 -0700
commit0ef59ac0e57e9b99d174d4a53f7d9639357743ac (patch)
treeab71b8c3506571d5ce39a27bd7bb7a3ed1e65a21 /core/java
parent2a16460c7c914729e9c256ce39d681524d53b7dc (diff)
downloadframeworks_base-0ef59ac0e57e9b99d174d4a53f7d9639357743ac.zip
frameworks_base-0ef59ac0e57e9b99d174d4a53f7d9639357743ac.tar.gz
frameworks_base-0ef59ac0e57e9b99d174d4a53f7d9639357743ac.tar.bz2
Update DatePicker and CalendarView to latest Material spec
Bug: 19431364 Change-Id: If364a051a5208d170495de4182e46b32c7560e08
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/content/res/ColorStateList.java27
-rw-r--r--core/java/android/widget/CalendarView.java199
-rw-r--r--core/java/android/widget/CalendarViewLegacyDelegate.java9
-rw-r--r--core/java/android/widget/CalendarViewMaterialDelegate.java160
-rw-r--r--core/java/android/widget/DatePicker.java13
-rwxr-xr-xcore/java/android/widget/DatePickerCalendarDelegate.java463
-rw-r--r--core/java/android/widget/DayPickerAdapter.java283
-rw-r--r--core/java/android/widget/DayPickerView.java560
-rw-r--r--core/java/android/widget/SimpleMonthAdapter.java220
-rw-r--r--core/java/android/widget/SimpleMonthView.java695
-rw-r--r--core/java/android/widget/TextViewWithCircularIndicator.java87
-rw-r--r--core/java/android/widget/YearPickerView.java245
-rw-r--r--core/java/com/android/internal/widget/AccessibleDateAnimator.java56
-rw-r--r--core/java/com/android/internal/widget/ViewPager.java34
14 files changed, 1447 insertions, 1604 deletions
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 841b09d..7d8dff3 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -464,6 +464,33 @@ public class ColorStateList implements Parcelable {
return mColors;
}
+ /**
+ * Returns whether the specified state is referenced in any of the state
+ * specs contained within this ColorStateList.
+ * <p>
+ * Any reference, either positive or negative {ex. ~R.attr.state_enabled},
+ * will cause this method to return {@code true}. Wildcards are not counted
+ * as references.
+ *
+ * @param state the state to search for
+ * @return {@code true} if the state if referenced, {@code false} otherwise
+ * @hide Use only as directed. For internal use only.
+ */
+ public boolean hasState(int state) {
+ final int[][] stateSpecs = mStateSpecs;
+ final int specCount = stateSpecs.length;
+ for (int specIndex = 0; specIndex < specCount; specIndex++) {
+ final int[] states = stateSpecs[specIndex];
+ final int stateCount = states.length;
+ for (int stateIndex = 0; stateIndex < stateCount; stateIndex++) {
+ if (states[stateIndex] == state || states[stateIndex] == ~state) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@Override
public String toString() {
return "ColorStateList{" +
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 5bc16cb..2aaa356 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -18,6 +18,7 @@ package android.widget;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.StyleRes;
import android.annotation.Widget;
import android.content.Context;
import android.content.res.Configuration;
@@ -31,6 +32,7 @@ import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
+import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -118,7 +120,9 @@ public class CalendarView extends FrameLayout {
* @param count The shown week count.
*
* @attr ref android.R.styleable#CalendarView_shownWeekCount
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setShownWeekCount(int count) {
mDelegate.setShownWeekCount(count);
}
@@ -129,7 +133,9 @@ public class CalendarView extends FrameLayout {
* @return The shown week count.
*
* @attr ref android.R.styleable#CalendarView_shownWeekCount
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public int getShownWeekCount() {
return mDelegate.getShownWeekCount();
}
@@ -140,7 +146,9 @@ public class CalendarView extends FrameLayout {
* @param color The week background color.
*
* @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setSelectedWeekBackgroundColor(@ColorInt int color) {
mDelegate.setSelectedWeekBackgroundColor(color);
}
@@ -151,8 +159,10 @@ public class CalendarView extends FrameLayout {
* @return The week background color.
*
* @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getSelectedWeekBackgroundColor() {
return mDelegate.getSelectedWeekBackgroundColor();
}
@@ -163,7 +173,9 @@ public class CalendarView extends FrameLayout {
* @param color The focused month date color.
*
* @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setFocusedMonthDateColor(@ColorInt int color) {
mDelegate.setFocusedMonthDateColor(color);
}
@@ -174,8 +186,10 @@ public class CalendarView extends FrameLayout {
* @return The focused month date color.
*
* @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getFocusedMonthDateColor() {
return mDelegate.getFocusedMonthDateColor();
}
@@ -186,7 +200,9 @@ public class CalendarView extends FrameLayout {
* @param color A not focused month date color.
*
* @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setUnfocusedMonthDateColor(@ColorInt int color) {
mDelegate.setUnfocusedMonthDateColor(color);
}
@@ -197,8 +213,10 @@ public class CalendarView extends FrameLayout {
* @return A not focused month date color.
*
* @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getUnfocusedMonthDateColor() {
return mDelegate.getUnfocusedMonthDateColor();
}
@@ -209,7 +227,9 @@ public class CalendarView extends FrameLayout {
* @param color The week number color.
*
* @attr ref android.R.styleable#CalendarView_weekNumberColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setWeekNumberColor(@ColorInt int color) {
mDelegate.setWeekNumberColor(color);
}
@@ -220,8 +240,10 @@ public class CalendarView extends FrameLayout {
* @return The week number color.
*
* @attr ref android.R.styleable#CalendarView_weekNumberColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getWeekNumberColor() {
return mDelegate.getWeekNumberColor();
}
@@ -232,7 +254,9 @@ public class CalendarView extends FrameLayout {
* @param color The week separator color.
*
* @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setWeekSeparatorLineColor(@ColorInt int color) {
mDelegate.setWeekSeparatorLineColor(color);
}
@@ -243,8 +267,10 @@ public class CalendarView extends FrameLayout {
* @return The week separator color.
*
* @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
+ * @deprecated No longer used by Material-style CalendarView.
*/
@ColorInt
+ @Deprecated
public int getWeekSeparatorLineColor() {
return mDelegate.getWeekSeparatorLineColor();
}
@@ -256,7 +282,9 @@ public class CalendarView extends FrameLayout {
* @param resourceId The vertical bar drawable resource id.
*
* @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setSelectedDateVerticalBar(@DrawableRes int resourceId) {
mDelegate.setSelectedDateVerticalBar(resourceId);
}
@@ -268,7 +296,9 @@ public class CalendarView extends FrameLayout {
* @param drawable The vertical bar drawable.
*
* @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public void setSelectedDateVerticalBar(Drawable drawable) {
mDelegate.setSelectedDateVerticalBar(drawable);
}
@@ -278,7 +308,9 @@ public class CalendarView extends FrameLayout {
* the end of the selected date.
*
* @return The vertical bar drawable.
+ * @deprecated No longer used by Material-style CalendarView.
*/
+ @Deprecated
public Drawable getSelectedDateVerticalBar() {
return mDelegate.getSelectedDateVerticalBar();
}
@@ -519,29 +551,36 @@ public class CalendarView extends FrameLayout {
void setShownWeekCount(int count);
int getShownWeekCount();
- void setSelectedWeekBackgroundColor(int color);
+ void setSelectedWeekBackgroundColor(@ColorInt int color);
+ @ColorInt
int getSelectedWeekBackgroundColor();
- void setFocusedMonthDateColor(int color);
+ void setFocusedMonthDateColor(@ColorInt int color);
+ @ColorInt
int getFocusedMonthDateColor();
- void setUnfocusedMonthDateColor(int color);
+ void setUnfocusedMonthDateColor(@ColorInt int color);
+ @ColorInt
int getUnfocusedMonthDateColor();
- void setWeekNumberColor(int color);
+ void setWeekNumberColor(@ColorInt int color);
+ @ColorInt
int getWeekNumberColor();
- void setWeekSeparatorLineColor(int color);
+ void setWeekSeparatorLineColor(@ColorInt int color);
+ @ColorInt
int getWeekSeparatorLineColor();
- void setSelectedDateVerticalBar(int resourceId);
+ void setSelectedDateVerticalBar(@DrawableRes int resourceId);
void setSelectedDateVerticalBar(Drawable drawable);
Drawable getSelectedDateVerticalBar();
- void setWeekDayTextAppearance(int resourceId);
+ void setWeekDayTextAppearance(@StyleRes int resourceId);
+ @StyleRes
int getWeekDayTextAppearance();
- void setDateTextAppearance(int resourceId);
+ void setDateTextAppearance(@StyleRes int resourceId);
+ @StyleRes
int getDateTextAppearance();
void setMinDate(long minDate);
@@ -569,18 +608,12 @@ public class CalendarView extends FrameLayout {
* An abstract class which can be used as a start for CalendarView implementations
*/
abstract static class AbstractCalendarViewDelegate implements CalendarViewDelegate {
- /** String for parsing dates. */
- private static final String DATE_FORMAT = "MM/dd/yyyy";
-
/** The default minimal date. */
protected static final String DEFAULT_MIN_DATE = "01/01/1900";
/** The default maximal date. */
protected static final String DEFAULT_MAX_DATE = "01/01/2100";
- /** Date format for parsing dates. */
- protected static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
-
protected CalendarView mDelegator;
protected Context mContext;
protected Locale mCurrentLocale;
@@ -600,21 +633,131 @@ public class CalendarView extends FrameLayout {
mCurrentLocale = locale;
}
- /**
- * Parses the given <code>date</code> and in case of success sets
- * the result to the <code>outDate</code>.
- *
- * @return True if the date was parsed.
- */
- protected boolean parseDate(String date, Calendar outDate) {
- try {
- outDate.setTime(DATE_FORMATTER.parse(date));
- return true;
- } catch (ParseException e) {
- Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
- return false;
- }
+ @Override
+ public void setShownWeekCount(int count) {
+ // Deprecated.
+ }
+
+ @Override
+ public int getShownWeekCount() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedWeekBackgroundColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getSelectedWeekBackgroundColor() {
+ return 0;
+ }
+
+ @Override
+ public void setFocusedMonthDateColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getFocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setUnfocusedMonthDateColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getUnfocusedMonthDateColor() {
+ return 0;
+ }
+
+ @Override
+ public void setWeekNumberColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getWeekNumberColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setWeekSeparatorLineColor(@ColorInt int color) {
+ // Deprecated.
+ }
+
+ @ColorInt
+ @Override
+ public int getWeekSeparatorLineColor() {
+ // Deprecated.
+ return 0;
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(@DrawableRes int resId) {
+ // Deprecated.
+ }
+
+ @Override
+ public void setSelectedDateVerticalBar(Drawable drawable) {
+ // Deprecated.
+ }
+
+ @Override
+ public Drawable getSelectedDateVerticalBar() {
+ // Deprecated.
+ return null;
+ }
+
+ @Override
+ public void setShowWeekNumber(boolean showWeekNumber) {
+ // Deprecated.
+ }
+
+ @Override
+ public boolean getShowWeekNumber() {
+ // Deprecated.
+ return false;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Nothing to do here, configuration changes are already propagated
+ // by ViewGroup.
}
}
+ /** String for parsing dates. */
+ private static final String DATE_FORMAT = "MM/dd/yyyy";
+
+ /** Date format for parsing dates. */
+ private static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
+
+ /**
+ * Utility method for the date format used by CalendarView's min/max date.
+ *
+ * @hide Use only as directed. For internal use only.
+ */
+ public static boolean parseDate(String date, Calendar outDate) {
+ if (date == null || date.isEmpty()) {
+ return false;
+ }
+
+ try {
+ final Date parsedDate = DATE_FORMATTER.parse(date);
+ outDate.setTime(parsedDate);
+ return true;
+ } catch (ParseException e) {
+ Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 2ab3548..6ab3828 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -27,7 +27,6 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -267,12 +266,12 @@ class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelega
mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
LocaleData.get(Locale.getDefault()).firstDayOfWeek);
final String minDate = a.getString(R.styleable.CalendarView_minDate);
- if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
- parseDate(DEFAULT_MIN_DATE, mMinDate);
+ if (!CalendarView.parseDate(minDate, mMinDate)) {
+ CalendarView.parseDate(DEFAULT_MIN_DATE, mMinDate);
}
final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
- if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
- parseDate(DEFAULT_MAX_DATE, mMaxDate);
+ if (!CalendarView.parseDate(maxDate, mMaxDate)) {
+ CalendarView.parseDate(DEFAULT_MAX_DATE, mMaxDate);
}
if (mMaxDate.before(mMinDate)) {
throw new IllegalArgumentException("Max date cannot be before min date.");
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
index b0f3740..7bce756 100644
--- a/core/java/android/widget/CalendarViewMaterialDelegate.java
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -16,20 +16,11 @@
package android.widget;
-import com.android.internal.R;
-
+import android.annotation.StyleRes;
import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.MathUtils;
import java.util.Calendar;
-import java.util.Locale;
-
-import libcore.icu.LocaleData;
class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDelegate {
private final DayPickerView mDayPickerView;
@@ -40,142 +31,32 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele
int defStyleAttr, int defStyleRes) {
super(delegator, context);
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.CalendarView, defStyleAttr, defStyleRes);
- final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
- LocaleData.get(Locale.getDefault()).firstDayOfWeek);
-
- final long minDate = parseDateToMillis(a.getString(
- R.styleable.CalendarView_minDate), DEFAULT_MIN_DATE);
- final long maxDate = parseDateToMillis(a.getString(
- R.styleable.CalendarView_maxDate), DEFAULT_MAX_DATE);
- if (maxDate < minDate) {
- throw new IllegalArgumentException("max date cannot be before min date");
- }
-
- final long setDate = MathUtils.constrain(System.currentTimeMillis(), minDate, maxDate);
- final int dateTextAppearanceResId = a.getResourceId(
- R.styleable.CalendarView_dateTextAppearance,
- R.style.TextAppearance_DeviceDefault_Small);
-
- a.recycle();
-
- mDayPickerView = new DayPickerView(context);
- mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
- mDayPickerView.setCalendarTextAppearance(dateTextAppearanceResId);
- mDayPickerView.setMinDate(minDate);
- mDayPickerView.setMaxDate(maxDate);
- mDayPickerView.setDate(setDate, false, true);
+ mDayPickerView = new DayPickerView(context, attrs, defStyleAttr, defStyleRes);
mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
delegator.addView(mDayPickerView);
}
- private long parseDateToMillis(String dateStr, String defaultDateStr) {
- final Calendar tempCalendar = Calendar.getInstance();
- if (TextUtils.isEmpty(dateStr) || !parseDate(dateStr, tempCalendar)) {
- parseDate(defaultDateStr, tempCalendar);
- }
- return tempCalendar.getTimeInMillis();
- }
-
@Override
- public void setShownWeekCount(int count) {
- // Deprecated.
- }
-
- @Override
- public int getShownWeekCount() {
- // Deprecated.
- return 0;
- }
-
- @Override
- public void setSelectedWeekBackgroundColor(int color) {
- // TODO: Should use a ColorStateList. Deprecate?
- }
-
- @Override
- public int getSelectedWeekBackgroundColor() {
- return 0;
- }
-
- @Override
- public void setFocusedMonthDateColor(int color) {
- // TODO: Should use a ColorStateList. Deprecate?
- }
-
- @Override
- public int getFocusedMonthDateColor() {
- return 0;
- }
-
- @Override
- public void setUnfocusedMonthDateColor(int color) {
- // TODO: Should use a ColorStateList. Deprecate?
- }
-
- @Override
- public int getUnfocusedMonthDateColor() {
- return 0;
- }
-
- @Override
- public void setWeekDayTextAppearance(int resourceId) {
-
+ public void setWeekDayTextAppearance(@StyleRes int resId) {
+ mDayPickerView.setDayOfWeekTextAppearance(resId);
}
+ @StyleRes
@Override
public int getWeekDayTextAppearance() {
- return 0;
+ return mDayPickerView.getDayOfWeekTextAppearance();
}
@Override
- public void setDateTextAppearance(int resourceId) {
-
+ public void setDateTextAppearance(@StyleRes int resId) {
+ mDayPickerView.setDayTextAppearance(resId);
}
+ @StyleRes
@Override
public int getDateTextAppearance() {
- return 0;
- }
-
- @Override
- public void setWeekNumberColor(int color) {
- // Deprecated.
- }
-
- @Override
- public int getWeekNumberColor() {
- // Deprecated.
- return 0;
- }
-
- @Override
- public void setWeekSeparatorLineColor(int color) {
- // Deprecated.
- }
-
- @Override
- public int getWeekSeparatorLineColor() {
- // Deprecated.
- return 0;
- }
-
- @Override
- public void setSelectedDateVerticalBar(int resourceId) {
- // Deprecated.
- }
-
- @Override
- public void setSelectedDateVerticalBar(Drawable drawable) {
- // Deprecated.
- }
-
- @Override
- public Drawable getSelectedDateVerticalBar() {
- // Deprecated.
- return null;
+ return mDayPickerView.getDayTextAppearance();
}
@Override
@@ -199,17 +80,6 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele
}
@Override
- public void setShowWeekNumber(boolean showWeekNumber) {
- // Deprecated.
- }
-
- @Override
- public boolean getShowWeekNumber() {
- // Deprecated.
- return false;
- }
-
- @Override
public void setFirstDayOfWeek(int firstDayOfWeek) {
mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
}
@@ -221,12 +91,12 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele
@Override
public void setDate(long date) {
- mDayPickerView.setDate(date, true, false);
+ mDayPickerView.setDate(date, true);
}
@Override
public void setDate(long date, boolean animate, boolean center) {
- mDayPickerView.setDate(date, animate, center);
+ mDayPickerView.setDate(date, animate);
}
@Override
@@ -239,12 +109,6 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele
mOnDateChangeListener = listener;
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- // Nothing to do here, configuration changes are already propagated
- // by ViewGroup.
- }
-
private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener =
new DayPickerView.OnDaySelectedListener() {
@Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 45998f7..19ae6e2 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -98,7 +98,7 @@ public class DatePicker extends FrameLayout {
private final DatePickerDelegate mDelegate;
/**
- * The callback used to indicate the user changes\d the date.
+ * The callback used to indicate the user changed the date.
*/
public interface OnDateChangedListener {
@@ -489,15 +489,14 @@ public class DatePicker extends FrameLayout {
mDelegator = delegator;
mContext = context;
- // initialization based on locale
setCurrentLocale(Locale.getDefault());
}
protected void setCurrentLocale(Locale locale) {
- if (locale.equals(mCurrentLocale)) {
- return;
+ if (!locale.equals(mCurrentLocale)) {
+ mCurrentLocale = locale;
+ onLocaleChanged(locale);
}
- mCurrentLocale = locale;
}
@Override
@@ -510,6 +509,10 @@ public class DatePicker extends FrameLayout {
mValidationCallback.onValidationChanged(valid);
}
}
+
+ protected void onLocaleChanged(Locale locale) {
+ // Stub.
+ }
}
/**
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 0e3ec7f..de43b2f 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -26,31 +27,34 @@ import android.os.Parcelable;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.StateSet;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
+import android.widget.DayPickerView.OnDaySelectedListener;
+import android.widget.YearPickerView.OnYearSelectedListener;
import com.android.internal.R;
-import com.android.internal.widget.AccessibleDateAnimator;
import java.text.SimpleDateFormat;
import java.util.Calendar;
-import java.util.HashSet;
import java.util.Locale;
/**
* A delegate for picking up a date (day / month / year).
*/
-class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements
- View.OnClickListener, DatePickerController {
+class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
+
private static final int USE_LOCALE = 0;
private static final int UNINITIALIZED = -1;
- private static final int MONTH_AND_DAY_VIEW = 0;
- private static final int YEAR_VIEW = 1;
+ private static final int VIEW_MONTH_DAY = 0;
+ private static final int VIEW_YEAR = 1;
private static final int DEFAULT_START_YEAR = 1900;
private static final int DEFAULT_END_YEAR = 2100;
@@ -61,33 +65,30 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
private static final int DAY_INDEX = 1;
private static final int YEAR_INDEX = 2;
- private SimpleDateFormat mYearFormat = new SimpleDateFormat("y", Locale.getDefault());
- private SimpleDateFormat mDayFormat = new SimpleDateFormat("d", Locale.getDefault());
+ public static final int[] ATTRS_TEXT_COLOR = new int[]{com.android.internal.R.attr.textColor};
+
+ public static final int[] ATTRS_DISABLED_ALPHA = new int[]{
+ com.android.internal.R.attr.disabledAlpha};
- private TextView mDayOfWeekView;
+ private SimpleDateFormat mYearFormat;
+ private SimpleDateFormat mMonthDayFormat;
- /** Layout that contains the current month, day, and year. */
- private LinearLayout mMonthDayYearLayout;
+ // Top-level container.
+ private ViewGroup mContainer;
- /** Clickable layout that contains the current day and year. */
- private LinearLayout mMonthAndDayLayout;
+ // Header views.
+ private TextView mHeaderYear;
+ private TextView mHeaderMonthDay;
- private TextView mHeaderMonthTextView;
- private TextView mHeaderDayOfMonthTextView;
- private TextView mHeaderYearTextView;
+ // Picker views.
+ private ViewAnimator mAnimator;
private DayPickerView mDayPickerView;
private YearPickerView mYearPickerView;
- private boolean mIsEnabled = true;
-
// Accessibility strings.
- private String mDayPickerDescription;
private String mSelectDay;
- private String mYearPickerDescription;
private String mSelectYear;
- private AccessibleDateAnimator mAnimator;
-
private DatePicker.OnDateChangedListener mDateChangedListener;
private int mCurrentView = UNINITIALIZED;
@@ -99,13 +100,11 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
private int mFirstDayOfWeek = USE_LOCALE;
- private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
-
public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(delegator, context);
- final Locale locale = Locale.getDefault();
+ final Locale locale = mCurrentLocale;
mMinDate = getCalendarForLocale(mMinDate, locale);
mMaxDate = getCalendarForLocale(mMaxDate, locale);
mTempDate = getCalendarForLocale(mMaxDate, locale);
@@ -120,71 +119,64 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
final int layoutResourceId = a.getResourceId(
- R.styleable.DatePicker_internalLayout, R.layout.date_picker_holo);
- final View mainView = inflater.inflate(layoutResourceId, null);
- mDelegator.addView(mainView);
+ R.styleable.DatePicker_internalLayout, R.layout.date_picker_material);
- mDayOfWeekView = (TextView) mainView.findViewById(R.id.date_picker_header);
+ // Set up and attach container.
+ mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator);
- // Layout that contains the current date and day name header.
- final LinearLayout dateLayout = (LinearLayout) mainView.findViewById(
- R.id.day_picker_selector_layout);
- mMonthDayYearLayout = (LinearLayout) mainView.findViewById(
- R.id.date_picker_month_day_year_layout);
- mMonthAndDayLayout = (LinearLayout) mainView.findViewById(
- R.id.date_picker_month_and_day_layout);
- mMonthAndDayLayout.setOnClickListener(this);
- mHeaderMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_month);
- mHeaderDayOfMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_day);
- mHeaderYearTextView = (TextView) mainView.findViewById(R.id.date_picker_year);
- mHeaderYearTextView.setOnClickListener(this);
+ // Set up header views.
+ final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
+ mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year);
+ mHeaderYear.setOnClickListener(mOnHeaderClickListener);
+ mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date);
+ mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
- // Obtain default highlight color from the theme.
- final int defaultHighlightColor = mHeaderYearTextView.getHighlightColor();
+ // For the sake of backwards compatibility, attempt to extract the text
+ // color from the header month text appearance. If it's set, we'll let
+ // that override the "real" header text color.
+ ColorStateList headerTextColor = null;
- // Use Theme attributes if possible
- final int dayOfWeekTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_dayOfWeekTextAppearance, 0);
- if (dayOfWeekTextAppearanceResId != 0) {
- mDayOfWeekView.setTextAppearance(context, dayOfWeekTextAppearanceResId);
+ @SuppressWarnings("deprecation")
+ final int monthHeaderTextAppearance = a.getResourceId(
+ R.styleable.DatePicker_headerMonthTextAppearance, 0);
+ if (monthHeaderTextAppearance != 0) {
+ final TypedArray textAppearance = mContext.obtainStyledAttributes(null,
+ ATTRS_TEXT_COLOR, 0, monthHeaderTextAppearance);
+ final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0);
+ headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor);
+ textAppearance.recycle();
}
- mDayOfWeekView.setBackground(a.getDrawable(R.styleable.DatePicker_dayOfWeekBackground));
-
- dateLayout.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
-
- final int monthTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_headerMonthTextAppearance, 0);
- if (monthTextAppearanceResId != 0) {
- mHeaderMonthTextView.setTextAppearance(context, monthTextAppearanceResId);
+ if (headerTextColor == null) {
+ headerTextColor = a.getColorStateList(R.styleable.DatePicker_headerTextColor);
}
- final int dayOfMonthTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_headerDayOfMonthTextAppearance, 0);
- if (dayOfMonthTextAppearanceResId != 0) {
- mHeaderDayOfMonthTextView.setTextAppearance(context, dayOfMonthTextAppearanceResId);
+ if (headerTextColor != null) {
+ mHeaderYear.setTextColor(headerTextColor);
+ mHeaderMonthDay.setTextColor(headerTextColor);
}
- final int headerYearTextAppearanceResId = a.getResourceId(
- R.styleable.DatePicker_headerYearTextAppearance, 0);
- if (headerYearTextAppearanceResId != 0) {
- mHeaderYearTextView.setTextAppearance(context, headerYearTextAppearanceResId);
+ // Set up header background, if available.
+ if (a.hasValueOrEmpty(R.styleable.DatePicker_headerBackground)) {
+ header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
}
- mDayPickerView = new DayPickerView(mContext);
+ // Set up picker container.
+ mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
+
+ // Set up day picker view.
+ mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker);
mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
- mYearPickerView = new YearPickerView(mContext);
- mYearPickerView.init(this);
+ // Set up year picker view.
+ mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
mYearPickerView.setRange(mMinDate, mMaxDate);
-
- final ColorStateList yearBackgroundColor = a.getColorStateList(
- R.styleable.DatePicker_yearListSelectorColor);
- mYearPickerView.setYearBackgroundColor(yearBackgroundColor);
+ mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+ mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
final int yearTextAppearanceResId = a.getResourceId(
R.styleable.DatePicker_yearListItemTextAppearance, 0);
@@ -192,37 +184,138 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
mYearPickerView.setYearTextAppearance(yearTextAppearanceResId);
}
- final ColorStateList calendarTextColor = a.getColorStateList(
- R.styleable.DatePicker_calendarTextColor);
- mDayPickerView.setCalendarTextColor(calendarTextColor);
+ final int yearActivatedTextAppearanceResId = a.getResourceId(
+ R.styleable.DatePicker_yearListItemActivatedTextAppearance, 0);
+ if (yearActivatedTextAppearanceResId != 0) {
+ mYearPickerView.setYearActivatedTextAppearance(yearActivatedTextAppearanceResId);
+ }
- final ColorStateList calendarDayBackgroundColor = a.getColorStateList(
- R.styleable.DatePicker_calendarDayBackgroundColor);
- mDayPickerView.setCalendarDayBackgroundColor(calendarDayBackgroundColor);
+ a.recycle();
- mDayPickerDescription = res.getString(R.string.day_picker_description);
+ // Set up content descriptions.
mSelectDay = res.getString(R.string.select_day);
- mYearPickerDescription = res.getString(R.string.year_picker_description);
mSelectYear = res.getString(R.string.select_year);
- mAnimator = (AccessibleDateAnimator) mainView.findViewById(R.id.animator);
- mAnimator.addView(mDayPickerView);
- mAnimator.addView(mYearPickerView);
- mAnimator.setDateMillis(mCurrentDate.getTimeInMillis());
+ final Animation inAnim = new AlphaAnimation(0, 1);
+ inAnim.setDuration(ANIMATION_DURATION);
+ mAnimator.setInAnimation(inAnim);
+
+ final Animation outAnim = new AlphaAnimation(1, 0);
+ outAnim.setDuration(ANIMATION_DURATION);
+ mAnimator.setOutAnimation(outAnim);
- final Animation animation = new AlphaAnimation(0.0f, 1.0f);
- animation.setDuration(ANIMATION_DURATION);
- mAnimator.setInAnimation(animation);
+ // Initialize for current locale. This also initializes the date, so no
+ // need to call onDateChanged.
+ onLocaleChanged(mCurrentLocale);
- final Animation animation2 = new AlphaAnimation(1.0f, 0.0f);
- animation2.setDuration(ANIMATION_DURATION);
- mAnimator.setOutAnimation(animation2);
+ setCurrentView(VIEW_MONTH_DAY);
+ }
+
+ /**
+ * The legacy text color might have been poorly defined. Ensures that it
+ * has an appropriate activated state, using the selected state if one
+ * exists or modifying the default text color otherwise.
+ *
+ * @param color a legacy text color, or {@code null}
+ * @return a color state list with an appropriate activated state, or
+ * {@code null} if a valid activated state could not be generated
+ */
+ @Nullable
+ private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) {
+ if (color == null || color.hasState(R.attr.state_activated)) {
+ return color;
+ }
+
+ final int activatedColor;
+ final int defaultColor;
+ if (color.hasState(R.attr.state_selected)) {
+ activatedColor = color.getColorForState(StateSet.get(
+ StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0);
+ defaultColor = color.getColorForState(StateSet.get(
+ StateSet.VIEW_STATE_ENABLED), 0);
+ } else {
+ activatedColor = color.getDefaultColor();
+
+ // Generate a non-activated color using the disabled alpha.
+ final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA);
+ final float disabledAlpha = ta.getFloat(0, 0.30f);
+ defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha);
+ }
+
+ if (activatedColor == 0 || defaultColor == 0) {
+ // We somehow failed to obtain the colors.
+ return null;
+ }
+
+ final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}};
+ final int[] colors = new int[] { activatedColor, defaultColor };
+ return new ColorStateList(stateSet, colors);
+ }
- updateDisplay(false);
- setCurrentView(MONTH_AND_DAY_VIEW);
+ private int multiplyAlphaComponent(int color, float alphaMod) {
+ final int srcRgb = color & 0xFFFFFF;
+ final int srcAlpha = (color >> 24) & 0xFF;
+ final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f);
+ return srcRgb | (dstAlpha << 24);
}
/**
+ * Listener called when the user selects a day in the day picker view.
+ */
+ private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() {
+ @Override
+ public void onDaySelected(DayPickerView view, Calendar day) {
+ mCurrentDate.setTimeInMillis(day.getTimeInMillis());
+ onDateChanged(true, true);
+ }
+ };
+
+ /**
+ * Listener called when the user selects a year in the year picker view.
+ */
+ private final OnYearSelectedListener mOnYearSelectedListener = new OnYearSelectedListener() {
+ @Override
+ public void onYearChanged(YearPickerView view, int year) {
+ // If the newly selected month / year does not contain the
+ // currently selected day number, change the selected day number
+ // to the last day of the selected month or year.
+ // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
+ // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
+ final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
+ final int month = mCurrentDate.get(Calendar.MONTH);
+ final int daysInMonth = getDaysInMonth(month, year);
+ if (day > daysInMonth) {
+ mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
+ }
+
+ mCurrentDate.set(Calendar.YEAR, year);
+ onDateChanged(true, true);
+
+ // Automatically switch to day picker.
+ setCurrentView(VIEW_MONTH_DAY);
+ }
+ };
+
+ /**
+ * Listener called when the user clicks on a header item.
+ */
+ private final OnClickListener mOnHeaderClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ tryVibrate();
+
+ switch (v.getId()) {
+ case R.id.date_picker_header_year:
+ setCurrentView(VIEW_YEAR);
+ break;
+ case R.id.date_picker_header_date:
+ setCurrentView(VIEW_MONTH_DAY);
+ break;
+ }
+ }
+ };
+
+ /**
* Gets a calendar for locale bootstrapped with the value of a given calendar.
*
* @param oldCalendar The old calendar.
@@ -277,85 +370,70 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
return result;
}
- private void updateDisplay(boolean announce) {
- if (mDayOfWeekView != null) {
- mDayOfWeekView.setText(mCurrentDate.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG,
- Locale.getDefault()));
+ @Override
+ protected void onLocaleChanged(Locale locale) {
+ final TextView headerYear = mHeaderYear;
+ if (headerYear == null) {
+ // Abort, we haven't initialized yet. This method will get called
+ // again later after everything has been set up.
+ return;
}
- // Compute indices of Month, Day and Year views
- final String bestDateTimePattern =
- DateFormat.getBestDateTimePattern(mCurrentLocale, "yMMMd");
- final int[] viewIndices = getMonthDayYearIndexes(bestDateTimePattern);
+ // Update the date formatter.
+ final String datePattern = DateFormat.getBestDateTimePattern(locale, "EMMMd");
+ mMonthDayFormat = new SimpleDateFormat(datePattern, locale);
+ mYearFormat = new SimpleDateFormat("y", locale);
- // Position the Year and MonthAndDay views within the header.
- mMonthDayYearLayout.removeAllViews();
- if (viewIndices[YEAR_INDEX] == 0) {
- mMonthDayYearLayout.addView(mHeaderYearTextView);
- mMonthDayYearLayout.addView(mMonthAndDayLayout);
- } else {
- mMonthDayYearLayout.addView(mMonthAndDayLayout);
- mMonthDayYearLayout.addView(mHeaderYearTextView);
- }
+ // Update the header text.
+ onCurrentDateChanged(false);
+ }
- // Position Day and Month views within the MonthAndDay view.
- mMonthAndDayLayout.removeAllViews();
- if (viewIndices[MONTH_INDEX] > viewIndices[DAY_INDEX]) {
- mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView);
- mMonthAndDayLayout.addView(mHeaderMonthTextView);
- } else {
- mMonthAndDayLayout.addView(mHeaderMonthTextView);
- mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView);
+ private void onCurrentDateChanged(boolean announce) {
+ if (mHeaderYear == null) {
+ // Abort, we haven't initialized yet. This method will get called
+ // again later after everything has been set up.
+ return;
}
- mHeaderMonthTextView.setText(mCurrentDate.getDisplayName(Calendar.MONTH, Calendar.SHORT,
- Locale.getDefault()).toUpperCase(Locale.getDefault()));
- mHeaderDayOfMonthTextView.setText(mDayFormat.format(mCurrentDate.getTime()));
- mHeaderYearTextView.setText(mYearFormat.format(mCurrentDate.getTime()));
+ final String year = mYearFormat.format(mCurrentDate.getTime());
+ mHeaderYear.setText(year);
- // Accessibility.
- long millis = mCurrentDate.getTimeInMillis();
- mAnimator.setDateMillis(millis);
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
- String monthAndDayText = DateUtils.formatDateTime(mContext, millis, flags);
- mMonthAndDayLayout.setContentDescription(monthAndDayText);
+ final String monthDay = mMonthDayFormat.format(mCurrentDate.getTime());
+ mHeaderMonthDay.setText(monthDay);
+ // TODO: This should use live regions.
if (announce) {
- flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
- String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
+ final long millis = mCurrentDate.getTimeInMillis();
+ final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
+ final String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
mAnimator.announceForAccessibility(fullDateText);
}
}
private void setCurrentView(final int viewIndex) {
- long millis = mCurrentDate.getTimeInMillis();
-
switch (viewIndex) {
- case MONTH_AND_DAY_VIEW:
- mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
+ case VIEW_MONTH_DAY:
+ mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
+
if (mCurrentView != viewIndex) {
- mMonthAndDayLayout.setSelected(true);
- mHeaderYearTextView.setSelected(false);
- mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW);
+ mHeaderMonthDay.setActivated(true);
+ mHeaderYear.setActivated(false);
+ mAnimator.setDisplayedChild(VIEW_MONTH_DAY);
mCurrentView = viewIndex;
}
- final int flags = DateUtils.FORMAT_SHOW_DATE;
- final String dayString = DateUtils.formatDateTime(mContext, millis, flags);
- mAnimator.setContentDescription(mDayPickerDescription + ": " + dayString);
mAnimator.announceForAccessibility(mSelectDay);
break;
- case YEAR_VIEW:
- mYearPickerView.onDateChanged();
+ case VIEW_YEAR:
+ mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+
if (mCurrentView != viewIndex) {
- mMonthAndDayLayout.setSelected(false);
- mHeaderYearTextView.setSelected(true);
- mAnimator.setDisplayedChild(YEAR_VIEW);
+ mHeaderMonthDay.setActivated(false);
+ mHeaderYear.setActivated(true);
+ mAnimator.setDisplayedChild(VIEW_YEAR);
mCurrentView = viewIndex;
}
- final CharSequence yearString = mYearFormat.format(millis);
- mAnimator.setContentDescription(mYearPickerDescription + ": " + yearString);
mAnimator.announceForAccessibility(mSelectYear);
break;
}
@@ -383,20 +461,18 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
}
private void onDateChanged(boolean fromUser, boolean callbackToClient) {
+ final int year = mCurrentDate.get(Calendar.YEAR);
+
if (callbackToClient && mDateChangedListener != null) {
- final int year = mCurrentDate.get(Calendar.YEAR);
final int monthOfYear = mCurrentDate.get(Calendar.MONTH);
final int dayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH);
mDateChangedListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth);
}
- for (OnDateChangedListener listener : mListeners) {
- listener.onDateChanged();
- }
-
- mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
+ mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
+ mYearPickerView.setYear(year);
- updateDisplay(fromUser);
+ onCurrentDateChanged(fromUser);
if (fromUser) {
tryVibrate();
@@ -477,15 +553,12 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
@Override
public void setEnabled(boolean enabled) {
- mMonthAndDayLayout.setEnabled(enabled);
- mHeaderYearTextView.setEnabled(enabled);
- mAnimator.setEnabled(enabled);
- mIsEnabled = enabled;
+ mContainer.setEnabled(false);
}
@Override
public boolean isEnabled() {
- return mIsEnabled;
+ return mContainer.isEnabled();
}
@Override
@@ -516,8 +589,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
@Override
public void onConfigurationChanged(Configuration newConfig) {
- mYearFormat = new SimpleDateFormat("y", newConfig.locale);
- mDayFormat = new SimpleDateFormat("d", newConfig.locale);
+ setCurrentLocale(newConfig.locale);
}
@Override
@@ -529,9 +601,9 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
int listPosition = -1;
int listPositionOffset = -1;
- if (mCurrentView == MONTH_AND_DAY_VIEW) {
+ if (mCurrentView == VIEW_MONTH_DAY) {
listPosition = mDayPickerView.getMostVisiblePosition();
- } else if (mCurrentView == YEAR_VIEW) {
+ } else if (mCurrentView == VIEW_YEAR) {
listPosition = mYearPickerView.getFirstVisiblePosition();
listPositionOffset = mYearPickerView.getFirstPositionOffset();
}
@@ -544,20 +616,22 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
+ // TODO: Move instance state into DayPickerView, YearPickerView.
mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
mCurrentView = ss.getCurrentView();
mMinDate.setTimeInMillis(ss.getMinDate());
mMaxDate.setTimeInMillis(ss.getMaxDate());
- updateDisplay(false);
+ onCurrentDateChanged(false);
setCurrentView(mCurrentView);
final int listPosition = ss.getListPosition();
if (listPosition != -1) {
- if (mCurrentView == MONTH_AND_DAY_VIEW) {
- mDayPickerView.postSetSelection(listPosition);
- } else if (mCurrentView == YEAR_VIEW) {
- mYearPickerView.postSetSelectionFromTop(listPosition, ss.getListPositionOffset());
+ if (mCurrentView == VIEW_MONTH_DAY) {
+ mDayPickerView.setCurrentItem(listPosition);
+ } else if (mCurrentView == VIEW_YEAR) {
+ final int listPositionOffset = ss.getListPositionOffset();
+ mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
}
}
}
@@ -577,28 +651,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
return DatePicker.class.getName();
}
- @Override
- public void onYearSelected(int year) {
- adjustDayInMonthIfNeeded(mCurrentDate.get(Calendar.MONTH), year);
- mCurrentDate.set(Calendar.YEAR, year);
- onDateChanged(true, true);
-
- // Auto-advance to month and day view.
- setCurrentView(MONTH_AND_DAY_VIEW);
- }
-
- // If the newly selected month / year does not contain the currently selected day number,
- // change the selected day number to the last day of the selected month or year.
- // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
- // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
- private void adjustDayInMonthIfNeeded(int month, int year) {
- int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
- int daysInMonth = getDaysInMonth(month, year);
- if (day > daysInMonth) {
- mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
- }
- }
-
public static int getDaysInMonth(int month, int year) {
switch (month) {
case Calendar.JANUARY:
@@ -621,43 +673,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i
}
}
- @Override
- public void registerOnDateChangedListener(OnDateChangedListener listener) {
- mListeners.add(listener);
- }
-
- @Override
- public Calendar getSelectedDay() {
- return mCurrentDate;
- }
-
- @Override
- public void tryVibrate() {
+ private void tryVibrate() {
mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
}
- @Override
- public void onClick(View v) {
- tryVibrate();
- if (v.getId() == R.id.date_picker_year) {
- setCurrentView(YEAR_VIEW);
- } else if (v.getId() == R.id.date_picker_month_and_day_layout) {
- setCurrentView(MONTH_AND_DAY_VIEW);
- }
- }
-
- /**
- * Listener called when the user selects a day in the day picker view.
- */
- private final DayPickerView.OnDaySelectedListener
- mOnDaySelectedListener = new DayPickerView.OnDaySelectedListener() {
- @Override
- public void onDaySelected(DayPickerView view, Calendar day) {
- mCurrentDate.setTimeInMillis(day.getTimeInMillis());
- onDateChanged(true, true);
- }
- };
-
/**
* Class for managing state storing/restoring.
*/
diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerAdapter.java
new file mode 100644
index 0000000..4f9f09e
--- /dev/null
+++ b/core/java/android/widget/DayPickerAdapter.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import com.android.internal.widget.PagerAdapter;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SimpleMonthView.OnDayClickListener;
+
+import java.util.Calendar;
+
+/**
+ * An adapter for a list of {@link android.widget.SimpleMonthView} items.
+ */
+class DayPickerAdapter extends PagerAdapter {
+ private static final int MONTHS_IN_YEAR = 12;
+
+ private final Calendar mMinDate = Calendar.getInstance();
+ private final Calendar mMaxDate = Calendar.getInstance();
+
+ private final SparseArray<SimpleMonthView> mItems = new SparseArray<>();
+
+ private Calendar mSelectedDay = Calendar.getInstance();
+
+ private int mMonthTextAppearance;
+ private int mDayOfWeekTextAppearance;
+ private int mDayTextAppearance;
+
+ private ColorStateList mCalendarTextColor;
+ private ColorStateList mDaySelectorColor;
+ private ColorStateList mDayHighlightColor;
+
+ private OnDaySelectedListener mOnDaySelectedListener;
+
+ private int mFirstDayOfWeek;
+
+ public DayPickerAdapter(Context context) {
+ final TypedArray ta = context.obtainStyledAttributes(new int[] {
+ com.android.internal.R.attr.colorControlHighlight});
+ mDayHighlightColor = ta.getColorStateList(0);
+ ta.recycle();
+ }
+
+ public void setRange(Calendar min, Calendar max) {
+ mMinDate.setTimeInMillis(min.getTimeInMillis());
+ mMaxDate.setTimeInMillis(max.getTimeInMillis());
+
+ // Positions are now invalid, clear everything and start over.
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Sets the first day of the week.
+ *
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ */
+ public void setFirstDayOfWeek(int weekStart) {
+ mFirstDayOfWeek = weekStart;
+
+ // Update displayed views.
+ final int count = mItems.size();
+ for (int i = 0; i < count; i++) {
+ final SimpleMonthView monthView = mItems.valueAt(i);
+ monthView.setFirstDayOfWeek(weekStart);
+ }
+ }
+
+ public int getFirstDayOfWeek() {
+ return mFirstDayOfWeek;
+ }
+
+ /**
+ * Sets the selected day.
+ *
+ * @param day the selected day
+ */
+ public void setSelectedDay(Calendar day) {
+ final int oldPosition = getPositionForDay(mSelectedDay);
+ final int newPosition = getPositionForDay(day);
+
+ // Clear the old position if necessary.
+ if (oldPosition != newPosition) {
+ final SimpleMonthView oldMonthView = mItems.get(oldPosition, null);
+ if (oldMonthView != null) {
+ oldMonthView.setSelectedDay(-1);
+ }
+ }
+
+ // Set the new position.
+ final SimpleMonthView newMonthView = mItems.get(newPosition, null);
+ if (newMonthView != null) {
+ final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+ newMonthView.setSelectedDay(dayOfMonth);
+ }
+
+ mSelectedDay = day;
+ }
+
+ /**
+ * Sets the listener to call when the user selects a day.
+ *
+ * @param listener The listener to call.
+ */
+ public void setOnDaySelectedListener(OnDaySelectedListener listener) {
+ mOnDaySelectedListener = listener;
+ }
+
+ void setCalendarTextColor(ColorStateList calendarTextColor) {
+ mCalendarTextColor = calendarTextColor;
+ }
+
+ void setDaySelectorColor(ColorStateList selectorColor) {
+ mDaySelectorColor = selectorColor;
+ }
+
+ void setMonthTextAppearance(int resId) {
+ mMonthTextAppearance = resId;
+ }
+
+ void setDayOfWeekTextAppearance(int resId) {
+ mDayOfWeekTextAppearance = resId;
+ }
+
+ int getDayOfWeekTextAppearance() {
+ return mDayOfWeekTextAppearance;
+ }
+
+ void setDayTextAppearance(int resId) {
+ mDayTextAppearance = resId;
+ }
+
+ int getDayTextAppearance() {
+ return mDayTextAppearance;
+ }
+
+ @Override
+ public int getCount() {
+ final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
+ final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
+ return diffMonth + MONTHS_IN_YEAR * diffYear + 1;
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+
+ private int getMonthForPosition(int position) {
+ return position % MONTHS_IN_YEAR + mMinDate.get(Calendar.MONTH);
+ }
+
+ private int getYearForPosition(int position) {
+ return position / MONTHS_IN_YEAR + mMinDate.get(Calendar.YEAR);
+ }
+
+ private int getPositionForDay(Calendar day) {
+ final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
+ final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH));
+ return yearOffset * MONTHS_IN_YEAR + monthOffset;
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ final SimpleMonthView v = new SimpleMonthView(container.getContext());
+ v.setOnDayClickListener(mOnDayClickListener);
+ v.setMonthTextAppearance(mMonthTextAppearance);
+ v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance);
+ v.setDayTextAppearance(mDayTextAppearance);
+
+ if (mDaySelectorColor != null) {
+ v.setDaySelectorColor(mDaySelectorColor);
+ }
+
+ if (mDayHighlightColor != null) {
+ v.setDayHighlightColor(mDayHighlightColor);
+ }
+
+ if (mCalendarTextColor != null) {
+ v.setMonthTextColor(mCalendarTextColor);
+ v.setDayOfWeekTextColor(mCalendarTextColor);
+ v.setDayTextColor(mCalendarTextColor);
+ }
+
+ final int month = getMonthForPosition(position);
+ final int year = getYearForPosition(position);
+
+ final int selectedDay;
+ if (mSelectedDay.get(Calendar.MONTH) == month) {
+ selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
+ } else {
+ selectedDay = -1;
+ }
+
+ final int enabledDayRangeStart;
+ if (mMinDate.get(Calendar.MONTH) == month && mMinDate.get(Calendar.YEAR) == year) {
+ enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH);
+ } else {
+ enabledDayRangeStart = 1;
+ }
+
+ final int enabledDayRangeEnd;
+ if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) {
+ enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH);
+ } else {
+ enabledDayRangeEnd = 31;
+ }
+
+ v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
+ enabledDayRangeStart, enabledDayRangeEnd);
+
+ mItems.put(position, v);
+
+ container.addView(v);
+
+ return v;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ container.removeView(mItems.get(position));
+
+ mItems.remove(position);
+ }
+
+ @Override
+ public int getItemPosition(Object object) {
+ final int index = mItems.indexOfValue((SimpleMonthView) object);
+ if (index < 0) {
+ return mItems.keyAt(index);
+ }
+ return -1;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ final SimpleMonthView v = mItems.get(position);
+ if (v != null) {
+ return v.getTitle();
+ }
+ return null;
+ }
+
+ private boolean isCalendarInRange(Calendar value) {
+ return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0;
+ }
+
+ private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
+ @Override
+ public void onDayClick(SimpleMonthView view, Calendar day) {
+ if (day != null && isCalendarInRange(day)) {
+ setSelectedDay(day);
+
+ if (mOnDaySelectedListener != null) {
+ mOnDaySelectedListener.onDaySelected(DayPickerAdapter.this, day);
+ }
+ }
+ }
+ };
+
+ public interface OnDaySelectedListener {
+ public void onDaySelected(DayPickerAdapter view, Calendar day);
+ }
+}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 65af45d..a7ae926 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -16,87 +16,177 @@
package android.widget;
+import com.android.internal.widget.ViewPager;
+import com.android.internal.R;
+
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
import android.util.MathUtils;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
+import libcore.icu.LocaleData;
+
/**
* This displays a list of months in a calendar format with selectable days.
*/
-class DayPickerView extends ListView implements AbsListView.OnScrollListener {
- private static final String TAG = "DayPickerView";
+class DayPickerView extends ViewPager {
+ private static final int DEFAULT_START_YEAR = 1900;
+ private static final int DEFAULT_END_YEAR = 2100;
- // How long the GoTo fling animation should last
- private static final int GOTO_SCROLL_DURATION = 250;
+ private final Calendar mSelectedDay = Calendar.getInstance();
+ private final Calendar mMinDate = Calendar.getInstance();
+ private final Calendar mMaxDate = Calendar.getInstance();
- // How long to wait after receiving an onScrollStateChanged notification before acting on it
- private static final int SCROLL_CHANGE_DELAY = 40;
+ private final DayPickerAdapter mAdapter;
- // so that the top line will be under the separator
- private static final int LIST_TOP_OFFSET = -1;
+ /** Temporary calendar used for date calculations. */
+ private Calendar mTempCalendar;
- private final SimpleMonthAdapter mAdapter = new SimpleMonthAdapter(getContext());
+ private OnDaySelectedListener mOnDaySelectedListener;
- private final ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this);
+ public DayPickerView(Context context) {
+ this(context, null);
+ }
- private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
+ public DayPickerView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.calendarViewStyle);
+ }
- // highlighted time
- private Calendar mSelectedDay = Calendar.getInstance();
- private Calendar mTempDay = Calendar.getInstance();
- private Calendar mMinDate = Calendar.getInstance();
- private Calendar mMaxDate = Calendar.getInstance();
+ public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
- private Calendar mTempCalendar;
+ public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
- private OnDaySelectedListener mOnDaySelectedListener;
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.CalendarView, defStyleAttr, defStyleRes);
- // which month should be displayed/highlighted [0-11]
- private int mCurrentMonthDisplayed;
- // used for tracking what state listview is in
- private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
- // used for tracking what state listview is in
- private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+ final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
+ LocaleData.get(Locale.getDefault()).firstDayOfWeek);
- private boolean mPerformingScroll;
+ final String minDate = a.getString(R.styleable.CalendarView_minDate);
+ final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
- public DayPickerView(Context context) {
- super(context);
+ final int monthTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_monthTextAppearance,
+ R.style.TextAppearance_Material_Widget_Calendar_Month);
+ final int dayOfWeekTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_weekDayTextAppearance,
+ R.style.TextAppearance_Material_Widget_Calendar_DayOfWeek);
+ final int dayTextAppearanceResId = a.getResourceId(
+ R.styleable.CalendarView_dateTextAppearance,
+ R.style.TextAppearance_Material_Widget_Calendar_Day);
+
+ final ColorStateList daySelectorColor = a.getColorStateList(
+ R.styleable.CalendarView_daySelectorColor);
+
+ a.recycle();
+
+ // Set up adapter.
+ mAdapter = new DayPickerAdapter(context);
+ mAdapter.setMonthTextAppearance(monthTextAppearanceResId);
+ mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId);
+ mAdapter.setDayTextAppearance(dayTextAppearanceResId);
+ mAdapter.setDaySelectorColor(daySelectorColor);
setAdapter(mAdapter);
- setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- setDrawSelectorOnTop(false);
- setUpListView();
- goTo(mSelectedDay.getTimeInMillis(), false, false, true);
+ // Set up min and max dates.
+ final Calendar tempDate = Calendar.getInstance();
+ if (!CalendarView.parseDate(minDate, tempDate)) {
+ tempDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1);
+ }
+ final long minDateMillis = tempDate.getTimeInMillis();
+
+ if (!CalendarView.parseDate(maxDate, tempDate)) {
+ tempDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31);
+ }
+ final long maxDateMillis = tempDate.getTimeInMillis();
+
+ if (maxDateMillis < minDateMillis) {
+ throw new IllegalArgumentException("maxDate must be >= minDate");
+ }
+
+ final long setDateMillis = MathUtils.constrain(
+ System.currentTimeMillis(), minDateMillis, maxDateMillis);
- mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener);
+ setFirstDayOfWeek(firstDayOfWeek);
+ setMinDate(minDateMillis);
+ setMaxDate(maxDateMillis);
+ setDate(setDateMillis, false);
+
+ // Proxy selection callbacks to our own listener.
+ mAdapter.setOnDaySelectedListener(new DayPickerAdapter.OnDaySelectedListener() {
+ @Override
+ public void onDaySelected(DayPickerAdapter adapter, Calendar day) {
+ if (mOnDaySelectedListener != null) {
+ mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
+ }
+ }
+ });
+ }
+
+ public void setDayOfWeekTextAppearance(int resId) {
+ mAdapter.setDayOfWeekTextAppearance(resId);
+ }
+
+ public int getDayOfWeekTextAppearance() {
+ return mAdapter.getDayOfWeekTextAppearance();
+ }
+
+ public void setDayTextAppearance(int resId) {
+ mAdapter.setDayTextAppearance(resId);
+ }
+
+ public int getDayTextAppearance() {
+ return mAdapter.getDayTextAppearance();
}
/**
* Sets the currently selected date to the specified timestamp. Jumps
* immediately to the new date. To animate to the new date, use
- * {@link #setDate(long, boolean, boolean)}.
+ * {@link #setDate(long, boolean)}.
*
- * @param timeInMillis
+ * @param timeInMillis the target day in milliseconds
*/
public void setDate(long timeInMillis) {
- setDate(timeInMillis, false, true);
+ setDate(timeInMillis, false);
}
- public void setDate(long timeInMillis, boolean animate, boolean forceScroll) {
- goTo(timeInMillis, animate, true, forceScroll);
+ /**
+ * Sets the currently selected date to the specified timestamp. Jumps
+ * immediately to the new date, optionally animating the transition.
+ *
+ * @param timeInMillis the target day in milliseconds
+ * @param animate whether to smooth scroll to the new position
+ */
+ public void setDate(long timeInMillis, boolean animate) {
+ setDate(timeInMillis, animate, true);
+ }
+
+ /**
+ * Moves to the month containing the specified day, optionally setting the
+ * day as selected.
+ *
+ * @param timeInMillis the target day in milliseconds
+ * @param animate whether to smooth scroll to the new position
+ * @param setSelected whether to set the specified day as selected
+ */
+ private void setDate(long timeInMillis, boolean animate, boolean setSelected) {
+ // Set the selected day
+ if (setSelected) {
+ mSelectedDay.setTimeInMillis(timeInMillis);
+ }
+
+ final int position = getPositionFromDay(timeInMillis);
+ if (position != getCurrentItem()) {
+ setCurrentItem(position, animate);
+ }
}
public long getDate() {
@@ -137,7 +227,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
// Changing the min/max date changes the selection position since we
// don't really have stable IDs. Jumps immediately to the new position.
- goTo(mSelectedDay.getTimeInMillis(), false, false, true);
+ setDate(mSelectedDay.getTimeInMillis(), false, false);
}
/**
@@ -149,30 +239,9 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
mOnDaySelectedListener = listener;
}
- /*
- * Sets all the required fields for the list view. Override this method to
- * set a different list view behavior.
- */
- private void setUpListView() {
- // Transparent background on scroll
- setCacheColorHint(0);
- // No dividers
- setDivider(null);
- // Items are clickable
- setItemsCanFocus(true);
- // The thumb gets in the way, so disable it
- setFastScrollEnabled(false);
- setVerticalScrollBarEnabled(false);
- setOnScrollListener(this);
- setFadingEdgeLength(0);
- // Make the scrolling behavior nicer
- setFriction(ViewConfiguration.getScrollFriction());
- }
-
private int getDiffMonths(Calendar start, Calendar end) {
final int diffYears = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);
- final int diffMonths = end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
- return diffMonths;
+ return end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
}
private int getPositionFromDay(long timeInMillis) {
@@ -190,366 +259,13 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener {
}
/**
- * This moves to the specified time in the view. If the time is not already
- * in range it will move the list so that the first of the month containing
- * the time is at the top of the view. If the new time is already in view
- * the list will not be scrolled unless forceScroll is true. This time may
- * optionally be highlighted as selected as well.
- *
- * @param day The day to move to
- * @param animate Whether to scroll to the given time or just redraw at the
- * new location
- * @param setSelected Whether to set the given time as selected
- * @param forceScroll Whether to recenter even if the time is already
- * visible
- * @return Whether or not the view animated to the new location
- */
- private boolean goTo(long day, boolean animate, boolean setSelected, boolean forceScroll) {
-
- // Set the selected day
- if (setSelected) {
- mSelectedDay.setTimeInMillis(day);
- }
-
- mTempDay.setTimeInMillis(day);
- final int position = getPositionFromDay(day);
-
- View child;
- int i = 0;
- int top = 0;
- // Find a child that's completely in the view
- do {
- child = getChildAt(i++);
- if (child == null) {
- break;
- }
- top = child.getTop();
- } while (top < 0);
-
- // Compute the first and last position visible
- int selectedPosition;
- if (child != null) {
- selectedPosition = getPositionForView(child);
- } else {
- selectedPosition = 0;
- }
-
- if (setSelected) {
- mAdapter.setSelectedDay(mSelectedDay);
- }
-
- // Check if the selected day is now outside of our visible range
- // and if so scroll to the month that contains it
- if (position != selectedPosition || forceScroll) {
- setMonthDisplayed(mTempDay);
- mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
- if (animate) {
- smoothScrollToPositionFromTop(
- position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION);
- return true;
- } else {
- postSetSelection(position);
- }
- } else if (setSelected) {
- setMonthDisplayed(mSelectedDay);
- }
- return false;
- }
-
- public void postSetSelection(final int position) {
- clearFocus();
- post(new Runnable() {
-
- @Override
- public void run() {
- setSelection(position);
- }
- });
- onScrollStateChanged(this, OnScrollListener.SCROLL_STATE_IDLE);
- }
-
- /**
- * Updates the title and selected month if the view has moved to a new
- * month.
- */
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- SimpleMonthView child = (SimpleMonthView) view.getChildAt(0);
- if (child == null) {
- return;
- }
-
- mPreviousScrollState = mCurrentScrollState;
- }
-
- /**
- * Sets the month displayed at the top of this view based on time. Override
- * to add custom events when the title is changed.
- */
- protected void setMonthDisplayed(Calendar date) {
- if (mCurrentMonthDisplayed != date.get(Calendar.MONTH)) {
- mCurrentMonthDisplayed = date.get(Calendar.MONTH);
- invalidateViews();
- }
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- // use a post to prevent re-entering onScrollStateChanged before it
- // exits
- mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
- }
-
- void setCalendarTextColor(ColorStateList colors) {
- mAdapter.setCalendarTextColor(colors);
- }
-
- void setCalendarDayBackgroundColor(ColorStateList dayBackgroundColor) {
- mAdapter.setCalendarDayBackgroundColor(dayBackgroundColor);
- }
-
- void setCalendarTextAppearance(int resId) {
- mAdapter.setCalendarTextAppearance(resId);
- }
-
- protected class ScrollStateRunnable implements Runnable {
- private int mNewState;
- private View mParent;
-
- ScrollStateRunnable(View view) {
- mParent = view;
- }
-
- /**
- * Sets up the runnable with a short delay in case the scroll state
- * immediately changes again.
- *
- * @param view The list view that changed state
- * @param scrollState The new state it changed to
- */
- public void doScrollStateChange(AbsListView view, int scrollState) {
- mParent.removeCallbacks(this);
- mNewState = scrollState;
- mParent.postDelayed(this, SCROLL_CHANGE_DELAY);
- }
-
- @Override
- public void run() {
- mCurrentScrollState = mNewState;
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG,
- "new scroll state: " + mNewState + " old state: " + mPreviousScrollState);
- }
- // Fix the position after a scroll or a fling ends
- if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
- && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE
- && mPreviousScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
- mPreviousScrollState = mNewState;
- int i = 0;
- View child = getChildAt(i);
- while (child != null && child.getBottom() <= 0) {
- child = getChildAt(++i);
- }
- if (child == null) {
- // The view is no longer visible, just return
- return;
- }
- int firstPosition = getFirstVisiblePosition();
- int lastPosition = getLastVisiblePosition();
- boolean scroll = firstPosition != 0 && lastPosition != getCount() - 1;
- final int top = child.getTop();
- final int bottom = child.getBottom();
- final int midpoint = getHeight() / 2;
- if (scroll && top < LIST_TOP_OFFSET) {
- if (bottom > midpoint) {
- smoothScrollBy(top, GOTO_SCROLL_DURATION);
- } else {
- smoothScrollBy(bottom, GOTO_SCROLL_DURATION);
- }
- }
- } else {
- mPreviousScrollState = mNewState;
- }
- }
- }
-
- /**
* Gets the position of the view that is most prominently displayed within the list view.
*/
public int getMostVisiblePosition() {
- final int firstPosition = getFirstVisiblePosition();
- final int height = getHeight();
-
- int maxDisplayedHeight = 0;
- int mostVisibleIndex = 0;
- int i=0;
- int bottom = 0;
- while (bottom < height) {
- View child = getChildAt(i);
- if (child == null) {
- break;
- }
- bottom = child.getBottom();
- int displayedHeight = Math.min(bottom, height) - Math.max(0, child.getTop());
- if (displayedHeight > maxDisplayedHeight) {
- mostVisibleIndex = i;
- maxDisplayedHeight = displayedHeight;
- }
- i++;
- }
- return firstPosition + mostVisibleIndex;
- }
-
- /**
- * Attempts to return the date that has accessibility focus.
- *
- * @return The date that has accessibility focus, or {@code null} if no date
- * has focus.
- */
- private Calendar findAccessibilityFocus() {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child instanceof SimpleMonthView) {
- final Calendar focus = ((SimpleMonthView) child).getAccessibilityFocus();
- if (focus != null) {
- return focus;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Attempts to restore accessibility focus to a given date. No-op if
- * {@code day} is {@code null}.
- *
- * @param day The date that should receive accessibility focus
- * @return {@code true} if focus was restored
- */
- private boolean restoreAccessibilityFocus(Calendar day) {
- if (day == null) {
- return false;
- }
-
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- if (child instanceof SimpleMonthView) {
- if (((SimpleMonthView) child).restoreAccessibilityFocus(day)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- @Override
- protected void layoutChildren() {
- final Calendar focusedDay = findAccessibilityFocus();
- super.layoutChildren();
- if (mPerformingScroll) {
- mPerformingScroll = false;
- } else {
- restoreAccessibilityFocus(focusedDay);
- }
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
- }
-
- /** @hide */
- @Override
- public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
- super.onInitializeAccessibilityEventInternal(event);
- event.setItemCount(-1);
- }
-
- private String getMonthAndYearString(Calendar day) {
- final StringBuilder sbuf = new StringBuilder();
- sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
- sbuf.append(" ");
- sbuf.append(mYearFormat.format(day.getTime()));
- return sbuf.toString();
- }
-
- /**
- * Necessary for accessibility, to ensure we support "scrolling" forward and backward
- * in the month list.
- *
- * @hide
- */
- @Override
- public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfoInternal(info);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
- }
-
- /**
- * When scroll forward/backward events are received, announce the newly scrolled-to month.
- *
- * @hide
- */
- @Override
- public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
- if (action != AccessibilityNodeInfo.ACTION_SCROLL_FORWARD &&
- action != AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
- return super.performAccessibilityActionInternal(action, arguments);
- }
-
- // Figure out what month is showing.
- final int firstVisiblePosition = getFirstVisiblePosition();
- final int month = firstVisiblePosition % 12;
- final int year = firstVisiblePosition / 12 + mMinDate.get(Calendar.YEAR);
- final Calendar day = Calendar.getInstance();
- day.set(year, month, 1);
-
- // Scroll either forward or backward one month.
- if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
- day.add(Calendar.MONTH, 1);
- if (day.get(Calendar.MONTH) == 12) {
- day.set(Calendar.MONTH, 0);
- day.add(Calendar.YEAR, 1);
- }
- } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
- View firstVisibleView = getChildAt(0);
- // If the view is fully visible, jump one month back. Otherwise, we'll just jump
- // to the first day of first visible month.
- if (firstVisibleView != null && firstVisibleView.getTop() >= -1) {
- // There's an off-by-one somewhere, so the top of the first visible item will
- // actually be -1 when it's at the exact top.
- day.add(Calendar.MONTH, -1);
- if (day.get(Calendar.MONTH) == -1) {
- day.set(Calendar.MONTH, 11);
- day.add(Calendar.YEAR, -1);
- }
- }
- }
-
- // Go to that month.
- announceForAccessibility(getMonthAndYearString(day));
- goTo(day.getTimeInMillis(), true, false, true);
- mPerformingScroll = true;
- return true;
+ return getCurrentItem();
}
public interface OnDaySelectedListener {
public void onDaySelected(DayPickerView view, Calendar day);
}
-
- private final SimpleMonthAdapter.OnDaySelectedListener
- mProxyOnDaySelectedListener = new SimpleMonthAdapter.OnDaySelectedListener() {
- @Override
- public void onDaySelected(SimpleMonthAdapter adapter, Calendar day) {
- if (mOnDaySelectedListener != null) {
- mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
- }
- }
- };
}
diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java
deleted file mode 100644
index c807d56..0000000
--- a/core/java/android/widget/SimpleMonthAdapter.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget;
-
-import com.android.internal.R;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.SimpleMonthView.OnDayClickListener;
-
-import java.util.Calendar;
-
-/**
- * An adapter for a list of {@link android.widget.SimpleMonthView} items.
- */
-class SimpleMonthAdapter extends BaseAdapter {
- private final Calendar mMinDate = Calendar.getInstance();
- private final Calendar mMaxDate = Calendar.getInstance();
-
- private final Context mContext;
-
- private Calendar mSelectedDay = Calendar.getInstance();
- private ColorStateList mCalendarTextColors = ColorStateList.valueOf(Color.BLACK);
- private ColorStateList mCalendarDayBackgroundColor = ColorStateList.valueOf(Color.MAGENTA);
- private OnDaySelectedListener mOnDaySelectedListener;
-
- private int mFirstDayOfWeek;
-
- public SimpleMonthAdapter(Context context) {
- mContext = context;
- }
-
- public void setRange(Calendar min, Calendar max) {
- mMinDate.setTimeInMillis(min.getTimeInMillis());
- mMaxDate.setTimeInMillis(max.getTimeInMillis());
-
- notifyDataSetInvalidated();
- }
-
- public void setFirstDayOfWeek(int firstDayOfWeek) {
- mFirstDayOfWeek = firstDayOfWeek;
-
- notifyDataSetInvalidated();
- }
-
- public int getFirstDayOfWeek() {
- return mFirstDayOfWeek;
- }
-
- /**
- * Updates the selected day and related parameters.
- *
- * @param day The day to highlight
- */
- public void setSelectedDay(Calendar day) {
- mSelectedDay = day;
-
- notifyDataSetChanged();
- }
-
- /**
- * Sets the listener to call when the user selects a day.
- *
- * @param listener The listener to call.
- */
- public void setOnDaySelectedListener(OnDaySelectedListener listener) {
- mOnDaySelectedListener = listener;
- }
-
- void setCalendarTextColor(ColorStateList colors) {
- mCalendarTextColors = colors;
- }
-
- void setCalendarDayBackgroundColor(ColorStateList dayBackgroundColor) {
- mCalendarDayBackgroundColor = dayBackgroundColor;
- }
-
- /**
- * Sets the text color, size, style, hint color, and highlight color from
- * the specified TextAppearance resource. This is mostly copied from
- * {@link TextView#setTextAppearance(Context, int)}.
- */
- void setCalendarTextAppearance(int resId) {
- final TypedArray a = mContext.obtainStyledAttributes(resId, R.styleable.TextAppearance);
-
- final ColorStateList textColor = a.getColorStateList(R.styleable.TextAppearance_textColor);
- if (textColor != null) {
- mCalendarTextColors = textColor;
- }
-
- // TODO: Support font size, etc.
-
- a.recycle();
- }
-
- @Override
- public int getCount() {
- final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
- final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
- return diffMonth + 12 * diffYear + 1;
- }
-
- @Override
- public Object getItem(int position) {
- return null;
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final SimpleMonthView v;
- if (convertView != null) {
- v = (SimpleMonthView) convertView;
- } else {
- v = new SimpleMonthView(mContext);
-
- // Set up the new view
- final AbsListView.LayoutParams params = new AbsListView.LayoutParams(
- AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.MATCH_PARENT);
- v.setLayoutParams(params);
- v.setClickable(true);
- v.setOnDayClickListener(mOnDayClickListener);
-
- v.setMonthTextColor(mCalendarTextColors);
- v.setDayOfWeekTextColor(mCalendarTextColors);
- v.setDayTextColor(mCalendarTextColors);
-
- v.setDayBackgroundColor(mCalendarDayBackgroundColor);
- }
-
- final int minMonth = mMinDate.get(Calendar.MONTH);
- final int minYear = mMinDate.get(Calendar.YEAR);
- final int currentMonth = position + minMonth;
- final int month = currentMonth % 12;
- final int year = currentMonth / 12 + minYear;
- final int selectedDay;
- if (isSelectedDayInMonth(year, month)) {
- selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
- } else {
- selectedDay = -1;
- }
-
- // Invokes requestLayout() to ensure that the recycled view is set with the appropriate
- // height/number of weeks before being displayed.
- v.reuse();
-
- final int enabledDayRangeStart;
- if (minMonth == month && minYear == year) {
- enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH);
- } else {
- enabledDayRangeStart = 1;
- }
-
- final int enabledDayRangeEnd;
- if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) {
- enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH);
- } else {
- enabledDayRangeEnd = 31;
- }
-
- v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
- enabledDayRangeStart, enabledDayRangeEnd);
- v.invalidate();
-
- return v;
- }
-
- private boolean isSelectedDayInMonth(int year, int month) {
- return mSelectedDay.get(Calendar.YEAR) == year && mSelectedDay.get(Calendar.MONTH) == month;
- }
-
- private boolean isCalendarInRange(Calendar value) {
- return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0;
- }
-
- private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
- @Override
- public void onDayClick(SimpleMonthView view, Calendar day) {
- if (day != null && isCalendarInRange(day)) {
- setSelectedDay(day);
-
- if (mOnDaySelectedListener != null) {
- mOnDaySelectedListener.onDaySelected(SimpleMonthAdapter.this, day);
- }
- }
- }
- };
-
- public interface OnDaySelectedListener {
- public void onDaySelected(SimpleMonthAdapter view, Calendar day);
- }
-}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 58ad515..4e5a39a 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -18,8 +18,8 @@ package android.widget;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
@@ -29,8 +29,6 @@ import android.graphics.Typeface;
import android.os.Bundle;
import android.text.TextPaint;
import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.format.Time;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.StateSet;
@@ -38,13 +36,13 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
import com.android.internal.widget.ExploreByTouchHelper;
import java.text.SimpleDateFormat;
import java.util.Calendar;
-import java.util.Formatter;
import java.util.Locale;
/**
@@ -52,93 +50,80 @@ import java.util.Locale;
* within the specified month.
*/
class SimpleMonthView extends View {
- private static final int MIN_ROW_HEIGHT = 10;
+ private static final int DAYS_IN_WEEK = 7;
+ private static final int MAX_WEEKS_IN_MONTH = 6;
private static final int DEFAULT_SELECTED_DAY = -1;
private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
- private static final int DEFAULT_NUM_DAYS = 7;
- private static final int DEFAULT_NUM_ROWS = 6;
- private static final int MAX_NUM_ROWS = 6;
- private final Formatter mFormatter;
- private final StringBuilder mStringBuilder;
-
- private final int mMonthTextSize;
- private final int mDayOfWeekTextSize;
- private final int mDayTextSize;
-
- /** Height of the header containing the month and day of week labels. */
- private final int mMonthHeaderHeight;
+ private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
+ private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
private final TextPaint mMonthPaint = new TextPaint();
private final TextPaint mDayOfWeekPaint = new TextPaint();
private final TextPaint mDayPaint = new TextPaint();
+ private final Paint mDaySelectorPaint = new Paint();
+ private final Paint mDayHighlightPaint = new Paint();
- private final Paint mDayBackgroundPaint = new Paint();
+ private final Calendar mCalendar = Calendar.getInstance();
+ private final Calendar mDayLabelCalendar = Calendar.getInstance();
- /** Single-letter (when available) formatter for the day of week label. */
- private SimpleDateFormat mDayFormatter = new SimpleDateFormat("EEEEE", Locale.getDefault());
+ private final MonthViewTouchHelper mTouchHelper;
- // affects the padding on the sides of this view
- private int mPadding = 0;
+ private final SimpleDateFormat mTitleFormatter;
+ private final SimpleDateFormat mDayOfWeekFormatter;
- private String mDayOfWeekTypeface;
- private String mMonthTypeface;
+ private CharSequence mTitle;
private int mMonth;
private int mYear;
- // Quick reference to the width of this view, matches parent
- private int mWidth;
-
- // The height this view should draw at in pixels, set by height param
- private final int mRowHeight;
+ private int mPaddedWidth;
+ private int mPaddedHeight;
- // If this view contains the today
- private boolean mHasToday = false;
+ private final int mMonthHeight;
+ private final int mDayOfWeekHeight;
+ private final int mDayHeight;
+ private final int mCellWidth;
+ private final int mDaySelectorRadius;
- // Which day is selected [0-6] or -1 if no day is selected
+ /** The day of month for the selected day, or -1 if no day is selected. */
private int mActivatedDay = -1;
- // Which day is today [0-6] or -1 if no day is today
+ /**
+ * The day of month for today, or -1 if the today is not in the current
+ * month.
+ */
private int mToday = DEFAULT_SELECTED_DAY;
- // Which day of the week to start on [0-6]
+ /** The first day of the week (ex. Calendar.SUNDAY). */
private int mWeekStart = DEFAULT_WEEK_START;
- // How many days to display
- private int mNumDays = DEFAULT_NUM_DAYS;
-
- // The number of days + a spot for week number if it is displayed
- private int mNumCells = mNumDays;
+ /** The number of days (ex. 28) in the current month. */
+ private int mDaysInMonth;
- private int mDayOfWeekStart = 0;
+ /**
+ * The day of week (ex. Calendar.SUNDAY) for the first day of the current
+ * month.
+ */
+ private int mDayOfWeekStart;
- // First enabled day
+ /** The day of month for the first (inclusive) enabled day. */
private int mEnabledDayStart = 1;
- // Last enabled day
+ /** The day of month for the last (inclusive) enabled day. */
private int mEnabledDayEnd = 31;
- private final Calendar mCalendar = Calendar.getInstance();
- private final Calendar mDayLabelCalendar = Calendar.getInstance();
-
- private final MonthViewTouchHelper mTouchHelper;
-
- private int mNumRows = DEFAULT_NUM_ROWS;
+ /** The number of week rows needed to display the current month. */
+ private int mNumWeeks = MAX_WEEKS_IN_MONTH;
- // Optional listener for handling day click actions
+ /** Optional listener for handling day click actions. */
private OnDayClickListener mOnDayClickListener;
- // Whether to prevent setting the accessibility delegate
- private boolean mLockAccessibilityDelegate;
-
- private int mNormalTextColor;
- private int mDisabledTextColor;
- private int mSelectedDayColor;
-
private ColorStateList mDayTextColor;
+ private int mTouchedDay = -1;
+
public SimpleMonthView(Context context) {
this(context, null);
}
@@ -155,64 +140,123 @@ class SimpleMonthView extends View {
super(context, attrs, defStyleAttr, defStyleRes);
final Resources res = context.getResources();
- mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface);
- mMonthTypeface = res.getString(R.string.sans_serif);
-
- mStringBuilder = new StringBuilder(50);
- mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
-
- mDayTextSize = res.getDimensionPixelSize(R.dimen.datepicker_day_number_size);
- mMonthTextSize = res.getDimensionPixelSize(R.dimen.datepicker_month_label_size);
- mDayOfWeekTextSize = res.getDimensionPixelSize(
- R.dimen.datepicker_month_day_label_text_size);
- mMonthHeaderHeight = res.getDimensionPixelOffset(
- R.dimen.datepicker_month_list_item_header_height);
-
- mRowHeight = Math.max(MIN_ROW_HEIGHT,
- (res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height)
- - mMonthHeaderHeight) / MAX_NUM_ROWS);
+ mMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height);
+ mDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height);
+ mDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height);
+ mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
+ mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
// Set up accessibility components.
mTouchHelper = new MonthViewTouchHelper(this);
setAccessibilityDelegate(mTouchHelper);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- mLockAccessibilityDelegate = true;
- initPaints();
+ final Locale locale = res.getConfiguration().locale;
+ final String titleFormat = DateFormat.getBestDateTimePattern(locale, DEFAULT_TITLE_FORMAT);
+ mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
+ mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
+
+ setClickable(true);
+ initPaints(res);
+ }
+
+ /**
+ * Applies the specified text appearance resource to a paint, returning the
+ * text color if one is set in the text appearance.
+ *
+ * @param p the paint to modify
+ * @param resId the resource ID of the text appearance
+ * @return the text color, if available
+ */
+ private ColorStateList applyTextAppearance(Paint p, int resId) {
+ final TypedArray ta = mContext.obtainStyledAttributes(null,
+ R.styleable.TextAppearance, 0, resId);
+
+ final String fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily);
+ if (fontFamily != null) {
+ p.setTypeface(Typeface.create(fontFamily, 0));
+ }
+
+ p.setTextSize(ta.getDimensionPixelSize(
+ R.styleable.TextAppearance_textSize, (int) p.getTextSize()));
+
+ final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor);
+ if (textColor != null) {
+ final int enabledColor = textColor.getColorForState(ENABLED_STATE_SET, 0);
+ p.setColor(enabledColor);
+ }
+
+ ta.recycle();
+
+ return textColor;
+ }
+
+ public void setMonthTextAppearance(int resId) {
+ applyTextAppearance(mMonthPaint, resId);
+ invalidate();
+ }
+
+ public void setDayOfWeekTextAppearance(int resId) {
+ applyTextAppearance(mDayOfWeekPaint, resId);
+ invalidate();
+ }
+
+ public void setDayTextAppearance(int resId) {
+ final ColorStateList textColor = applyTextAppearance(mDayPaint, resId);
+ if (textColor != null) {
+ mDayTextColor = textColor;
+ }
+
+ invalidate();
+ }
+
+ public CharSequence getTitle() {
+ if (mTitle == null) {
+ mTitle = mTitleFormatter.format(mCalendar.getTime());
+ }
+ return mTitle;
}
/**
* Sets up the text and style properties for painting.
*/
- private void initPaints() {
+ private void initPaints(Resources res) {
+ final String monthTypeface = res.getString(R.string.date_picker_month_typeface);
+ final String dayOfWeekTypeface = res.getString(R.string.date_picker_day_of_week_typeface);
+ final String dayTypeface = res.getString(R.string.date_picker_day_typeface);
+
+ final int monthTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_month_text_size);
+ final int dayOfWeekTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_day_of_week_text_size);
+ final int dayTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_day_text_size);
+
mMonthPaint.setAntiAlias(true);
- mMonthPaint.setTextSize(mMonthTextSize);
- mMonthPaint.setTypeface(Typeface.create(mMonthTypeface, Typeface.BOLD));
+ mMonthPaint.setTextSize(monthTextSize);
+ mMonthPaint.setTypeface(Typeface.create(monthTypeface, 0));
mMonthPaint.setTextAlign(Align.CENTER);
mMonthPaint.setStyle(Style.FILL);
mDayOfWeekPaint.setAntiAlias(true);
- mDayOfWeekPaint.setTextSize(mDayOfWeekTextSize);
- mDayOfWeekPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.BOLD));
+ mDayOfWeekPaint.setTextSize(dayOfWeekTextSize);
+ mDayOfWeekPaint.setTypeface(Typeface.create(dayOfWeekTypeface, 0));
mDayOfWeekPaint.setTextAlign(Align.CENTER);
mDayOfWeekPaint.setStyle(Style.FILL);
- mDayBackgroundPaint.setAntiAlias(true);
- mDayBackgroundPaint.setStyle(Style.FILL);
+ mDaySelectorPaint.setAntiAlias(true);
+ mDaySelectorPaint.setStyle(Style.FILL);
+
+ mDayHighlightPaint.setAntiAlias(true);
+ mDayHighlightPaint.setStyle(Style.FILL);
mDayPaint.setAntiAlias(true);
- mDayPaint.setTextSize(mDayTextSize);
+ mDayPaint.setTextSize(dayTextSize);
+ mDayPaint.setTypeface(Typeface.create(dayTypeface, 0));
mDayPaint.setTextAlign(Align.CENTER);
mDayPaint.setStyle(Style.FILL);
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- mDayFormatter = new SimpleDateFormat("EEEEE", newConfig.locale);
- }
-
void setMonthTextColor(ColorStateList monthTextColor) {
final int enabledColor = monthTextColor.getColorForState(ENABLED_STATE_SET, 0);
mMonthPaint.setColor(enabledColor);
@@ -230,20 +274,18 @@ class SimpleMonthView extends View {
invalidate();
}
- void setDayBackgroundColor(ColorStateList dayBackgroundColor) {
+ void setDaySelectorColor(ColorStateList dayBackgroundColor) {
final int activatedColor = dayBackgroundColor.getColorForState(
StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
- mDayBackgroundPaint.setColor(activatedColor);
+ mDaySelectorPaint.setColor(activatedColor);
invalidate();
}
- @Override
- public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
- // Workaround for a JB MR1 issue where accessibility delegates on
- // top-level ListView items are overwritten.
- if (!mLockAccessibilityDelegate) {
- super.setAccessibilityDelegate(delegate);
- }
+ void setDayHighlightColor(ColorStateList dayHighlightColor) {
+ final int pressedColor = dayHighlightColor.getColorForState(
+ StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED), 0);
+ mDayHighlightPaint.setColor(pressedColor);
+ invalidate();
}
public void setOnDayClickListener(OnDayClickListener listener) {
@@ -253,30 +295,124 @@ class SimpleMonthView extends View {
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
// First right-of-refusal goes the touch exploration helper.
- if (mTouchHelper.dispatchHoverEvent(event)) {
- return true;
- }
- return super.dispatchHoverEvent(event);
+ return mTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
- case MotionEvent.ACTION_UP:
- final int day = getDayFromLocation(event.getX(), event.getY());
- if (day >= 0) {
- onDayClick(day);
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ final int touchedDay = getDayAtLocation(event.getX(), event.getY());
+ if (mTouchedDay != touchedDay) {
+ mTouchedDay = touchedDay;
+ invalidate();
}
break;
+
+ case MotionEvent.ACTION_UP:
+ final int clickedDay = getDayAtLocation(event.getX(), event.getY());
+ onDayClicked(clickedDay);
+ // Fall through.
+ case MotionEvent.ACTION_CANCEL:
+ // Reset touched day on stream end.
+ mTouchedDay = -1;
+ invalidate();
+ break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
- drawMonthTitle(canvas);
- drawWeekDayLabels(canvas);
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = getPaddingTop();
+ canvas.translate(paddingLeft, paddingTop);
+
+ drawMonth(canvas);
+ drawDaysOfWeek(canvas);
drawDays(canvas);
+
+ canvas.translate(-paddingLeft, -paddingTop);
+ }
+
+ private void drawMonth(Canvas canvas) {
+ final float x = mPaddedWidth / 2f;
+
+ // Vertically centered within the month header height.
+ final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
+ final float y = (mMonthHeight - lineHeight) / 2f;
+
+ canvas.drawText(getTitle().toString(), x, y, mMonthPaint);
+ }
+
+ private void drawDaysOfWeek(Canvas canvas) {
+ final float cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+ // Vertically centered within the cell height.
+ final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
+ final float y = mMonthHeight + (mDayOfWeekHeight - lineHeight) / 2f;
+
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ final int calendarDay = (i + mWeekStart) % DAYS_IN_WEEK;
+ mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
+
+ final String dayLabel = mDayOfWeekFormatter.format(mDayLabelCalendar.getTime());
+ final float x = (2 * i + 1) * cellWidthHalf;
+ canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
+ }
+ }
+
+ /**
+ * Draws the month days.
+ */
+ private void drawDays(Canvas canvas) {
+ final int cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+ // Vertically centered within the cell height.
+ final float halfLineHeight = (mDayPaint.ascent() + mDayPaint.descent()) / 2;
+ float centerY = mMonthHeight + mDayOfWeekHeight + mDayHeight / 2f;
+
+ for (int day = 1, j = findDayOffset(); day <= mDaysInMonth; day++) {
+ final int x = (2 * j + 1) * cellWidthHalf;
+ int stateMask = 0;
+
+ if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
+ stateMask |= StateSet.VIEW_STATE_ENABLED;
+ }
+
+ final boolean isDayActivated = mActivatedDay == day;
+ if (isDayActivated) {
+ stateMask |= StateSet.VIEW_STATE_ACTIVATED;
+
+ // Adjust the circle to be centered on the row.
+ canvas.drawCircle(x, centerY, mDaySelectorRadius, mDaySelectorPaint);
+ } else if (mTouchedDay == day) {
+ stateMask |= StateSet.VIEW_STATE_PRESSED;
+
+ // Adjust the circle to be centered on the row.
+ canvas.drawCircle(x, centerY, mDaySelectorRadius, mDayHighlightPaint);
+ }
+
+ final boolean isDayToday = mToday == day;
+ final int dayTextColor;
+ if (isDayToday && !isDayActivated) {
+ dayTextColor = mDaySelectorPaint.getColor();
+ } else {
+ final int[] stateSet = StateSet.get(stateMask);
+ dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
+ }
+ mDayPaint.setColor(dayTextColor);
+
+ canvas.drawText("" + day, x, centerY - halfLineHeight, mDayPaint);
+
+ j++;
+
+ if (j == DAYS_IN_WEEK) {
+ j = 0;
+ centerY += mDayHeight;
+ }
+ }
}
private static boolean isValidDayOfWeek(int day) {
@@ -288,18 +424,52 @@ class SimpleMonthView extends View {
}
/**
- * Sets all the parameters for displaying this week. Parameters have a default value and
- * will only update if a new value is included, except for focus month, which will always
- * default to no focus month if no value is passed in. The only required parameter is the
- * week start.
+ * Sets the selected day.
*
- * @param selectedDay the selected day of the month, or -1 for no selection.
- * @param month the month.
- * @param year the year.
- * @param weekStart which day the week should start on. {@link Calendar#SUNDAY} through
- * {@link Calendar#SATURDAY}.
- * @param enabledDayStart the first enabled day.
- * @param enabledDayEnd the last enabled day.
+ * @param dayOfMonth the selected day of the month, or {@code -1} to clear
+ * the selection
+ */
+ public void setSelectedDay(int dayOfMonth) {
+ mActivatedDay = dayOfMonth;
+
+ // Invalidate cached accessibility information.
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
+ /**
+ * Sets the first day of the week.
+ *
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ */
+ public void setFirstDayOfWeek(int weekStart) {
+ if (isValidDayOfWeek(weekStart)) {
+ mWeekStart = weekStart;
+ } else {
+ mWeekStart = mCalendar.getFirstDayOfWeek();
+ }
+
+ // Invalidate cached accessibility information.
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
+ /**
+ * Sets all the parameters for displaying this week.
+ * <p>
+ * Parameters have a default value and will only update if a new value is
+ * included, except for focus month, which will always default to no focus
+ * month if no value is passed in. The only required parameter is the week
+ * start.
+ *
+ * @param selectedDay the selected day of the month, or -1 for no selection
+ * @param month the month
+ * @param year the year
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ * @param enabledDayStart the first enabled day
+ * @param enabledDayEnd the last enabled day
*/
void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart,
int enabledDayEnd) {
@@ -310,12 +480,6 @@ class SimpleMonthView extends View {
}
mYear = year;
- // Figure out what day today is
- final Time today = new Time(Time.getCurrentTimezone());
- today.setToNow();
- mHasToday = false;
- mToday = -1;
-
mCalendar.set(Calendar.MONTH, mMonth);
mCalendar.set(Calendar.YEAR, mYear);
mCalendar.set(Calendar.DAY_OF_MONTH, 1);
@@ -334,15 +498,20 @@ class SimpleMonthView extends View {
mEnabledDayEnd = enabledDayEnd;
}
- mNumCells = getDaysInMonth(mMonth, mYear);
- for (int i = 0; i < mNumCells; i++) {
+ // Figure out what day today is.
+ final Calendar today = Calendar.getInstance();
+ mToday = -1;
+ mDaysInMonth = getDaysInMonth(mMonth, mYear);
+ for (int i = 0; i < mDaysInMonth; i++) {
final int day = i + 1;
if (sameDay(day, today)) {
- mHasToday = true;
mToday = day;
}
}
- mNumRows = calculateNumRows();
+ mNumWeeks = calculateNumRows();
+
+ // Invalidate the old title.
+ mTitle = null;
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
@@ -371,154 +540,118 @@ class SimpleMonthView extends View {
}
public void reuse() {
- mNumRows = DEFAULT_NUM_ROWS;
+ mNumWeeks = MAX_WEEKS_IN_MONTH;
requestLayout();
}
private int calculateNumRows() {
- int offset = findDayOffset();
- int dividend = (offset + mNumCells) / mNumDays;
- int remainder = (offset + mNumCells) % mNumDays;
- return (dividend + (remainder > 0 ? 1 : 0));
+ final int offset = findDayOffset();
+ final int dividend = (offset + mDaysInMonth) / DAYS_IN_WEEK;
+ final int remainder = (offset + mDaysInMonth) % DAYS_IN_WEEK;
+ return dividend + (remainder > 0 ? 1 : 0);
}
- private boolean sameDay(int day, Time today) {
- return mYear == today.year &&
- mMonth == today.month &&
- day == today.monthDay;
+ private boolean sameDay(int day, Calendar today) {
+ return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH)
+ && day == today.get(Calendar.DAY_OF_MONTH);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows
- + mMonthHeaderHeight);
+ final int preferredHeight = mDayHeight * mNumWeeks + mDayOfWeekHeight + mMonthHeight
+ + getPaddingTop() + getPaddingBottom();
+ final int preferredWidth = mCellWidth * DAYS_IN_WEEK
+ + getPaddingStart() + getPaddingEnd();
+ final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec);
+ final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec);
+ setMeasuredDimension(resolvedWidth, resolvedHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mWidth = w;
+ mPaddedWidth = w - getPaddingLeft() - getPaddingRight();
+ mPaddedHeight = w - getPaddingTop() - getPaddingBottom();
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
}
- private String getMonthAndYearString() {
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
- | DateUtils.FORMAT_NO_MONTH_DAY;
- mStringBuilder.setLength(0);
- long millis = mCalendar.getTimeInMillis();
- return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags,
- Time.getCurrentTimezone()).toString();
- }
-
- private void drawMonthTitle(Canvas canvas) {
- final float x = (mWidth + 2 * mPadding) / 2f;
-
- // Centered on the upper half of the month header.
- final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
- final float y = mMonthHeaderHeight * 0.25f - lineHeight / 2f;
-
- canvas.drawText(getMonthAndYearString(), x, y, mMonthPaint);
- }
-
- private void drawWeekDayLabels(Canvas canvas) {
- final float dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
- // Centered on the lower half of the month header.
- final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
- final float y = mMonthHeaderHeight * 0.75f - lineHeight / 2f;
-
- for (int i = 0; i < mNumDays; i++) {
- final int calendarDay = (i + mWeekStart) % mNumDays;
- mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
-
- final String dayLabel = mDayFormatter.format(mDayLabelCalendar.getTime());
- final float x = (2 * i + 1) * dayWidthHalf + mPadding;
- canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
+ private int findDayOffset() {
+ final int offset = mDayOfWeekStart - mWeekStart;
+ if (mDayOfWeekStart < mWeekStart) {
+ return offset + DAYS_IN_WEEK;
}
+ return offset;
}
/**
- * Draws the month days.
+ * Calculates the day of the month at the specified touch position. Returns
+ * the day of the month or -1 if the position wasn't in a valid day.
+ *
+ * @param x the x position of the touch event
+ * @param y the y position of the touch event
+ * @return the day of the month at (x, y) or -1 if the position wasn't in a
+ * valid day
*/
- private void drawDays(Canvas canvas) {
- final int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
- // Centered within the row.
- final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
- float y = mMonthHeaderHeight + (mRowHeight - lineHeight) / 2f;
-
- for (int day = 1, j = findDayOffset(); day <= mNumCells; day++) {
- final int x = (2 * j + 1) * dayWidthHalf + mPadding;
- int stateMask = 0;
-
- if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
- stateMask |= StateSet.VIEW_STATE_ENABLED;
- }
-
- if (mActivatedDay == day) {
- stateMask |= StateSet.VIEW_STATE_ACTIVATED;
-
- // Adjust the circle to be centered the row.
- final float rowCenterY = y + lineHeight / 2;
- canvas.drawCircle(x, rowCenterY, mRowHeight / 2,
- mDayBackgroundPaint);
- }
-
- final int[] stateSet = StateSet.get(stateMask);
- final int dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
- mDayPaint.setColor(dayTextColor);
-
- final boolean isDayToday = mHasToday && mToday == day;
- mDayPaint.setFakeBoldText(isDayToday);
-
- canvas.drawText(String.format("%d", day), x, y, mDayPaint);
+ private int getDayAtLocation(float x, float y) {
+ final int paddedX = (int) (x - getPaddingLeft() + 0.5f);
+ if (paddedX < 0 || paddedX >= mPaddedWidth) {
+ return -1;
+ }
- j++;
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int paddedY = (int) (y - getPaddingTop() + 0.5f);
+ if (paddedY < headerHeight || paddedY >= mPaddedHeight) {
+ return -1;
+ }
- if (j == mNumDays) {
- j = 0;
- y += mRowHeight;
- }
+ final int row = (paddedY - headerHeight) / mDayHeight;
+ final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth;
+ final int index = col + row * DAYS_IN_WEEK;
+ final int day = index + 1 - findDayOffset();
+ if (day < 1 || day > mDaysInMonth) {
+ return -1;
}
- }
- private int findDayOffset() {
- return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
- - mWeekStart;
+ return day;
}
/**
- * Calculates the day that the given x position is in, accounting for week
- * number. Returns the day or -1 if the position wasn't in a day.
+ * Calculates the bounds of the specified day.
*
- * @param x The x position of the touch event
- * @return The day number, or -1 if the position wasn't in a day
+ * @param day the day of the month
+ * @param outBounds the rect to populate with bounds
*/
- private int getDayFromLocation(float x, float y) {
- int dayStart = mPadding;
- if (x < dayStart || x > mWidth - mPadding) {
- return -1;
+ private boolean getBoundsForDay(int day, Rect outBounds) {
+ if (day < 1 || day > mDaysInMonth) {
+ return false;
}
- // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
- int row = (int) (y - mMonthHeaderHeight) / mRowHeight;
- int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding));
- int day = column - findDayOffset() + 1;
- day += row * mNumDays;
- if (day < 1 || day > mNumCells) {
- return -1;
- }
- return day;
+ final int index = day - 1 + findDayOffset();
+ final int row = index / DAYS_IN_WEEK;
+ final int col = index % DAYS_IN_WEEK;
+
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int paddedY = row * mDayHeight + headerHeight;
+ final int paddedX = col * mPaddedWidth;
+
+ final int y = paddedY + getPaddingTop();
+ final int x = paddedX + getPaddingLeft();
+
+ final int cellHeight = mDayHeight;
+ final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
+ outBounds.set(x, y, (x + cellWidth), (y + cellHeight));
+
+ return true;
}
/**
* Called when the user clicks on a day. Handles callbacks to the
* {@link OnDayClickListener} if one is set.
*
- * @param day The day that was clicked
+ * @param day the day that was clicked
*/
- private void onDayClick(int day) {
+ private void onDayClicked(int day) {
if (mOnDayClickListener != null) {
Calendar date = Calendar.getInstance();
date.set(mYear, mMonth, day);
@@ -530,44 +663,6 @@ class SimpleMonthView extends View {
}
/**
- * @return The date that has accessibility focus, or {@code null} if no date
- * has focus
- */
- Calendar getAccessibilityFocus() {
- final int day = mTouchHelper.getFocusedVirtualView();
- Calendar date = null;
- if (day >= 0) {
- date = Calendar.getInstance();
- date.set(mYear, mMonth, day);
- }
- return date;
- }
-
- /**
- * Clears accessibility focus within the view. No-op if the view does not
- * contain accessibility focus.
- */
- public void clearAccessibilityFocus() {
- mTouchHelper.clearFocusedVirtualView();
- }
-
- /**
- * Attempts to restore accessibility focus to the specified date.
- *
- * @param day The date which should receive focus
- * @return {@code false} if the date is not valid for this month view, or
- * {@code true} if the date received focus
- */
- boolean restoreAccessibilityFocus(Calendar day) {
- if ((day.get(Calendar.YEAR) != mYear) || (day.get(Calendar.MONTH) != mMonth) ||
- (day.get(Calendar.DAY_OF_MONTH) > mNumCells)) {
- return false;
- }
- mTouchHelper.setFocusedVirtualView(day.get(Calendar.DAY_OF_MONTH));
- return true;
- }
-
- /**
* Provides a virtual view hierarchy for interfacing with an accessibility
* service.
*/
@@ -581,24 +676,9 @@ class SimpleMonthView extends View {
super(host);
}
- public void setFocusedVirtualView(int virtualViewId) {
- getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
- virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- }
-
- public void clearFocusedVirtualView() {
- final int focusedVirtualView = getFocusedVirtualView();
- if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) {
- getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
- focusedVirtualView,
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
- null);
- }
- }
-
@Override
protected int getVirtualViewAt(float x, float y) {
- final int day = getDayFromLocation(x, y);
+ final int day = getDayAtLocation(x, y);
if (day >= 0) {
return day;
}
@@ -607,7 +687,7 @@ class SimpleMonthView extends View {
@Override
protected void getVisibleVirtualViews(IntArray virtualViewIds) {
- for (int day = 1; day <= mNumCells; day++) {
+ for (int day = 1; day <= mDaysInMonth; day++) {
virtualViewIds.add(day);
}
}
@@ -619,11 +699,20 @@ class SimpleMonthView extends View {
@Override
protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
- getItemBounds(virtualViewId, mTempRect);
+ final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
+
+ if (!hasBounds) {
+ // The day is invalid, kill the node.
+ mTempRect.setEmpty();
+ node.setContentDescription("");
+ node.setBoundsInParent(mTempRect);
+ node.setVisibleToUser(false);
+ return;
+ }
node.setContentDescription(getItemDescription(virtualViewId));
node.setBoundsInParent(mTempRect);
- node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+ node.addAction(AccessibilityAction.ACTION_CLICK);
if (virtualViewId == mActivatedDay) {
node.setSelected(true);
@@ -636,7 +725,7 @@ class SimpleMonthView extends View {
Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK:
- onDayClick(virtualViewId);
+ onDayClicked(virtualViewId);
return true;
}
@@ -644,26 +733,6 @@ class SimpleMonthView extends View {
}
/**
- * Calculates the bounding rectangle of a given time object.
- *
- * @param day The day to calculate bounds for
- * @param rect The rectangle in which to store the bounds
- */
- private void getItemBounds(int day, Rect rect) {
- final int offsetX = mPadding;
- final int offsetY = mMonthHeaderHeight;
- final int cellHeight = mRowHeight;
- final int cellWidth = ((mWidth - (2 * mPadding)) / mNumDays);
- final int index = ((day - 1) + findDayOffset());
- final int row = (index / mNumDays);
- final int column = (index % mNumDays);
- final int x = (offsetX + (column * cellWidth));
- final int y = (offsetY + (row * cellHeight));
-
- rect.set(x, y, (x + cellWidth), (y + cellHeight));
- }
-
- /**
* Generates a description for a given time object. Since this
* description will be spoken, the components are ordered by descending
* specificity as DAY MONTH YEAR.
diff --git a/core/java/android/widget/TextViewWithCircularIndicator.java b/core/java/android/widget/TextViewWithCircularIndicator.java
deleted file mode 100644
index d3c786c..0000000
--- a/core/java/android/widget/TextViewWithCircularIndicator.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.util.AttributeSet;
-
-import com.android.internal.R;
-
-class TextViewWithCircularIndicator extends TextView {
- private final Paint mCirclePaint = new Paint();
- private final String mItemIsSelectedText;
-
- public TextViewWithCircularIndicator(Context context) {
- this(context, null);
- }
-
- public TextViewWithCircularIndicator(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TextViewWithCircularIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TextViewWithCircularIndicator(Context context, AttributeSet attrs,
- int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- final Resources res = context.getResources();
- mItemIsSelectedText = res.getString(R.string.item_is_selected);
-
- init();
- }
-
- private void init() {
- mCirclePaint.setTypeface(Typeface.create(mCirclePaint.getTypeface(), Typeface.BOLD));
- mCirclePaint.setAntiAlias(true);
- mCirclePaint.setTextAlign(Paint.Align.CENTER);
- mCirclePaint.setStyle(Paint.Style.FILL);
- }
-
- public void setCircleColor(int color) {
- mCirclePaint.setColor(color);
- invalidate();
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- if (isActivated()) {
- final int width = getWidth();
- final int height = getHeight();
- final int radius = Math.min(width, height) / 2;
- canvas.drawCircle(width / 2, height / 2, radius, mCirclePaint);
- }
-
- super.onDraw(canvas);
- }
-
- @Override
- public CharSequence getContentDescription() {
- final CharSequence itemText = getText();
- if (isActivated()) {
- return String.format(mItemIsSelectedText, itemText);
- } else {
- return itemText;
- }
- }
-} \ No newline at end of file
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 6f0465f..7bd502e 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -17,10 +17,9 @@
package android.widget;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.util.AttributeSet;
-import android.util.StateSet;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -32,23 +31,14 @@ import com.android.internal.R;
/**
* Displays a selectable list of years.
*/
-class YearPickerView extends ListView implements AdapterView.OnItemClickListener,
- OnDateChangedListener {
- private final Calendar mMinDate = Calendar.getInstance();
- private final Calendar mMaxDate = Calendar.getInstance();
-
+class YearPickerView extends ListView {
private final YearAdapter mAdapter;
private final int mViewSize;
private final int mChildSize;
- private DatePickerController mController;
-
- private int mSelectedPosition = -1;
- private int mYearActivatedColor;
+ private OnYearSelectedListener mOnYearSelectedListener;
- public YearPickerView(Context context) {
- this(context, null);
- }
+ private long mCurrentTimeMillis;
public YearPickerView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.listViewStyle);
@@ -69,104 +59,187 @@ class YearPickerView extends ListView implements AdapterView.OnItemClickListener
mViewSize = res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height);
mChildSize = res.getDimensionPixelOffset(R.dimen.datepicker_year_label_height);
- setVerticalFadingEdgeEnabled(true);
- setFadingEdgeLength(mChildSize / 3);
-
- final int paddingTop = res.getDimensionPixelSize(
- R.dimen.datepicker_year_picker_padding_top);
- setPadding(0, paddingTop, 0, 0);
+ setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final int year = mAdapter.getYearForPosition(position);
+ mAdapter.setSelection(year);
- setOnItemClickListener(this);
- setDividerHeight(0);
+ if (mOnYearSelectedListener != null) {
+ mOnYearSelectedListener.onYearChanged(YearPickerView.this, year);
+ }
+ }
+ });
- mAdapter = new YearAdapter(getContext(), R.layout.year_label_text_view);
+ mAdapter = new YearAdapter(getContext());
setAdapter(mAdapter);
}
- public void setRange(Calendar min, Calendar max) {
- mMinDate.setTimeInMillis(min.getTimeInMillis());
- mMaxDate.setTimeInMillis(max.getTimeInMillis());
+ public void setOnYearSelectedListener(OnYearSelectedListener listener) {
+ mOnYearSelectedListener = listener;
+ }
- updateAdapterData();
+ public void setDate(long currentTimeMillis) {
+ mCurrentTimeMillis = currentTimeMillis;
}
- public void init(DatePickerController controller) {
- mController = controller;
- mController.registerOnDateChangedListener(this);
+ /**
+ * Sets the currently selected year. Jumps immediately to the new year.
+ *
+ * @param year the target year
+ */
+ public void setYear(final int year) {
+ mAdapter.setSelection(year);
- updateAdapterData();
+ post(new Runnable() {
+ @Override
+ public void run() {
+ final int position = mAdapter.getPositionForYear(year);
+ if (position >= 0 && position < getCount()) {
+ setSelectionCentered(position);
+ }
+ }
+ });
+ }
- onDateChanged();
+ public void setSelectionCentered(int position) {
+ final int offset = mViewSize / 2 - mChildSize / 2;
+ setSelectionFromTop(position, offset);
}
- public void setYearBackgroundColor(ColorStateList yearBackgroundColor) {
- mYearActivatedColor = yearBackgroundColor.getColorForState(
- StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
- invalidate();
+ public void setRange(Calendar min, Calendar max) {
+ mAdapter.setRange(min, max);
}
public void setYearTextAppearance(int resId) {
mAdapter.setItemTextAppearance(resId);
}
- private void updateAdapterData() {
- mAdapter.clear();
+ public void setYearActivatedTextAppearance(int resId) {
+ mAdapter.setItemActivatedTextAppearance(resId);
+ }
+
+ private static class YearAdapter extends BaseAdapter {
+ private static final int ITEM_LAYOUT = R.layout.year_label_text_view;
+
+ private final LayoutInflater mInflater;
+
+ private int mActivatedYear;
+ private int mMinYear;
+ private int mCount;
- final int maxYear = mMaxDate.get(Calendar.YEAR);
- for (int year = mMinDate.get(Calendar.YEAR); year <= maxYear; year++) {
- mAdapter.add(year);
+ private int mItemTextAppearanceResId;
+ private int mItemActivatedTextAppearanceResId;
+
+ public YearAdapter(Context context) {
+ mInflater = LayoutInflater.from(context);
}
- }
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mController.tryVibrate();
- if (position != mSelectedPosition) {
- mSelectedPosition = position;
- mAdapter.notifyDataSetChanged();
+ public void setRange(Calendar minDate, Calendar maxDate) {
+ final int minYear = minDate.get(Calendar.YEAR);
+ final int count = maxDate.get(Calendar.YEAR) - minYear + 1;
+
+ if (mMinYear != minYear || mCount != count) {
+ mMinYear = minYear;
+ mCount = count;
+ notifyDataSetInvalidated();
+ }
}
- mController.onYearSelected(mAdapter.getItem(position));
- }
- private class YearAdapter extends ArrayAdapter<Integer> {
- private int mItemTextAppearanceResId;
+ public boolean setSelection(int year) {
+ if (mActivatedYear != year) {
+ mActivatedYear = year;
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ public void setItemTextAppearance(int resId) {
+ mItemTextAppearanceResId = resId;
+ notifyDataSetChanged();
+ }
+
+ public void setItemActivatedTextAppearance(int resId) {
+ mItemActivatedTextAppearanceResId = resId;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mCount;
+ }
- public YearAdapter(Context context, int resource) {
- super(context, resource);
+ @Override
+ public Integer getItem(int position) {
+ return getYearForPosition(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getYearForPosition(position);
+ }
+
+ public int getPositionForYear(int year) {
+ return year - mMinYear;
+ }
+
+ public int getYearForPosition(int position) {
+ return mMinYear + position;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- final TextViewWithCircularIndicator v = (TextViewWithCircularIndicator)
- super.getView(position, convertView, parent);
- v.setTextAppearance(v.getContext(), mItemTextAppearanceResId);
- v.setCircleColor(mYearActivatedColor);
+ if (convertView == null) {
+ convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
+ }
- final int year = getItem(position);
- final boolean selected = mController.getSelectedDay().get(Calendar.YEAR) == year;
- v.setActivated(selected);
+ final int year = getYearForPosition(position);
+ final boolean activated = mActivatedYear == year;
+ final int textAppearanceResId;
+ if (activated && mItemActivatedTextAppearanceResId != 0) {
+ textAppearanceResId = mItemActivatedTextAppearanceResId;
+ } else {
+ textAppearanceResId = mItemTextAppearanceResId;
+ }
+
+ final TextView v = (TextView) convertView;
+ v.setText("" + year);
+ v.setTextAppearance(v.getContext(), textAppearanceResId);
+ v.setActivated(activated);
return v;
}
- public void setItemTextAppearance(int resId) {
- mItemTextAppearanceResId = resId;
+ @Override
+ public int getItemViewType(int position) {
+ return 0;
}
- }
- public void postSetSelectionCentered(final int position) {
- postSetSelectionFromTop(position, mViewSize / 2 - mChildSize / 2);
- }
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
- public void postSetSelectionFromTop(final int position, final int offset) {
- post(new Runnable() {
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
- @Override
- public void run() {
- setSelectionFromTop(position, offset);
- requestLayout();
- }
- });
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return true;
+ }
}
public int getFirstPositionOffset() {
@@ -177,22 +250,28 @@ class YearPickerView extends ListView implements AdapterView.OnItemClickListener
return firstChild.getTop();
}
- @Override
- public void onDateChanged() {
- updateAdapterData();
- mAdapter.notifyDataSetChanged();
- postSetSelectionCentered(
- mController.getSelectedDay().get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
- }
-
/** @hide */
@Override
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
super.onInitializeAccessibilityEventInternal(event);
+ // There are a bunch of years, so don't bother.
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
event.setFromIndex(0);
event.setToIndex(0);
}
}
+
+ /**
+ * The callback used to indicate the user changed the year.
+ */
+ public interface OnYearSelectedListener {
+ /**
+ * Called upon a year change.
+ *
+ * @param view The view associated with this listener.
+ * @param year The year that was set.
+ */
+ void onYearChanged(YearPickerView view, int year);
+ }
} \ No newline at end of file
diff --git a/core/java/com/android/internal/widget/AccessibleDateAnimator.java b/core/java/com/android/internal/widget/AccessibleDateAnimator.java
deleted file mode 100644
index f97a5d1..0000000
--- a/core/java/com/android/internal/widget/AccessibleDateAnimator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.content.Context;
-import android.text.format.DateUtils;
-import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ViewAnimator;
-
-/**
- * @hide
- */
-public class AccessibleDateAnimator extends ViewAnimator {
- private long mDateMillis;
-
- public AccessibleDateAnimator(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setDateMillis(long dateMillis) {
- mDateMillis = dateMillis;
- }
-
- /**
- * Announce the currently-selected date when launched.
- */
- @Override
- public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- // Clear the event's current text so that only the current date will be spoken.
- event.getText().clear();
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
- DateUtils.FORMAT_SHOW_WEEKDAY;
-
- String dateString = DateUtils.formatDateTime(getContext(), mDateMillis, flags);
- event.getText().add(dateString);
- return true;
- }
- return super.dispatchPopulateAccessibilityEventInternal(event);
- }
-}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index f916e6f..8018942 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -137,7 +137,7 @@ public class ViewPager extends ViewGroup {
private int mRestoredCurItem = -1;
private Parcelable mRestoredAdapterState = null;
private ClassLoader mRestoredClassLoader = null;
- private Scroller mScroller;
+ private final Scroller mScroller;
private PagerObserver mObserver;
private int mPageMargin;
@@ -162,9 +162,9 @@ public class ViewPager extends ViewGroup {
private boolean mIsBeingDragged;
private boolean mIsUnableToDrag;
- private int mDefaultGutterSize;
+ private final int mDefaultGutterSize;
private int mGutterSize;
- private int mTouchSlop;
+ private final int mTouchSlop;
/**
* Position of the last motion event.
*/
@@ -187,10 +187,10 @@ public class ViewPager extends ViewGroup {
* Determines speed during touch scrolling
*/
private VelocityTracker mVelocityTracker;
- private int mMinimumVelocity;
- private int mMaximumVelocity;
- private int mFlingDistance;
- private int mCloseEnough;
+ private final int mMinimumVelocity;
+ private final int mMaximumVelocity;
+ private final int mFlingDistance;
+ private final int mCloseEnough;
// If the pager is at least this close to its final position, complete the scroll
// on touch down and let the user interact with the content inside instead of
@@ -200,8 +200,8 @@ public class ViewPager extends ViewGroup {
private boolean mFakeDragging;
private long mFakeDragBeginTime;
- private EdgeEffect mLeftEdge;
- private EdgeEffect mRightEdge;
+ private final EdgeEffect mLeftEdge;
+ private final EdgeEffect mRightEdge;
private boolean mFirstLayout = true;
private boolean mNeedCalculatePageOffsets = false;
@@ -339,20 +339,24 @@ public class ViewPager extends ViewGroup {
interface Decor {}
public ViewPager(Context context) {
- super(context);
- initViewPager();
+ this(context, null);
}
public ViewPager(Context context, AttributeSet attrs) {
- super(context, attrs);
- initViewPager();
+ this(context, attrs, 0);
}
- void initViewPager() {
+ public ViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public ViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
setWillNotDraw(false);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setFocusable(true);
- final Context context = getContext();
+
mScroller = new Scroller(context, sInterpolator);
final ViewConfiguration configuration = ViewConfiguration.get(context);
final float density = context.getResources().getDisplayMetrics().density;