Loading core/java/android/widget/CalendarViewMaterialDelegate.java +2 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.widget; import android.annotation.StyleRes; import android.content.Context; import android.util.AttributeSet; import android.widget.DayPickerView.OnDaySelectedListener; import java.util.Calendar; Loading Loading @@ -109,8 +110,7 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele mOnDateChangeListener = listener; } private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener = new DayPickerView.OnDaySelectedListener() { private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() { @Override public void onDaySelected(DayPickerView view, Calendar day) { if (mOnDateChangeListener != null) { Loading core/java/android/widget/DatePickerCalendarDelegate.java +1 −3 Original line number Diff line number Diff line Loading @@ -34,8 +34,6 @@ 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; Loading Loading @@ -549,7 +547,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { final int listPosition = ss.getListPosition(); if (listPosition != -1) { if (currentView == VIEW_MONTH_DAY) { mDayPickerView.setCurrentItem(listPosition); mDayPickerView.setPosition(listPosition); } else if (currentView == VIEW_YEAR) { final int listPositionOffset = ss.getListPositionOffset(); mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset); Loading core/java/android/widget/DayPickerAdapter.java→core/java/android/widget/DayPickerPagerAdapter.java +6 −15 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ import java.util.Calendar; /** * An adapter for a list of {@link android.widget.SimpleMonthView} items. */ class DayPickerAdapter extends PagerAdapter { class DayPickerPagerAdapter extends PagerAdapter { private static final int MONTHS_IN_YEAR = 12; private final Calendar mMinDate = Calendar.getInstance(); Loading @@ -63,7 +63,7 @@ class DayPickerAdapter extends PagerAdapter { private int mCount; private int mFirstDayOfWeek; public DayPickerAdapter(@NonNull Context context, @LayoutRes int layoutResId, public DayPickerPagerAdapter(@NonNull Context context, @LayoutRes int layoutResId, @IdRes int calendarViewId) { mInflater = LayoutInflater.from(context); mLayoutResId = layoutResId; Loading Loading @@ -200,7 +200,8 @@ class DayPickerAdapter extends PagerAdapter { 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; final int position = yearOffset * MONTHS_IN_YEAR + monthOffset; return position; } @Override Loading Loading @@ -253,8 +254,6 @@ class DayPickerAdapter extends PagerAdapter { v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek, enabledDayRangeStart, enabledDayRangeEnd); v.setPrevEnabled(position > 0); v.setNextEnabled(position < mCount - 1); final ViewHolder holder = new ViewHolder(position, itemView, v); mItems.put(position, holder); Loading Loading @@ -298,17 +297,10 @@ class DayPickerAdapter extends PagerAdapter { setSelectedDay(day); if (mOnDaySelectedListener != null) { mOnDaySelectedListener.onDaySelected(DayPickerAdapter.this, day); mOnDaySelectedListener.onDaySelected(DayPickerPagerAdapter.this, day); } } } @Override public void onNavigationClick(SimpleMonthView view, int direction, boolean animate) { if (mOnDaySelectedListener != null) { mOnDaySelectedListener.onNavigationClick(DayPickerAdapter.this, direction, animate); } } }; private static class ViewHolder { Loading @@ -324,7 +316,6 @@ class DayPickerAdapter extends PagerAdapter { } public interface OnDaySelectedListener { public void onDaySelected(DayPickerAdapter view, Calendar day); public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate); public void onDaySelected(DayPickerPagerAdapter view, Calendar day); } } core/java/android/widget/DayPickerView.java +156 −109 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * 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. Loading @@ -16,37 +16,44 @@ package android.widget; import com.android.internal.widget.ViewPager; import com.android.internal.R; import com.android.internal.widget.ViewPager; import com.android.internal.widget.ViewPager.OnPageChangeListener; import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.MathUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; import java.util.ArrayList; 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 ViewPager { class DayPickerView extends ViewGroup { private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material; private static final int DEFAULT_START_YEAR = 1900; private static final int DEFAULT_END_YEAR = 2100; private static final int[] ATTRS_TEXT_COLOR = new int[] { R.attr.textColor }; private final Calendar mSelectedDay = Calendar.getInstance(); private final Calendar mMinDate = Calendar.getInstance(); private final Calendar mMaxDate = Calendar.getInstance(); private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); private final AccessibilityManager mAccessibilityManager; private final ViewPager mViewPager; private final ImageButton mPrevButton; private final ImageButton mNextButton; private final DayPickerAdapter mAdapter; private final DayPickerPagerAdapter mAdapter; /** Temporary calendar used for date calculations. */ private Calendar mTempCalendar; Loading @@ -57,17 +64,21 @@ class DayPickerView extends ViewPager { this(context, null); } public DayPickerView(Context context, AttributeSet attrs) { public DayPickerView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, R.attr.calendarViewStyle); } public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr) { public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mAccessibilityManager = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CalendarView, defStyleAttr, defStyleRes); Loading @@ -93,14 +104,44 @@ class DayPickerView extends ViewPager { a.recycle(); // Set up adapter. mAdapter = new DayPickerAdapter(context, mAdapter = new DayPickerPagerAdapter(context, R.layout.date_picker_month_item_material, R.id.month_view); mAdapter.setMonthTextAppearance(monthTextAppearanceResId); mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId); mAdapter.setDayTextAppearance(dayTextAppearanceResId); mAdapter.setDaySelectorColor(daySelectorColor); setAdapter(mAdapter); final LayoutInflater inflater = LayoutInflater.from(context); final ViewGroup content = (ViewGroup) inflater.inflate(DEFAULT_LAYOUT, this, false); // Transfer all children from content to here. while (content.getChildCount() > 0) { final View child = content.getChildAt(0); content.removeViewAt(0); addView(child); } mPrevButton = (ImageButton) findViewById(R.id.prev); mPrevButton.setOnClickListener(mOnClickListener); mNextButton = (ImageButton) findViewById(R.id.next); mNextButton.setOnClickListener(mOnClickListener); mViewPager = (ViewPager) findViewById(R.id.day_picker_view_pager); mViewPager.setAdapter(mAdapter); mViewPager.setOnPageChangeListener(mOnPageChangedListener); // Proxy the month text color into the previous and next buttons. if (monthTextAppearanceResId != 0) { final TypedArray ta = mContext.obtainStyledAttributes(null, ATTRS_TEXT_COLOR, 0, monthTextAppearanceResId); final ColorStateList monthColor = ta.getColorStateList(0); if (monthColor != null) { mPrevButton.setImageTintList(monthColor); mNextButton.setImageTintList(monthColor); } ta.recycle(); } // Set up min and max dates. final Calendar tempDate = Calendar.getInstance(); Loading @@ -127,109 +168,68 @@ class DayPickerView extends ViewPager { setDate(setDateMillis, false); // Proxy selection callbacks to our own listener. mAdapter.setOnDaySelectedListener(new DayPickerAdapter.OnDaySelectedListener() { mAdapter.setOnDaySelectedListener(new DayPickerPagerAdapter.OnDaySelectedListener() { @Override public void onDaySelected(DayPickerAdapter adapter, Calendar day) { public void onDaySelected(DayPickerPagerAdapter adapter, Calendar day) { if (mOnDaySelectedListener != null) { 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); } }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { populate(); final ViewPager viewPager = mViewPager; measureChild(viewPager, widthMeasureSpec, heightMeasureSpec); // Everything below is mostly copied from FrameLayout. int count = getChildCount(); final int measuredWidthAndState = viewPager.getMeasuredWidthAndState(); final int measuredHeightAndState = viewPager.getMeasuredHeightAndState(); setMeasuredDimension(measuredWidthAndState, measuredHeightAndState); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } final int pagerWidth = viewPager.getMeasuredWidth(); final int pagerHeight = viewPager.getMeasuredHeight(); final int buttonWidthSpec = MeasureSpec.makeMeasureSpec(pagerWidth, MeasureSpec.AT_MOST); final int buttonHeightSpec = MeasureSpec.makeMeasureSpec(pagerHeight, MeasureSpec.AT_MOST); mPrevButton.measure(buttonWidthSpec, buttonHeightSpec); mNextButton.measure(buttonWidthSpec, buttonHeightSpec); } } // Account for padding too maxWidth += getPaddingLeft() + getPaddingRight(); maxHeight += getPaddingTop() + getPaddingBottom(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec; final int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } mMatchParentChildren.clear(); @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { final ImageButton leftButton = mPrevButton; final ImageButton rightButton = mNextButton; final int width = right - left; final int height = bottom - top; mViewPager.layout(0, 0, width, height); if (mViewPager.getChildCount() < 1) { leftButton.setVisibility(View.INVISIBLE); rightButton.setVisibility(View.INVISIBLE); return; } final SimpleMonthView monthView = (SimpleMonthView) mViewPager.getChildAt(0); final int monthHeight = monthView.getMonthHeight(); final int cellWidth = monthView.getCellWidth(); // Vertically center the previous/next buttons within the month // header, horizontally center within the day cell. final int leftDW = leftButton.getMeasuredWidth(); final int leftDH = leftButton.getMeasuredHeight(); final int leftIconTop = monthView.getPaddingTop() + (monthHeight - leftDH) / 2; final int leftIconLeft = monthView.getPaddingLeft() + (cellWidth - leftDW) / 2; leftButton.layout(leftIconLeft, leftIconTop, leftIconLeft + leftDW, leftIconTop + leftDH); leftButton.setVisibility(View.VISIBLE); final int rightDW = rightButton.getMeasuredWidth(); final int rightDH = rightButton.getMeasuredHeight(); final int rightIconTop = monthView.getPaddingTop() + (monthHeight - rightDH) / 2; final int rightIconRight = width - monthView.getPaddingRight() - (cellWidth - rightDW) / 2; rightButton.layout(rightIconRight - rightDW, rightIconTop, rightIconRight, rightIconTop + rightDH); rightButton.setVisibility(View.VISIBLE); } public void setDayOfWeekTextAppearance(int resId) { Loading Loading @@ -284,8 +284,8 @@ class DayPickerView extends ViewPager { } final int position = getPositionFromDay(timeInMillis); if (position != getCurrentItem()) { setCurrentItem(position, animate); if (position != mViewPager.getCurrentItem()) { mViewPager.setCurrentItem(position, animate); } mTempCalendar.setTimeInMillis(timeInMillis); Loading Loading @@ -365,10 +365,57 @@ class DayPickerView extends ViewPager { * Gets the position of the view that is most prominently displayed within the list view. */ public int getMostVisiblePosition() { return getCurrentItem(); return mViewPager.getCurrentItem(); } public void setPosition(int position) { mViewPager.setCurrentItem(position, false); } private final OnPageChangeListener mOnPageChangedListener = new OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { final float alpha = Math.abs(0.5f - positionOffset) * 2.0f; mPrevButton.setAlpha(alpha); mNextButton.setAlpha(alpha); } @Override public void onPageScrollStateChanged(int state) {} @Override public void onPageSelected(int position) { mPrevButton.setVisibility( position > 0 ? View.VISIBLE : View.INVISIBLE); mNextButton.setVisibility( position < (mAdapter.getCount() - 1) ? View.VISIBLE : View.INVISIBLE); } }; private final OnClickListener mOnClickListener = new OnClickListener() { @Override public void onClick(View v) { final int direction; if (v == mPrevButton) { direction = -1; } else if (v == mNextButton) { direction = 1; } else { return; } // Animation is expensive for accessibility services since it sends // lots of scroll and content change events. final boolean animate = !mAccessibilityManager.isEnabled(); // ViewPager clamps input values, so we don't need to worry // about passing invalid indices. final int nextItem = mViewPager.getCurrentItem() + direction; mViewPager.setCurrentItem(nextItem, animate); } }; public interface OnDaySelectedListener { public void onDaySelected(DayPickerView view, Calendar day); void onDaySelected(DayPickerView view, Calendar day); } } core/java/android/widget/DayPickerViewPager.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * 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.ViewPager; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import java.util.ArrayList; /** * This displays a list of months in a calendar format with selectable days. */ class DayPickerViewPager extends ViewPager { private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); public DayPickerViewPager(Context context) { this(context, null); } public DayPickerViewPager(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { populate(); // Everything below is mostly copied from FrameLayout. int count = getChildCount(); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } } } // Account for padding too maxWidth += getPaddingLeft() + getPaddingRight(); maxHeight += getPaddingTop() + getPaddingBottom(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec; final int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } mMatchParentChildren.clear(); } } Loading
core/java/android/widget/CalendarViewMaterialDelegate.java +2 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package android.widget; import android.annotation.StyleRes; import android.content.Context; import android.util.AttributeSet; import android.widget.DayPickerView.OnDaySelectedListener; import java.util.Calendar; Loading Loading @@ -109,8 +110,7 @@ class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDele mOnDateChangeListener = listener; } private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener = new DayPickerView.OnDaySelectedListener() { private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() { @Override public void onDaySelected(DayPickerView view, Calendar day) { if (mOnDateChangeListener != null) { Loading
core/java/android/widget/DatePickerCalendarDelegate.java +1 −3 Original line number Diff line number Diff line Loading @@ -34,8 +34,6 @@ 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; Loading Loading @@ -549,7 +547,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { final int listPosition = ss.getListPosition(); if (listPosition != -1) { if (currentView == VIEW_MONTH_DAY) { mDayPickerView.setCurrentItem(listPosition); mDayPickerView.setPosition(listPosition); } else if (currentView == VIEW_YEAR) { final int listPositionOffset = ss.getListPositionOffset(); mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset); Loading
core/java/android/widget/DayPickerAdapter.java→core/java/android/widget/DayPickerPagerAdapter.java +6 −15 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ import java.util.Calendar; /** * An adapter for a list of {@link android.widget.SimpleMonthView} items. */ class DayPickerAdapter extends PagerAdapter { class DayPickerPagerAdapter extends PagerAdapter { private static final int MONTHS_IN_YEAR = 12; private final Calendar mMinDate = Calendar.getInstance(); Loading @@ -63,7 +63,7 @@ class DayPickerAdapter extends PagerAdapter { private int mCount; private int mFirstDayOfWeek; public DayPickerAdapter(@NonNull Context context, @LayoutRes int layoutResId, public DayPickerPagerAdapter(@NonNull Context context, @LayoutRes int layoutResId, @IdRes int calendarViewId) { mInflater = LayoutInflater.from(context); mLayoutResId = layoutResId; Loading Loading @@ -200,7 +200,8 @@ class DayPickerAdapter extends PagerAdapter { 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; final int position = yearOffset * MONTHS_IN_YEAR + monthOffset; return position; } @Override Loading Loading @@ -253,8 +254,6 @@ class DayPickerAdapter extends PagerAdapter { v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek, enabledDayRangeStart, enabledDayRangeEnd); v.setPrevEnabled(position > 0); v.setNextEnabled(position < mCount - 1); final ViewHolder holder = new ViewHolder(position, itemView, v); mItems.put(position, holder); Loading Loading @@ -298,17 +297,10 @@ class DayPickerAdapter extends PagerAdapter { setSelectedDay(day); if (mOnDaySelectedListener != null) { mOnDaySelectedListener.onDaySelected(DayPickerAdapter.this, day); mOnDaySelectedListener.onDaySelected(DayPickerPagerAdapter.this, day); } } } @Override public void onNavigationClick(SimpleMonthView view, int direction, boolean animate) { if (mOnDaySelectedListener != null) { mOnDaySelectedListener.onNavigationClick(DayPickerAdapter.this, direction, animate); } } }; private static class ViewHolder { Loading @@ -324,7 +316,6 @@ class DayPickerAdapter extends PagerAdapter { } public interface OnDaySelectedListener { public void onDaySelected(DayPickerAdapter view, Calendar day); public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate); public void onDaySelected(DayPickerPagerAdapter view, Calendar day); } }
core/java/android/widget/DayPickerView.java +156 −109 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * 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. Loading @@ -16,37 +16,44 @@ package android.widget; import com.android.internal.widget.ViewPager; import com.android.internal.R; import com.android.internal.widget.ViewPager; import com.android.internal.widget.ViewPager.OnPageChangeListener; import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.MathUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; import java.util.ArrayList; 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 ViewPager { class DayPickerView extends ViewGroup { private static final int DEFAULT_LAYOUT = R.layout.day_picker_content_material; private static final int DEFAULT_START_YEAR = 1900; private static final int DEFAULT_END_YEAR = 2100; private static final int[] ATTRS_TEXT_COLOR = new int[] { R.attr.textColor }; private final Calendar mSelectedDay = Calendar.getInstance(); private final Calendar mMinDate = Calendar.getInstance(); private final Calendar mMaxDate = Calendar.getInstance(); private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); private final AccessibilityManager mAccessibilityManager; private final ViewPager mViewPager; private final ImageButton mPrevButton; private final ImageButton mNextButton; private final DayPickerAdapter mAdapter; private final DayPickerPagerAdapter mAdapter; /** Temporary calendar used for date calculations. */ private Calendar mTempCalendar; Loading @@ -57,17 +64,21 @@ class DayPickerView extends ViewPager { this(context, null); } public DayPickerView(Context context, AttributeSet attrs) { public DayPickerView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, R.attr.calendarViewStyle); } public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr) { public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { public DayPickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mAccessibilityManager = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CalendarView, defStyleAttr, defStyleRes); Loading @@ -93,14 +104,44 @@ class DayPickerView extends ViewPager { a.recycle(); // Set up adapter. mAdapter = new DayPickerAdapter(context, mAdapter = new DayPickerPagerAdapter(context, R.layout.date_picker_month_item_material, R.id.month_view); mAdapter.setMonthTextAppearance(monthTextAppearanceResId); mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId); mAdapter.setDayTextAppearance(dayTextAppearanceResId); mAdapter.setDaySelectorColor(daySelectorColor); setAdapter(mAdapter); final LayoutInflater inflater = LayoutInflater.from(context); final ViewGroup content = (ViewGroup) inflater.inflate(DEFAULT_LAYOUT, this, false); // Transfer all children from content to here. while (content.getChildCount() > 0) { final View child = content.getChildAt(0); content.removeViewAt(0); addView(child); } mPrevButton = (ImageButton) findViewById(R.id.prev); mPrevButton.setOnClickListener(mOnClickListener); mNextButton = (ImageButton) findViewById(R.id.next); mNextButton.setOnClickListener(mOnClickListener); mViewPager = (ViewPager) findViewById(R.id.day_picker_view_pager); mViewPager.setAdapter(mAdapter); mViewPager.setOnPageChangeListener(mOnPageChangedListener); // Proxy the month text color into the previous and next buttons. if (monthTextAppearanceResId != 0) { final TypedArray ta = mContext.obtainStyledAttributes(null, ATTRS_TEXT_COLOR, 0, monthTextAppearanceResId); final ColorStateList monthColor = ta.getColorStateList(0); if (monthColor != null) { mPrevButton.setImageTintList(monthColor); mNextButton.setImageTintList(monthColor); } ta.recycle(); } // Set up min and max dates. final Calendar tempDate = Calendar.getInstance(); Loading @@ -127,109 +168,68 @@ class DayPickerView extends ViewPager { setDate(setDateMillis, false); // Proxy selection callbacks to our own listener. mAdapter.setOnDaySelectedListener(new DayPickerAdapter.OnDaySelectedListener() { mAdapter.setOnDaySelectedListener(new DayPickerPagerAdapter.OnDaySelectedListener() { @Override public void onDaySelected(DayPickerAdapter adapter, Calendar day) { public void onDaySelected(DayPickerPagerAdapter adapter, Calendar day) { if (mOnDaySelectedListener != null) { 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); } }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { populate(); final ViewPager viewPager = mViewPager; measureChild(viewPager, widthMeasureSpec, heightMeasureSpec); // Everything below is mostly copied from FrameLayout. int count = getChildCount(); final int measuredWidthAndState = viewPager.getMeasuredWidthAndState(); final int measuredHeightAndState = viewPager.getMeasuredHeightAndState(); setMeasuredDimension(measuredWidthAndState, measuredHeightAndState); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } final int pagerWidth = viewPager.getMeasuredWidth(); final int pagerHeight = viewPager.getMeasuredHeight(); final int buttonWidthSpec = MeasureSpec.makeMeasureSpec(pagerWidth, MeasureSpec.AT_MOST); final int buttonHeightSpec = MeasureSpec.makeMeasureSpec(pagerHeight, MeasureSpec.AT_MOST); mPrevButton.measure(buttonWidthSpec, buttonHeightSpec); mNextButton.measure(buttonWidthSpec, buttonHeightSpec); } } // Account for padding too maxWidth += getPaddingLeft() + getPaddingRight(); maxHeight += getPaddingTop() + getPaddingBottom(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec; final int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } mMatchParentChildren.clear(); @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { final ImageButton leftButton = mPrevButton; final ImageButton rightButton = mNextButton; final int width = right - left; final int height = bottom - top; mViewPager.layout(0, 0, width, height); if (mViewPager.getChildCount() < 1) { leftButton.setVisibility(View.INVISIBLE); rightButton.setVisibility(View.INVISIBLE); return; } final SimpleMonthView monthView = (SimpleMonthView) mViewPager.getChildAt(0); final int monthHeight = monthView.getMonthHeight(); final int cellWidth = monthView.getCellWidth(); // Vertically center the previous/next buttons within the month // header, horizontally center within the day cell. final int leftDW = leftButton.getMeasuredWidth(); final int leftDH = leftButton.getMeasuredHeight(); final int leftIconTop = monthView.getPaddingTop() + (monthHeight - leftDH) / 2; final int leftIconLeft = monthView.getPaddingLeft() + (cellWidth - leftDW) / 2; leftButton.layout(leftIconLeft, leftIconTop, leftIconLeft + leftDW, leftIconTop + leftDH); leftButton.setVisibility(View.VISIBLE); final int rightDW = rightButton.getMeasuredWidth(); final int rightDH = rightButton.getMeasuredHeight(); final int rightIconTop = monthView.getPaddingTop() + (monthHeight - rightDH) / 2; final int rightIconRight = width - monthView.getPaddingRight() - (cellWidth - rightDW) / 2; rightButton.layout(rightIconRight - rightDW, rightIconTop, rightIconRight, rightIconTop + rightDH); rightButton.setVisibility(View.VISIBLE); } public void setDayOfWeekTextAppearance(int resId) { Loading Loading @@ -284,8 +284,8 @@ class DayPickerView extends ViewPager { } final int position = getPositionFromDay(timeInMillis); if (position != getCurrentItem()) { setCurrentItem(position, animate); if (position != mViewPager.getCurrentItem()) { mViewPager.setCurrentItem(position, animate); } mTempCalendar.setTimeInMillis(timeInMillis); Loading Loading @@ -365,10 +365,57 @@ class DayPickerView extends ViewPager { * Gets the position of the view that is most prominently displayed within the list view. */ public int getMostVisiblePosition() { return getCurrentItem(); return mViewPager.getCurrentItem(); } public void setPosition(int position) { mViewPager.setCurrentItem(position, false); } private final OnPageChangeListener mOnPageChangedListener = new OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { final float alpha = Math.abs(0.5f - positionOffset) * 2.0f; mPrevButton.setAlpha(alpha); mNextButton.setAlpha(alpha); } @Override public void onPageScrollStateChanged(int state) {} @Override public void onPageSelected(int position) { mPrevButton.setVisibility( position > 0 ? View.VISIBLE : View.INVISIBLE); mNextButton.setVisibility( position < (mAdapter.getCount() - 1) ? View.VISIBLE : View.INVISIBLE); } }; private final OnClickListener mOnClickListener = new OnClickListener() { @Override public void onClick(View v) { final int direction; if (v == mPrevButton) { direction = -1; } else if (v == mNextButton) { direction = 1; } else { return; } // Animation is expensive for accessibility services since it sends // lots of scroll and content change events. final boolean animate = !mAccessibilityManager.isEnabled(); // ViewPager clamps input values, so we don't need to worry // about passing invalid indices. final int nextItem = mViewPager.getCurrentItem() + direction; mViewPager.setCurrentItem(nextItem, animate); } }; public interface OnDaySelectedListener { public void onDaySelected(DayPickerView view, Calendar day); void onDaySelected(DayPickerView view, Calendar day); } }
core/java/android/widget/DayPickerViewPager.java 0 → 100644 +137 −0 Original line number Diff line number Diff line /* * 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.ViewPager; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import java.util.ArrayList; /** * This displays a list of months in a calendar format with selectable days. */ class DayPickerViewPager extends ViewPager { private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); public DayPickerViewPager(Context context) { this(context, null); } public DayPickerViewPager(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public DayPickerViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { populate(); // Everything below is mostly copied from FrameLayout. int count = getChildCount(); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } } } // Account for padding too maxWidth += getPaddingLeft() + getPaddingRight(); maxHeight += getPaddingTop() + getPaddingBottom(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec; final int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } mMatchParentChildren.clear(); } }