diff options
author | Alan Viverette <alanv@google.com> | 2015-03-25 13:00:42 -0700 |
---|---|---|
committer | Alan Viverette <alanv@google.com> | 2015-03-30 12:15:25 -0700 |
commit | 60b674e07bf7346a673abd4a5f40bddeca16e7ff (patch) | |
tree | f6f700f5c0a7652b68886755335f64acb069cc4e /core/java | |
parent | 469d94490ed9cf3e08610250a3358bdd83d618a6 (diff) | |
download | frameworks_base-60b674e07bf7346a673abd4a5f40bddeca16e7ff.zip frameworks_base-60b674e07bf7346a673abd4a5f40bddeca16e7ff.tar.gz frameworks_base-60b674e07bf7346a673abd4a5f40bddeca16e7ff.tar.bz2 |
Clean up date picker attributes, add carets
Bug: 19819283
Bug: 19431364
Change-Id: Idd66f4ceb99d598c0f256d85c43bff6e25ccdd8f
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/view/View.java | 1 | ||||
-rwxr-xr-x | core/java/android/widget/DatePickerCalendarDelegate.java | 31 | ||||
-rw-r--r-- | core/java/android/widget/DayPickerAdapter.java | 107 | ||||
-rw-r--r-- | core/java/android/widget/DayPickerView.java | 11 | ||||
-rw-r--r-- | core/java/android/widget/SimpleMonthView.java | 355 | ||||
-rw-r--r-- | core/java/android/widget/YearPickerView.java | 31 |
6 files changed, 370 insertions, 166 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index cfcc6fe..887187a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11162,6 +11162,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index 7b8a979..a157087 100755 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -49,7 +49,6 @@ import java.util.Locale; * A delegate for picking up a date (day / month / year). */ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { - private static final int USE_LOCALE = 0; private static final int UNINITIALIZED = -1; @@ -61,9 +60,9 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { private static final int ANIMATION_DURATION = 300; - public static final int[] ATTRS_TEXT_COLOR = new int[]{com.android.internal.R.attr.textColor}; - - public static final int[] ATTRS_DISABLED_ALPHA = new int[]{ + private static final int[] ATTRS_TEXT_COLOR = new int[] { + com.android.internal.R.attr.textColor}; + private static final int[] ATTRS_DISABLED_ALPHA = new int[] { com.android.internal.R.attr.disabledAlpha}; private SimpleDateFormat mYearFormat; @@ -157,6 +156,8 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground)); } + a.recycle(); + // Set up picker container. mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator); @@ -174,32 +175,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { mYearPickerView.setDate(mCurrentDate.getTimeInMillis()); mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener); - final int yearTextAppearanceResId = a.getResourceId( - R.styleable.DatePicker_yearListItemTextAppearance, 0); - if (yearTextAppearanceResId != 0) { - mYearPickerView.setYearTextAppearance(yearTextAppearanceResId); - } - - final int yearActivatedTextAppearanceResId = a.getResourceId( - R.styleable.DatePicker_yearListItemActivatedTextAppearance, 0); - if (yearActivatedTextAppearanceResId != 0) { - mYearPickerView.setYearActivatedTextAppearance(yearActivatedTextAppearanceResId); - } - - a.recycle(); - // Set up content descriptions. mSelectDay = res.getString(R.string.select_day); mSelectYear = res.getString(R.string.select_year); - 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); - // Initialize for current locale. This also initializes the date, so no // need to call onDateChanged. onLocaleChanged(mCurrentLocale); diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerAdapter.java index 4f9f09e..9a4b6f5 100644 --- a/core/java/android/widget/DayPickerAdapter.java +++ b/core/java/android/widget/DayPickerAdapter.java @@ -18,10 +18,15 @@ package android.widget; import com.android.internal.widget.PagerAdapter; +import android.annotation.IdRes; +import android.annotation.LayoutRes; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.util.SparseArray; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.SimpleMonthView.OnDayClickListener; @@ -37,9 +42,13 @@ class DayPickerAdapter extends PagerAdapter { private final Calendar mMinDate = Calendar.getInstance(); private final Calendar mMaxDate = Calendar.getInstance(); - private final SparseArray<SimpleMonthView> mItems = new SparseArray<>(); + private final SparseArray<ViewHolder> mItems = new SparseArray<>(); - private Calendar mSelectedDay = Calendar.getInstance(); + private final LayoutInflater mInflater; + private final int mLayoutResId; + private final int mCalendarViewId; + + private Calendar mSelectedDay = null; private int mMonthTextAppearance; private int mDayOfWeekTextAppearance; @@ -51,19 +60,29 @@ class DayPickerAdapter extends PagerAdapter { private OnDaySelectedListener mOnDaySelectedListener; + private int mCount; private int mFirstDayOfWeek; - public DayPickerAdapter(Context context) { + public DayPickerAdapter(@NonNull Context context, @LayoutRes int layoutResId, + @IdRes int calendarViewId) { + mInflater = LayoutInflater.from(context); + mLayoutResId = layoutResId; + mCalendarViewId = calendarViewId; + 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) { + public void setRange(@NonNull Calendar min, @NonNull Calendar max) { mMinDate.setTimeInMillis(min.getTimeInMillis()); mMaxDate.setTimeInMillis(max.getTimeInMillis()); + final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR); + final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH); + mCount = diffMonth + MONTHS_IN_YEAR * diffYear + 1; + // Positions are now invalid, clear everything and start over. notifyDataSetChanged(); } @@ -80,7 +99,7 @@ class DayPickerAdapter extends PagerAdapter { // Update displayed views. final int count = mItems.size(); for (int i = 0; i < count; i++) { - final SimpleMonthView monthView = mItems.valueAt(i); + final SimpleMonthView monthView = mItems.valueAt(i).calendar; monthView.setFirstDayOfWeek(weekStart); } } @@ -94,23 +113,25 @@ class DayPickerAdapter extends PagerAdapter { * * @param day the selected day */ - public void setSelectedDay(Calendar day) { + public void setSelectedDay(@Nullable 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 (oldPosition != newPosition && oldPosition >= 0) { + final ViewHolder oldMonthView = mItems.get(oldPosition, null); if (oldMonthView != null) { - oldMonthView.setSelectedDay(-1); + oldMonthView.calendar.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); + if (newPosition >= 0) { + final ViewHolder newMonthView = mItems.get(newPosition, null); + if (newMonthView != null) { + final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH); + newMonthView.calendar.setSelectedDay(dayOfMonth); + } } mSelectedDay = day; @@ -155,14 +176,13 @@ class DayPickerAdapter extends PagerAdapter { @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; + return mCount; } @Override public boolean isViewFromObject(View view, Object object) { - return view == object; + final ViewHolder holder = (ViewHolder) object; + return view == holder.container; } private int getMonthForPosition(int position) { @@ -173,7 +193,11 @@ class DayPickerAdapter extends PagerAdapter { return position / MONTHS_IN_YEAR + mMinDate.get(Calendar.YEAR); } - private int getPositionForDay(Calendar day) { + private int getPositionForDay(@Nullable Calendar day) { + if (day == null) { + return -1; + } + 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; @@ -181,7 +205,9 @@ class DayPickerAdapter extends PagerAdapter { @Override public Object instantiateItem(ViewGroup container, int position) { - final SimpleMonthView v = new SimpleMonthView(container.getContext()); + final View itemView = mInflater.inflate(mLayoutResId, container, false); + + final SimpleMonthView v = (SimpleMonthView) itemView.findViewById(mCalendarViewId); v.setOnDayClickListener(mOnDayClickListener); v.setMonthTextAppearance(mMonthTextAppearance); v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance); @@ -205,7 +231,7 @@ class DayPickerAdapter extends PagerAdapter { final int year = getYearForPosition(position); final int selectedDay; - if (mSelectedDay.get(Calendar.MONTH) == month) { + if (mSelectedDay != null && mSelectedDay.get(Calendar.MONTH) == month) { selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH); } else { selectedDay = -1; @@ -227,33 +253,34 @@ class DayPickerAdapter extends PagerAdapter { v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek, enabledDayRangeStart, enabledDayRangeEnd); + v.setPrevEnabled(position > 0); + v.setNextEnabled(position < mCount - 1); - mItems.put(position, v); + final ViewHolder holder = new ViewHolder(position, itemView, v); + mItems.put(position, holder); - container.addView(v); + container.addView(itemView); - return v; + return holder; } @Override public void destroyItem(ViewGroup container, int position, Object object) { - container.removeView(mItems.get(position)); + final ViewHolder holder = (ViewHolder) object; + container.removeView(holder.container); 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; + final ViewHolder holder = (ViewHolder) object; + return holder.position; } @Override public CharSequence getPageTitle(int position) { - final SimpleMonthView v = mItems.get(position); + final SimpleMonthView v = mItems.get(position).calendar; if (v != null) { return v.getTitle(); } @@ -275,9 +302,29 @@ class DayPickerAdapter extends PagerAdapter { } } } + + @Override + public void onNavigationClick(SimpleMonthView view, int direction, boolean animate) { + if (mOnDaySelectedListener != null) { + mOnDaySelectedListener.onNavigationClick(DayPickerAdapter.this, direction, animate); + } + } }; + private static class ViewHolder { + public final int position; + public final View container; + public final SimpleMonthView calendar; + + public ViewHolder(int position, View container, SimpleMonthView calendar) { + this.position = position; + this.container = container; + this.calendar = calendar; + } + } + public interface OnDaySelectedListener { public void onDaySelected(DayPickerAdapter view, Calendar day); + public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate); } } diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index a7ae926..e2f8efc 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -88,7 +88,8 @@ class DayPickerView extends ViewPager { a.recycle(); // Set up adapter. - mAdapter = new DayPickerAdapter(context); + mAdapter = new DayPickerAdapter(context, + R.layout.date_picker_month_item_material, R.id.month_view); mAdapter.setMonthTextAppearance(monthTextAppearanceResId); mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId); mAdapter.setDayTextAppearance(dayTextAppearanceResId); @@ -128,6 +129,14 @@ class DayPickerView extends ViewPager { mOnDaySelectedListener.onDaySelected(DayPickerView.this, day); } } + + @Override + public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate) { + // ViewPager clamps input values, so we don't need to worry + // about passing invalid indices. + final int nextItem = getCurrentItem() + direction; + setCurrentItem(nextItem, animate); + } }); } diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index 4e5a39a..3fb096c 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -26,6 +26,7 @@ import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.TextPaint; import android.text.format.DateFormat; @@ -59,6 +60,12 @@ class SimpleMonthView extends View { private static final String DEFAULT_TITLE_FORMAT = "MMMMy"; private static final String DAY_OF_WEEK_FORMAT = "EEEEE"; + /** Virtual view ID for previous button. */ + private static final int ITEM_ID_PREV = 0x101; + + /** Virtual view ID for next button. */ + private static final int ITEM_ID_NEXT = 0x100; + private final TextPaint mMonthPaint = new TextPaint(); private final TextPaint mDayOfWeekPaint = new TextPaint(); private final TextPaint mDayPaint = new TextPaint(); @@ -66,13 +73,27 @@ class SimpleMonthView extends View { private final Paint mDayHighlightPaint = new Paint(); private final Calendar mCalendar = Calendar.getInstance(); - private final Calendar mDayLabelCalendar = Calendar.getInstance(); + private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance(); private final MonthViewTouchHelper mTouchHelper; private final SimpleDateFormat mTitleFormatter; private final SimpleDateFormat mDayOfWeekFormatter; + private final int mMonthHeight; + private final int mDayOfWeekHeight; + private final int mDayHeight; + private final int mCellWidth; + private final int mDaySelectorRadius; + + // Next/previous drawables. + private final Drawable mPrevDrawable; + private final Drawable mNextDrawable; + private final Rect mPrevHitArea; + private final Rect mNextHitArea; + private final CharSequence mPrevContentDesc; + private final CharSequence mNextContentDesc; + private CharSequence mTitle; private int mMonth; @@ -81,12 +102,6 @@ class SimpleMonthView extends View { private int mPaddedWidth; private int mPaddedHeight; - private final int mMonthHeight; - private final int mDayOfWeekHeight; - private final int mDayHeight; - private final int mCellWidth; - private final int mDaySelectorRadius; - /** The day of month for the selected day, or -1 if no day is selected. */ private int mActivatedDay = -1; @@ -122,7 +137,10 @@ class SimpleMonthView extends View { private ColorStateList mDayTextColor; - private int mTouchedDay = -1; + private int mTouchedItem = -1; + + private boolean mPrevEnabled; + private boolean mNextEnabled; public SimpleMonthView(Context context) { this(context, null); @@ -146,6 +164,13 @@ class SimpleMonthView extends View { mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width); mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius); + mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left); + mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right); + mPrevHitArea = mPrevDrawable != null ? new Rect() : null; + mNextHitArea = mNextDrawable != null ? new Rect() : null; + mPrevContentDesc = res.getText(R.string.date_picker_prev_month_button); + mNextContentDesc = res.getText(R.string.date_picker_next_month_button); + // Set up accessibility components. mTouchHelper = new MonthViewTouchHelper(this); setAccessibilityDelegate(mTouchHelper); @@ -160,6 +185,18 @@ class SimpleMonthView extends View { initPaints(res); } + public void setNextEnabled(boolean enabled) { + mNextEnabled = enabled; + mTouchHelper.invalidateRoot(); + invalidate(); + } + + public void setPrevEnabled(boolean enabled) { + mPrevEnabled = enabled; + mTouchHelper.invalidateRoot(); + invalidate(); + } + /** * Applies the specified text appearance resource to a paint, returning the * text color if one is set in the text appearance. @@ -192,7 +229,16 @@ class SimpleMonthView extends View { } public void setMonthTextAppearance(int resId) { - applyTextAppearance(mMonthPaint, resId); + final ColorStateList monthColor = applyTextAppearance(mMonthPaint, resId); + if (monthColor != null) { + if (mPrevDrawable != null) { + mPrevDrawable.setTintList(monthColor); + } + if (mNextDrawable != null) { + mNextDrawable.setTintList(monthColor); + } + } + invalidate(); } @@ -300,23 +346,26 @@ class SimpleMonthView extends View { @Override public boolean onTouchEvent(MotionEvent event) { + final int x = (int) (event.getX() + 0.5f); + final int y = (int) (event.getY() + 0.5f); + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: - final int touchedDay = getDayAtLocation(event.getX(), event.getY()); - if (mTouchedDay != touchedDay) { - mTouchedDay = touchedDay; + final int touchedItem = getItemAtLocation(x, y); + if (mTouchedItem != touchedItem) { + mTouchedItem = touchedItem; invalidate(); } break; case MotionEvent.ACTION_UP: - final int clickedDay = getDayAtLocation(event.getX(), event.getY()); - onDayClicked(clickedDay); + final int clickedItem = getItemAtLocation(x, y); + onItemClicked(clickedItem, true); // Fall through. case MotionEvent.ACTION_CANCEL: // Reset touched day on stream end. - mTouchedDay = -1; + mTouchedItem = -1; invalidate(); break; } @@ -332,6 +381,7 @@ class SimpleMonthView extends View { drawMonth(canvas); drawDaysOfWeek(canvas); drawDays(canvas); + drawButtons(canvas); canvas.translate(-paddingLeft, -paddingTop); } @@ -347,34 +397,43 @@ class SimpleMonthView extends View { } 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); + final TextPaint p = mDayOfWeekPaint; + final int headerHeight = mMonthHeight; + final int rowHeight = mDayOfWeekHeight; + final int colWidth = mPaddedWidth / DAYS_IN_WEEK; + + // Text is vertically centered within the day of week height. + final float halfLineHeight = (p.ascent() + p.descent()) / 2f; + final int rowCenter = headerHeight + rowHeight / 2; + + for (int col = 0; col < DAYS_IN_WEEK; col++) { + final int colCenter = colWidth * col + colWidth / 2; + final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK; + final String label = getDayOfWeekLabel(dayOfWeek); + canvas.drawText(label, colCenter, rowCenter - halfLineHeight, p); } } + private String getDayOfWeekLabel(int dayOfWeek) { + mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek); + return mDayOfWeekFormatter.format(mDayOfWeekLabelCalendar.getTime()); + } + /** * Draws the month days. */ private void drawDays(Canvas canvas) { - final int cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2); + final TextPaint p = mDayPaint; + final int headerHeight = mMonthHeight + mDayOfWeekHeight; + final int rowHeight = mDayHeight; + final int colWidth = mPaddedWidth / DAYS_IN_WEEK; - // Vertically centered within the cell height. - final float halfLineHeight = (mDayPaint.ascent() + mDayPaint.descent()) / 2; - float centerY = mMonthHeight + mDayOfWeekHeight + mDayHeight / 2f; + // Text is vertically centered within the row height. + final float halfLineHeight = (p.ascent() + p.descent()) / 2f; + int rowCenter = headerHeight + rowHeight / 2; - for (int day = 1, j = findDayOffset(); day <= mDaysInMonth; day++) { - final int x = (2 * j + 1) * cellWidthHalf; + for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) { + final int colCenter = colWidth * col + colWidth / 2; int stateMask = 0; if (day >= mEnabledDayStart && day <= mEnabledDayEnd) { @@ -386,12 +445,12 @@ class SimpleMonthView extends View { stateMask |= StateSet.VIEW_STATE_ACTIVATED; // Adjust the circle to be centered on the row. - canvas.drawCircle(x, centerY, mDaySelectorRadius, mDaySelectorPaint); - } else if (mTouchedDay == day) { + canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDaySelectorPaint); + } else if (mTouchedItem == day) { stateMask |= StateSet.VIEW_STATE_PRESSED; // Adjust the circle to be centered on the row. - canvas.drawCircle(x, centerY, mDaySelectorRadius, mDayHighlightPaint); + canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDayHighlightPaint); } final boolean isDayToday = mToday == day; @@ -402,19 +461,29 @@ class SimpleMonthView extends View { final int[] stateSet = StateSet.get(stateMask); dayTextColor = mDayTextColor.getColorForState(stateSet, 0); } - mDayPaint.setColor(dayTextColor); + p.setColor(dayTextColor); - canvas.drawText("" + day, x, centerY - halfLineHeight, mDayPaint); + canvas.drawText(Integer.toString(day), colCenter, rowCenter - halfLineHeight, p); - j++; + col++; - if (j == DAYS_IN_WEEK) { - j = 0; - centerY += mDayHeight; + if (col == DAYS_IN_WEEK) { + col = 0; + rowCenter += rowHeight; } } } + private void drawButtons(Canvas canvas) { + if (mPrevEnabled && mPrevDrawable != null) { + mPrevDrawable.draw(canvas); + } + + if (mNextEnabled && mNextDrawable != null) { + mNextDrawable.draw(canvas); + } + } + private static boolean isValidDayOfWeek(int day) { return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY; } @@ -569,8 +638,42 @@ class SimpleMonthView extends View { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { - mPaddedWidth = w - getPaddingLeft() - getPaddingRight(); - mPaddedHeight = w - getPaddingTop() - getPaddingBottom(); + final int paddedLeft = getPaddingLeft(); + final int paddedTop = getPaddingTop(); + final int paddedRight = w - getPaddingRight(); + final int paddedBottom = h - getPaddingBottom(); + mPaddedWidth = paddedRight - paddedLeft; + mPaddedHeight = paddedBottom - paddedTop; + + final int monthHeight = mMonthHeight; + final int cellWidth = mPaddedWidth / DAYS_IN_WEEK; + + // Vertically center the previous/next drawables within the month + // header, horizontally center within the day cell, then expand the + // hit area to ensure it's at least 48x48dp. + final Drawable prevDrawable = mPrevDrawable; + if (prevDrawable != null) { + final int dW = prevDrawable.getIntrinsicWidth(); + final int dH = prevDrawable.getIntrinsicHeight(); + final int iconTop = (monthHeight - dH) / 2; + final int iconLeft = (cellWidth - dW) / 2; + + // Button bounds don't include padding, but hit area does. + prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH); + mPrevHitArea.set(0, 0, paddedLeft + cellWidth, paddedTop + monthHeight); + } + + final Drawable nextDrawable = mNextDrawable; + if (nextDrawable != null) { + final int dW = nextDrawable.getIntrinsicWidth(); + final int dH = nextDrawable.getIntrinsicHeight(); + final int iconTop = (monthHeight - dH) / 2; + final int iconRight = mPaddedWidth - (cellWidth - dW) / 2; + + // Button bounds don't include padding, but hit area does. + nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH); + mNextHitArea.set(paddedRight - cellWidth, 0, w, paddedTop + monthHeight); + } // Invalidate cached accessibility information. mTouchHelper.invalidateRoot(); @@ -585,22 +688,29 @@ class SimpleMonthView extends View { } /** - * 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. + * Calculates the day of the month or item identifier 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 + * @return the day of the month at (x, y), an item identifier, or -1 if the + * position wasn't in a valid day or item */ - private int getDayAtLocation(float x, float y) { - final int paddedX = (int) (x - getPaddingLeft() + 0.5f); + private int getItemAtLocation(int x, int y) { + if (mNextEnabled && mNextDrawable != null && mNextHitArea.contains(x, y)) { + return ITEM_ID_NEXT; + } else if (mPrevEnabled && mPrevDrawable != null && mPrevHitArea.contains(x, y)) { + return ITEM_ID_PREV; + } + + final int paddedX = x - getPaddingLeft(); if (paddedX < 0 || paddedX >= mPaddedWidth) { return -1; } final int headerHeight = mMonthHeight + mDayOfWeekHeight; - final int paddedY = (int) (y - getPaddingTop() + 0.5f); + final int paddedY = y - getPaddingTop(); if (paddedY < headerHeight || paddedY >= mPaddedHeight) { return -1; } @@ -619,47 +729,97 @@ class SimpleMonthView extends View { /** * Calculates the bounds of the specified day. * - * @param day the day of the month + * @param id the day of the month, or an item identifier * @param outBounds the rect to populate with bounds */ - private boolean getBoundsForDay(int day, Rect outBounds) { - if (day < 1 || day > mDaysInMonth) { + private boolean getBoundsForItem(int id, Rect outBounds) { + if (mNextEnabled && id == ITEM_ID_NEXT) { + if (mNextDrawable != null) { + outBounds.set(mNextHitArea); + return true; + } + } else if (mPrevEnabled && id == ITEM_ID_PREV) { + if (mPrevDrawable != null) { + outBounds.set(mPrevHitArea); + return true; + } + } + + if (id < 1 || id > mDaysInMonth) { return false; } - final int index = day - 1 + findDayOffset(); - final int row = index / DAYS_IN_WEEK; + final int index = id - 1 + findDayOffset(); + + // Compute left edge. final int col = index % DAYS_IN_WEEK; + final int colWidth = mPaddedWidth / DAYS_IN_WEEK; + final int left = getPaddingLeft() + col * colWidth; + // Compute top edge. + final int row = index / DAYS_IN_WEEK; + final int rowHeight = mDayHeight; 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)); + final int top = getPaddingTop() + headerHeight + row * rowHeight; + outBounds.set(left, top, left + colWidth, top + rowHeight); return true; } /** + * Called when an item is clicked. + * + * @param id the day number or item identifier + */ + private boolean onItemClicked(int id, boolean animate) { + return onNavigationClicked(id, animate) || onDayClicked(id); + } + + /** * 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 */ - private void onDayClicked(int day) { + private boolean onDayClicked(int day) { + if (day < 0 || day > mDaysInMonth) { + return false; + } + if (mOnDayClickListener != null) { - Calendar date = Calendar.getInstance(); + final Calendar date = Calendar.getInstance(); date.set(mYear, mMonth, day); mOnDayClickListener.onDayClick(this, date); } // This is a no-op if accessibility is turned off. mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED); + return true; + } + + /** + * Called when the user clicks on a navigation button. Handles callbacks to + * the {@link OnDayClickListener} if one is set. + * + * @param id the item identifier + */ + private boolean onNavigationClicked(int id, boolean animate) { + final int direction; + if (id == ITEM_ID_NEXT) { + direction = 1; + } else if (id == ITEM_ID_PREV) { + direction = -1; + } else { + return false; + } + + if (mOnDayClickListener != null) { + mOnDayClickListener.onNavigationClick(this, direction, animate); + } + + // This is a no-op if accessibility is turned off. + mTouchHelper.sendEventForVirtualView(id, AccessibilityEvent.TYPE_VIEW_CLICKED); + return true; } /** @@ -678,7 +838,7 @@ class SimpleMonthView extends View { @Override protected int getVirtualViewAt(float x, float y) { - final int day = getDayAtLocation(x, y); + final int day = getItemAtLocation((int) (x + 0.5f), (int) (y + 0.5f)); if (day >= 0) { return day; } @@ -687,6 +847,14 @@ class SimpleMonthView extends View { @Override protected void getVisibleVirtualViews(IntArray virtualViewIds) { + if (mNextEnabled && mNextDrawable != null) { + virtualViewIds.add(ITEM_ID_PREV); + } + + if (mPrevEnabled && mPrevDrawable != null) { + virtualViewIds.add(ITEM_ID_NEXT); + } + for (int day = 1; day <= mDaysInMonth; day++) { virtualViewIds.add(day); } @@ -699,7 +867,7 @@ class SimpleMonthView extends View { @Override protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) { - final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect); + final boolean hasBounds = getBoundsForItem(virtualViewId, mTempRect); if (!hasBounds) { // The day is invalid, kill the node. @@ -710,12 +878,14 @@ class SimpleMonthView extends View { return; } + node.setText(getItemText(virtualViewId)); node.setContentDescription(getItemDescription(virtualViewId)); node.setBoundsInParent(mTempRect); node.addAction(AccessibilityAction.ACTION_CLICK); if (virtualViewId == mActivatedDay) { - node.setSelected(true); + // TODO: This should use activated once that's supported. + node.setChecked(true); } } @@ -725,31 +895,45 @@ class SimpleMonthView extends View { Bundle arguments) { switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: - onDayClicked(virtualViewId); - return true; + return onItemClicked(virtualViewId, false); } return false; } /** - * 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. + * Generates a description for a given virtual view. * - * @param day The day to generate a description for - * @return A description of the time object + * @param id the day or item identifier to generate a description for + * @return a description of the virtual view */ - private CharSequence getItemDescription(int day) { - mTempCalendar.set(mYear, mMonth, day); - final CharSequence date = DateFormat.format(DATE_FORMAT, - mTempCalendar.getTimeInMillis()); + private CharSequence getItemDescription(int id) { + if (id == ITEM_ID_NEXT) { + return mNextContentDesc; + } else if (id == ITEM_ID_PREV) { + return mPrevContentDesc; + } else if (id >= 1 && id <= mDaysInMonth) { + mTempCalendar.set(mYear, mMonth, id); + return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis()); + } - if (day == mActivatedDay) { - return getContext().getString(R.string.item_is_selected, date); + return ""; + } + + /** + * Generates displayed text for a given virtual view. + * + * @param id the day or item identifier to generate text for + * @return the visible text of the virtual view + */ + private CharSequence getItemText(int id) { + if (id == ITEM_ID_NEXT || id == ITEM_ID_PREV) { + return null; + } else if (id >= 1 && id <= mDaysInMonth) { + return Integer.toString(id); } - return date; + return null; } } @@ -758,5 +942,6 @@ class SimpleMonthView extends View { */ public interface OnDayClickListener { public void onDayClick(SimpleMonthView view, Calendar day); + public void onNavigationClick(SimpleMonthView view, int direction, boolean animate); } } diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java index 7bd502e..7182414 100644 --- a/core/java/android/widget/YearPickerView.java +++ b/core/java/android/widget/YearPickerView.java @@ -111,16 +111,12 @@ class YearPickerView extends ListView { mAdapter.setRange(min, max); } - public void setYearTextAppearance(int resId) { - mAdapter.setItemTextAppearance(resId); - } - - 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 static final int ITEM_TEXT_APPEARANCE = + R.style.TextAppearance_Material_DatePicker_List_YearLabel; + private static final int ITEM_TEXT_ACTIVATED_APPEARANCE = + R.style.TextAppearance_Material_DatePicker_List_YearLabel_Activated; private final LayoutInflater mInflater; @@ -128,9 +124,6 @@ class YearPickerView extends ListView { private int mMinYear; private int mCount; - private int mItemTextAppearanceResId; - private int mItemActivatedTextAppearanceResId; - public YearAdapter(Context context) { mInflater = LayoutInflater.from(context); } @@ -155,16 +148,6 @@ class YearPickerView extends ListView { return false; } - public void setItemTextAppearance(int resId) { - mItemTextAppearanceResId = resId; - notifyDataSetChanged(); - } - - public void setItemActivatedTextAppearance(int resId) { - mItemActivatedTextAppearanceResId = resId; - notifyDataSetChanged(); - } - @Override public int getCount() { return mCount; @@ -203,10 +186,10 @@ class YearPickerView extends ListView { final boolean activated = mActivatedYear == year; final int textAppearanceResId; - if (activated && mItemActivatedTextAppearanceResId != 0) { - textAppearanceResId = mItemActivatedTextAppearanceResId; + if (activated && ITEM_TEXT_ACTIVATED_APPEARANCE != 0) { + textAppearanceResId = ITEM_TEXT_ACTIVATED_APPEARANCE; } else { - textAppearanceResId = mItemTextAppearanceResId; + textAppearanceResId = ITEM_TEXT_APPEARANCE; } final TextView v = (TextView) convertView; |