startRangeDate
and endRangeDate
*
.
*
* @param startRangeDate The start date.
* @param endRangeDate The end date.
*/
public void setRange(Calendar startRangeDate, Calendar endRangeDate) {
boolean doSetupAdapter = false;
if (mRangeStartDate.get(Calendar.DAY_OF_YEAR) != startRangeDate.get(Calendar.DAY_OF_YEAR)
|| mRangeStartDate.get(Calendar.YEAR) != startRangeDate.get(Calendar.YEAR)) {
mRangeStartDate.setTimeInMillis(startRangeDate.getTimeInMillis());
mRangeStartDate.setTimeZone(startRangeDate.getTimeZone());
doSetupAdapter = true;
}
if (mRangeEndDate.get(Calendar.DAY_OF_YEAR) != endRangeDate.get(Calendar.DAY_OF_YEAR)
|| mRangeEndDate.get(Calendar.YEAR) != endRangeDate.get(Calendar.YEAR)) {
mRangeEndDate.setTimeInMillis(endRangeDate.getTimeInMillis());
mRangeEndDate.setTimeZone(endRangeDate.getTimeZone());
doSetupAdapter = true;
}
if (doSetupAdapter) {
setUpAdapter();
}
}
/**
* Sets the listener to be notified upon day selection changes.
*
* @param listener The listener to be called back.
*/
public void setOnDateChangeListener(OnSelectedDayChangeListener listener) {
mOnChangeListener = listener;
}
/**
* Gets the selected day.
*
* @return The selected day.
*/
public Calendar getSelectedDay() {
return (Calendar) mAdapter.mSelectedDay.clone();
}
/**
* Sets the selected day. This is equivalent to a call to
* {@link #goTo(Calendar, boolean, boolean, boolean)} with
* the arguments selectedDay
, false
,
* true
, false
respectively.
*
* @param selectedDay The selected day.
*/
public void setSelectedDay(Calendar selectedDay) {
goTo(selectedDay, false, true, false);
}
/**
* Creates a new adapter if necessary and sets up its parameters. Override
* this method to provide a custom adapter.
*
* @hide
*/
protected void setUpAdapter() {
if (mAdapter == null) {
mAdapter = new WeeksAdapter(getContext());
mAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
if (mOnChangeListener != null) {
Calendar selectedDay = mAdapter.getSelectedDay();
mOnChangeListener.onSelectedDayChange(DayPicker.this,
selectedDay.get(Calendar.YEAR),
selectedDay.get(Calendar.MONTH),
selectedDay.get(Calendar.DAY_OF_MONTH));
}
}
});
mListView.setAdapter(mAdapter);
}
// refresh the view with the new parameters
mAdapter.notifyDataSetChanged();
}
/**
* Sets up the strings to be used by the header. Override this method to use
* different strings or modify the view params.
*
* @hide
*/
protected void setUpHeader() {
mDayLabels = new String[mDaysPerWeek];
for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
int calendarDay = (i < mDaysPerWeek) ? i : 1; // Calendar.MONDAY is
// 1
mDayLabels[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
DateUtils.LENGTH_SHORTEST);
}
}
/**
* Sets all the required fields for the list view. Override this method to
* set a different list view behavior.
*
* @hide
*/
protected void setUpListView() {
// Configure the listview
mListView.setDivider(null);
mListView.setItemsCanFocus(true);
mListView.setVerticalScrollBarEnabled(false);
mListView.setOnScrollListener(new OnScrollListener() {
public void onScrollStateChanged(AbsListView view, int scrollState) {
DayPicker.this.onScrollStateChanged(view, scrollState);
}
public void onScroll(
AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
DayPicker.this.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
});
// Make the scrolling behavior nicer
mListView.setFriction(mFriction);
mListView.setVelocityScale(mVelocityScale);
}
/**
* Fixes the day names header to provide correct spacing and updates the
* label text. Override this to set up a custom header.
*
* @hide
*/
protected void updateHeader() {
TextView label = (TextView) mDayNamesHeader.getChildAt(0);
if (mShowWeekNumber) {
label.setVisibility(View.VISIBLE);
} else {
label.setVisibility(View.GONE);
}
for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
label = (TextView) mDayNamesHeader.getChildAt(i);
if (i < mDaysPerWeek + 1) {
label.setText(mDayLabels[i - 1]);
label.setVisibility(View.VISIBLE);
} else {
label.setVisibility(View.GONE);
}
}
mDayNamesHeader.invalidate();
}
/**
* 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 year The year to move to.
* @param month The month to move to starting from zero.
* @param dayOfMonth The month 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.
*
* @throws IllegalArgumentException of the provided date is before the
* range start of after the range end.
*
* @see #setRange(Calendar, Calendar)
*/
public void goTo(int year, int month, int dayOfMonth, boolean animate, boolean setSelected,
boolean forceScroll) {
mTempCalendar.clear();
mTempCalendar.set(year, month, dayOfMonth);
goTo(mTempCalendar, animate, setSelected, forceScroll);
}
/**
* 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 date The time 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.
*
* @throws IllegalArgumentException of the provided date is before the
* range start of after the range end.
*
* @see #setRange(Calendar, Calendar)
*/
public void goTo(Calendar date, boolean animate, boolean setSelected, boolean forceScroll) {
if (date.before(mRangeStartDate) || date.after(mRangeEndDate)) {
throw new IllegalArgumentException("Time not between " + mRangeStartDate.getTime()
+ " and " + mRangeEndDate.getTime());
}
// Find the first and last entirely visible weeks
int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
View firstChild = mListView.getChildAt(0);
if (firstChild != null && firstChild.getTop() < 0) {
firstFullyVisiblePosition++;
}
int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
lastFullyVisiblePosition--;
}
if (setSelected) {
mAdapter.setSelectedDay(date);
}
// Get the week we're going to
int position = getWeeksDelta(date);
// Check if the selected day is now outside of our visible range
// and if so scroll to the month that contains it
if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
|| forceScroll) {
mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
mFirstDayOfMonth.setTimeZone(date.getTimeZone());
mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
setMonthDisplayed(mFirstDayOfMonth);
position = getWeeksDelta(mFirstDayOfMonth);
mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
if (animate) {
mListView.smoothScrollToPositionFromTop(position, mListTopOffset,
GOTO_SCROLL_DURATION);
} else {
mListView.setSelectionFromTop(position, mListTopOffset);
// Perform any after scroll operations that are needed
onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE);
}
} else if (setSelected) {
// Otherwise just set the selection
setMonthDisplayed(date);
}
}
/**
* Called when a view
transitions to a new scrollState
*
.
*
* @hide
*/
protected void onScrollStateChanged(AbsListView view, int scrollState) {
mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
}
/**
* Updates the title and selected month if the view
has moved to a new
* month.
*
* @hide
*/
protected void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
WeekView child = (WeekView) view.getChildAt(0);
if (child == null) {
return;
}
// Figure out where we are
int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
// If we have moved since our last call update the direction
if (currScroll < mPreviousScrollPosition) {
mIsScrollingUp = true;
} else if (currScroll > mPreviousScrollPosition) {
mIsScrollingUp = false;
} else {
return;
}
// Use some hysteresis for checking which month to highlight. This
// causes the month to transition when two full weeks of a month are
// visible when scrolling up, and when the first day in a month reaches
// the top of the screen when scrolling down.
if (mIsScrollingUp) {
child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
} else if (offset != 0) {
child = (WeekView) view.getChildAt(offset);
}
// Find out which month we're moving into
int month;
if (mIsScrollingUp) {
month = child.getMonthOfFirstWeekDay();
} else {
month = child.getMonthOfLastWeekDay();
}
// And how it relates to our current highlighted month
int monthDiff;
if (mCurrentMonthDisplayed == 11 && month == 0) {
monthDiff = 1;
} else if (mCurrentMonthDisplayed == 0 && month == 11) {
monthDiff = -1;
} else {
monthDiff = month - mCurrentMonthDisplayed;
}
// Only switch months if we're scrolling away from the currently
// selected month
if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
Calendar firstDay = child.getFirstDay();
if (mIsScrollingUp) {
firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
} else {
firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
}
setMonthDisplayed(firstDay);
}
mPreviousScrollPosition = currScroll;
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.
*
* @param calendar A day in the new focus month.
*
* @hide
*/
protected void setMonthDisplayed(Calendar calendar) {
mMonthName.setText(DateFormat.format(mMonthNameFormatSrting, calendar));
mMonthName.invalidate();
mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
mAdapter.setFocusMonth(mCurrentMonthDisplayed);
// TODO Send Accessibility Event
}
/**
* @return Returns the number of weeks between the current week day of the
* fromDate
and the first day of week of
* toDate
.
*
* @hide
*/
protected int getWeeksDelta(Calendar toDate) {
if (toDate.before(mRangeStartDate)) {
throw new IllegalArgumentException("fromDate: " + mRangeStartDate.getTime()
+ " does not precede toDate: " + toDate.getTime());
}
int fromDateDayOfWeek = mRangeStartDate.get(Calendar.DAY_OF_WEEK);
long diff = (fromDateDayOfWeek - toDate.getFirstDayOfWeek()) * MILLIS_IN_DAY;
if (diff < 0) {
diff = diff + MILLIS_IN_WEEK;
}
long refDay = mRangeStartDate.getTimeInMillis() - diff;
return (int) ((toDate.getTimeInMillis() - refDay) / MILLIS_IN_WEEK);
}
/**
* Command responsible for acting upon scroll state changes.
*
* @hide
*/
protected class ScrollStateRunnable implements Runnable {
private AbsListView mView;
private int mNewState;
/**
* 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) {
removeCallbacks(this);
mView = view;
mNewState = scrollState;
removeCallbacks(this);
postDelayed(this, SCROLL_CHANGE_DELAY);
}
public void run() {
mCurrentScrollState = mNewState;
// Fix the position after a scroll or a fling ends
if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
&& mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
mPreviousScrollState = mNewState;
View child = mView.getChildAt(0);
if (child == null) {
// The view is no longer visible, just return
return;
}
int dist = child.getBottom() - mListTopOffset;
if (dist > mListTopOffset) {
if (mIsScrollingUp) {
mView.smoothScrollBy(dist - child.getHeight(), ADJUSTMENT_SCROLL_DURATION);
} else {
mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
}
}
} else {
mPreviousScrollState = mNewState;
}
}
}
/**
* * This is a specialized adapter for creating a list of weeks with * selectable days. It can be configured to display the week number, start * the week on a given day, show a reduced number of days, or display an * arbitrary number of weeks at a time. *
* * @hide */ public class WeeksAdapter extends BaseAdapter implements OnTouchListener { /** * The default maximum year supported by the Date Time Picker. */ public static final int DEFAULT_MAX_CALENDAR_YEAR = 2100; /** * The default minimum year supported by the Date Time Picker. */ public static final int DEFAULT_MIN_CALENDAR_YEAR = 1900; /** * The number of weeks to display at a time. */ public static final String WEEK_PARAMS_NUM_WEEKS = "num_weeks"; /** * Which month should be in focus currently. */ public static final String WEEK_PARAMS_FOCUS_MONTH = "focus_month"; /** * Whether the week number should be shown. Non-zero to show them. */ public static final String WEEK_PARAMS_SHOW_WEEK = "week_numbers"; /** * Which day the week should start on. {@link Time#SUNDAY} through * {@link Time#SATURDAY}. */ public static final String WEEK_PARAMS_WEEK_START = "week_start"; /** * The year of the highlighted day. */ public static final String WEEK_PARAMS_YEAR = "selected_year"; /** * The month of the highlighted day. */ public static final String WEEK_PARAMS_MONTH = "selected_month"; /** * The year of the highlighted day. */ public static final String WEEK_PARAMS_DAY_OF_MONTH = "selected_day_of_month"; /** * The start date of the supported interval. */ public static final String WEEK_PARAMS_START_DATE_RANGE_MILLIS = "start_date_gange_millis"; /** * The end date of the supported interval. */ public static final String WEEK_PARAMS_END_DATE_RANGE_MILLIS = "end_date_gange_millis"; /** * How many days of the week to display [1-7]. */ public static final String WEEK_PARAMS_DAYS_PER_WEEK = "days_per_week"; protected int WEEK_7_OVERHANG_HEIGHT = 7; protected int mSelectedWeek; protected GestureDetector mGestureDetector; protected int mFocusMonth = 0; private final Calendar mSelectedDay = Calendar.getInstance(); private int mTotalWeekCount = -1; public WeeksAdapter(Context context) { mContext = context; if (sScale == 0) { sScale = context.getResources().getDisplayMetrics().density; if (sScale != 1) { WEEK_7_OVERHANG_HEIGHT *= sScale; } } init(); } /** * Set up the gesture detector and selected time */ protected void init() { mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener()); mSelectedWeek = getWeeksDelta(mSelectedDay); mTotalWeekCount = getWeeksDelta(mRangeEndDate); } /** * Updates the selected day and related parameters. * * @param selectedDay The time to highlight */ public void setSelectedDay(Calendar selectedDay) { if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDay.get(Calendar.DAY_OF_YEAR) && selectedDay.get(Calendar.YEAR) == mSelectedDay.get(Calendar.YEAR)) { return; } mSelectedDay.setTimeInMillis(selectedDay.getTimeInMillis()); mSelectedDay.setTimeZone(selectedDay.getTimeZone()); mSelectedWeek = getWeeksDelta(mSelectedDay); mFocusMonth = mSelectedDay.get(Calendar.MONTH); notifyDataSetChanged(); invalidate(); // Test } /** * @return The selected day of month. */ public Calendar getSelectedDay() { return mSelectedDay; } /** * updates any config options that may have changed and refreshes the * view */ public void refresh() { notifyDataSetChanged(); } @Override public int getCount() { return mTotalWeekCount; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return position; } @SuppressWarnings("unchecked") @Override public View getView(int position, View convertView, ViewGroup parent) { WeekView v; HashMapvalue
for the drawing
* parameter
in the drawingParams
.
*/
private void putDrawingParementer(HashMap* This is a dynamic view for drawing a single week. It can be configured to * display the week number, start the week on a given day, or show a reduced * number of days. It is intended for use as a single view within a * ListView. See {@link WeeksAdapter} for usage. *
* * @hide */ public class WeekView extends View { /* * These params can be passed into the view to control how it appears. * {@link #VIEW_PARAMS_WEEK} is the only required field, though the * default values are unlikely to fit most layouts correctly. */ /** * This sets the height of this week in pixels */ public static final String VIEW_PARAMS_HEIGHT = "height"; /** * This specifies the position (or weeks since the epoch) of this week. */ public static final String VIEW_PARAMS_WEEK = "week"; /** * This sets one of the days in this view as selected * {@link Time#SUNDAY} through {@link Time#SATURDAY}. */ public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day"; /** * Which day the week should start on. {@link Time#SUNDAY} through * {@link Time#SATURDAY}. */ public static final String VIEW_PARAMS_WEEK_START = "week_start"; /** * How many days to display at a time. Days will be displayed starting * with {@link #mFirstDay}. */ public static final String VIEW_PARAMS_NUM_DAYS = "num_days"; /** * Which month is currently in focus, as defined by {@link Time#month} * [0-11]. */ public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month"; /** * If this month should display week numbers. false if 0, true * otherwise. */ public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num"; protected int mDefaultHeight = 32; protected int mMinHeight = 10; protected static final int DEFAULT_SELECTED_DAY = -1; protected static final int DEFAULT_WEEK_START = Calendar.SUNDAY; protected static final int DEFAULT_NUM_DAYS = 7; protected static final int DEFAULT_SHOW_WK_NUM = 0; protected static final int DEFAULT_FOCUS_MONTH = -1; protected static final int DAY_SEPARATOR_WIDTH = 1; protected int mNumberTextSize = 14; // affects the padding on the sides of this view protected int mPadding = 0; protected final Rect mTempRect = new Rect(); protected final Paint mDrawPaint = new Paint(); protected Paint mMonthNumDrawPaint = new Paint(); protected Drawable mSelectedDayLine; protected final int mSelectionBackgroundColor; protected final int mFocusedMonthDateColor; protected final int mOtherMonthDateColor; protected final int mGridLinesColor; protected final int mWeekNumberColor; // Cache the number strings so we don't have to recompute them each time protected String[] mDayNumbers; // Quick lookup for checking which days are in the focus month protected boolean[] mFocusDay; // The first day displayed by this item protected Calendar mFirstDay; // The month of the first day in this week protected int mMonthOfFirstWeekDay = -1; // The month of the last day in this week protected int mLastWeekDayMonth = -1; // The position of this week, equivalent to weeks since the week of Jan // 1st, 1900 protected int mWeek = -1; // Quick reference to the width of this view, matches parent protected int mWidth; // The height this view should draw at in pixels, set by height param protected int mHeight = mDefaultHeight; // Whether the week number should be shown protected boolean mShowWeekNum = false; // If this view contains the selected day protected boolean mHasSelectedDay = false; // Which day is selected [0-6] or -1 if no day is selected protected int mSelectedDay = DEFAULT_SELECTED_DAY; // How many days to display protected int mNumDays = DEFAULT_NUM_DAYS; // The number of days + a spot for week number if it is displayed protected int mNumCells = mNumDays; // The left edge of the selected day protected int mSelectedLeft = -1; // The right edge of the selected day protected int mSelectedRight = -1; public WeekView(Context context) { super(context); TypedValue outTypedValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.dayPickerWeekViewStyle, outTypedValue, true); TypedArray attributesArray = context.obtainStyledAttributes(outTypedValue.resourceId, R.styleable.DayPickerWeekView); mSelectionBackgroundColor = attributesArray.getColor( R.styleable.DayPickerWeekView_selectionBackgroundColor, 0); mFocusedMonthDateColor = attributesArray.getColor( R.styleable.DayPickerWeekView_focusedMonthDateColor, 0); mOtherMonthDateColor = attributesArray.getColor( R.styleable.DayPickerWeekView_otherMonthDateColor, 0); mGridLinesColor = attributesArray.getColor( R.styleable.DayPickerWeekView_gridLinesColor, 0); mWeekNumberColor = attributesArray.getColor( R.styleable.DayPickerWeekView_weekNumberColor, 0); mSelectedDayLine = attributesArray .getDrawable(R.styleable.DayPickerWeekView_selectedDayLine); attributesArray.recycle(); if (sScale == 0) { sScale = context.getResources().getDisplayMetrics().density; if (sScale != 1) { mDefaultHeight *= sScale; mMinHeight *= sScale; mNumberTextSize *= sScale; } } // Sets up any standard paints that will be used setPaintProperties(); } /** * Sets all the parameters for displaying this week. The only required * parameter is the week number. Other 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. See {@link #VIEW_PARAMS_HEIGHT} for more info on * parameters. * * @param params A map of the new parameters, see * {@link #VIEW_PARAMS_HEIGHT} */ public void setWeekParams(HashMap