package com.rey.material.widget; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.os.Build; import android.os.Handler; import android.os.SystemClock; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import com.rey.material.R; import com.rey.material.drawable.BlankDrawable; import com.rey.material.util.ThemeUtil; import com.rey.material.util.TypefaceUtil; import com.rey.material.util.ViewUtil; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; /** * Created by Rey on 12/31/2014. */ public class DatePicker extends ListView implements AbsListView.OnScrollListener{ private Typeface mTypeface; private int mTextSize; private int mTextColor; private int mTextLabelColor; private int mTextHighlightColor; private int mTextDisableColor; private int mSelectionColor; private int mAnimDuration; private Interpolator mInInterpolator; private Interpolator mOutInterpolator; private Paint mPaint; private float mDayBaseWidth; private float mDayBaseHeight; private float mDayHeight; private float mDayWidth; private int mDayPadding; private float mSelectionRadius; private int mMonthRealWidth; private int mMonthRealHeight; private Calendar mCalendar; private int mFirstDayOfWeek; private String[] mLabels = new String[7]; private static String[] mDayTexts; private MonthAdapter mAdapter; /** * Interface definition for a callback to be invoked when the selected date is changed. */ public interface OnDateChangedListener { /** * Called when the selected date is changed. * @param oldDay The day value of old date. * @param oldMonth The month value of old date. * @param oldYear The year value of old date. * @param newDay The day value of new date. * @param newMonth The month value of new date. * @param newYear The year value of new date. */ public void onDateChanged(int oldDay, int oldMonth, int oldYear, int newDay, int newMonth, int newYear); } private OnDateChangedListener mOnDateChangedListener; protected static final int SCROLL_DURATION = 250; protected static final int SCROLL_CHANGE_DELAY = 40; protected static final int LIST_TOP_OFFSET = -1; protected Handler mHandler = new Handler(); protected int mCurrentScrollState = 0; protected long mPreviousScrollPosition; protected int mPreviousScrollState = 0; protected float mFriction = 1.0F; protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(); private int mPaddingLeft; private int mPaddingTop; private int mPaddingRight; private int mPaddingBottom; private static final String DAY_FORMAT = "%2d"; private static final String YEAR_FORMAT = "%4d"; public DatePicker(Context context) { super(context); init(context, null, 0, 0); } public DatePicker(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, 0, 0); } public DatePicker(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs); init(context, attrs, defStyleAttr, 0); } public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs); init(context, attrs, defStyleAttr, defStyleRes); } private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ setWillNotDraw(false); setSelector(BlankDrawable.getInstance()); setCacheColorHint(0); setDivider(null); setItemsCanFocus(true); setFastScrollEnabled(false); setVerticalScrollBarEnabled(false); setOnScrollListener(this); setFadingEdgeLength(0); setFrictionIfSupported(ViewConfiguration.getScrollFriction() * mFriction); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); mPaint.setTextAlign(Paint.Align.CENTER); mDayPadding = ThemeUtil.dpToPx(context, 4); mCalendar = Calendar.getInstance(); mFirstDayOfWeek = mCalendar.getFirstDayOfWeek(); int index = mCalendar.get(Calendar.DAY_OF_WEEK) - 1; DateFormat format = new SimpleDateFormat(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 ? "EEEEE" : "E"); for(int i = 0; i < 7; i++){ mLabels[index] = format.format(mCalendar.getTime()); index = (index + 1) % 7; mCalendar.add(Calendar.DAY_OF_MONTH, 1); } mAdapter = new MonthAdapter(); setAdapter(mAdapter); applyStyle(context, attrs, defStyleAttr, defStyleRes); } public void applyStyle(int resId){ applyStyle(getContext(), null, 0, resId); } private void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker, defStyleAttr, defStyleRes); mTextSize = a.getDimensionPixelSize(R.styleable.DatePicker_dp_dayTextSize, context.getResources().getDimensionPixelOffset(R.dimen.abc_text_size_caption_material)); mTextColor = a.getColor(R.styleable.DatePicker_dp_textColor, 0xFF000000); mTextHighlightColor = a.getColor(R.styleable.DatePicker_dp_textHighlightColor, 0xFFFFFFFF); mTextLabelColor = a.getColor(R.styleable.DatePicker_dp_textLabelColor, 0xFF767676); mTextDisableColor = a.getColor(R.styleable.DatePicker_dp_textDisableColor, 0xFF767676); mSelectionColor = a.getColor(R.styleable.DatePicker_dp_selectionColor, ThemeUtil.colorPrimary(context, 0xFF000000)); mAnimDuration = a.getInteger(R.styleable.DatePicker_dp_animDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime)); int resId = a.getResourceId(R.styleable.DatePicker_dp_inInterpolator, 0); if(resId != 0) mInInterpolator = AnimationUtils.loadInterpolator(context, resId); else mInInterpolator = new DecelerateInterpolator(); resId = a.getResourceId(R.styleable.DatePicker_dp_outInterpolator, 0); if(resId != 0) mOutInterpolator = AnimationUtils.loadInterpolator(context, resId); else mOutInterpolator = new DecelerateInterpolator(); String familyName = a.getString(R.styleable.DatePicker_dp_fontFamily); int style = a.getInteger(R.styleable.DatePicker_dp_textStyle, Typeface.NORMAL); mTypeface = TypefaceUtil.load(context, familyName, style); int padding = a.getDimensionPixelSize(R.styleable.DatePicker_android_padding, -1); if(padding >= 0) setContentPadding(padding, padding, padding, padding); mPaddingLeft = a.getDimensionPixelSize(R.styleable.DatePicker_android_paddingLeft, mPaddingLeft); mPaddingTop = a.getDimensionPixelSize(R.styleable.DatePicker_android_paddingTop, mPaddingTop); mPaddingRight = a.getDimensionPixelSize(R.styleable.DatePicker_android_paddingRight, mPaddingRight); mPaddingBottom = a.getDimensionPixelSize(R.styleable.DatePicker_android_paddingBottom, mPaddingBottom); a.recycle(); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void setFrictionIfSupported(float friction) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) setFriction(friction); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { MonthView child = (MonthView) view.getChildAt(0); if (child == null) return; // Figure out where we are mPreviousScrollPosition = getFirstVisiblePosition() * child.getHeight() - child.getBottom(); mPreviousScrollState = mCurrentScrollState; } @Override public void onScrollStateChanged(AbsListView absListView, int scroll) { mScrollStateChangedRunnable.doScrollStateChange(absListView, scroll); } private void measureBaseSize(){ mPaint.setTextSize(mTextSize); mPaint.setTypeface(mTypeface); mDayBaseWidth = mPaint.measureText("88", 0, 2) + mDayPadding * 2; Rect bounds = new Rect(); mPaint.getTextBounds("88", 0, 2 ,bounds); mDayBaseHeight = bounds.height(); } private void measureMonthView(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); measureBaseSize(); int size = Math.round(Math.max(mDayBaseWidth, mDayBaseHeight)); int width = size * 7 + mPaddingLeft + mPaddingRight; int height = Math.round(size * 7 + mDayBaseHeight + mDayPadding * 2 + mPaddingTop + mPaddingBottom); switch (widthMode){ case MeasureSpec.AT_MOST: width = Math.min(width, widthSize); break; case MeasureSpec.EXACTLY: width = widthSize; break; } switch (heightMode){ case MeasureSpec.AT_MOST: height = Math.min(height, heightSize); break; case MeasureSpec.EXACTLY: height = heightSize; break; } mMonthRealWidth = width; mMonthRealHeight = height; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureMonthView(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { mDayWidth = (w - mPaddingLeft - mPaddingRight) / 7f; mDayHeight = (h - mDayBaseHeight - mDayPadding * 2 - mPaddingTop - mPaddingBottom) / 7f; mSelectionRadius = Math.min(mDayWidth, mDayHeight) / 2f; } @Override public void setPadding(int left, int top, int right, int bottom) { super.setPadding(0, 0, 0, 0); } public void setContentPadding(int left, int top, int right, int bottom){ mPaddingLeft = left; mPaddingTop = top; mPaddingRight = right; mPaddingBottom = bottom; } private String getDayText(int day){ if(mDayTexts == null){ synchronized (DatePicker.class){ if(mDayTexts == null) mDayTexts = new String[31]; } } if(mDayTexts[day - 1] == null) mDayTexts[day - 1] = String.format(DAY_FORMAT, day); return mDayTexts[day - 1]; } /** * Set the range of selectable dates. * @param minDay The day value of minimum date. * @param minMonth The month value of minimum date. * @param minYear The year value of minimum date. * @param maxDay The day value of maximum date. * @param maxMonth The month value of maximum date. * @param maxYear The year value of maximum date. */ public void setDateRange(int minDay, int minMonth, int minYear, int maxDay, int maxMonth, int maxYear){ mAdapter.setDateRange(minDay, minMonth, minYear, maxDay, maxMonth, maxYear); } /** * Jump to the view of a specific month. * @param month * @param year */ public void goTo(int month, int year){ int position = mAdapter.positionOfMonth(month, year); postSetSelectionFromTop(position, 0); } public void postSetSelectionFromTop(final int position, final int offset) { post(new Runnable() { @Override public void run() { setSelectionFromTop(position, offset); requestLayout(); } }); } /** * Set the selected date of this DatePicker. * @param day The day value of selected date. * @param month The month value of selected date. * @param year The year value of selected date. */ public void setDate(int day, int month, int year){ if(mAdapter.getYear() == year && mAdapter.getMonth() == month && mAdapter.getDay() == day) return; mAdapter.setDate(day, month, year, false); goTo(month, year); } /** * Set the listener will be called when the selected date is changed. * @param listener The {@link DatePicker.OnDateChangedListener} will be called. */ public void setOnDateChangedListener(OnDateChangedListener listener){ mOnDateChangedListener = listener; } /** * @return The day value of selected date. */ public int getDay(){ return mAdapter.getDay(); } /** * @return The month value of selected date. */ public int getMonth(){ return mAdapter.getMonth(); } /** * @return The year value of selected date. */ public int getYear(){ return mAdapter.getYear(); } /** * Get the formatted string of selected date. * @param formatter The DateFormat used to format the date. * @return */ public String getFormattedDate(DateFormat formatter){ mCalendar.set(Calendar.YEAR, mAdapter.getYear()); mCalendar.set(Calendar.MONTH, mAdapter.getMonth()); mCalendar.set(Calendar.DAY_OF_MONTH, mAdapter.getDay()); return formatter.format(mCalendar.getTime()); } public int getSelectionColor(){ return mSelectionColor; } public int getTextSize(){ return mTextSize; } public Typeface getTypeface(){ return mTypeface; } public int getTextColor(){ return mTextColor; } public int getTextLabelColor(){ return mTextLabelColor; } public int getTextHighlightColor(){ return mTextHighlightColor; } public int getTextDisableColor(){ return mTextDisableColor; } public Calendar getCalendar(){ return mCalendar; } private class ScrollStateRunnable implements Runnable { 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) { mHandler.removeCallbacks(this); mNewState = scrollState; mHandler.postDelayed(this, SCROLL_CHANGE_DELAY); } @Override 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 != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { mPreviousScrollState = mNewState; int i = 0; View child = getChildAt(i); while(child != null && child.getBottom() <= 0) child = getChildAt(++i); if (child == null) return; int firstPosition = getFirstVisiblePosition(); int lastPosition = getLastVisiblePosition(); boolean scroll = firstPosition != 0 && lastPosition != getCount() - 1; final int top = child.getTop(); final int bottom = child.getBottom(); final int midpoint = getHeight() / 2; if (scroll && top < LIST_TOP_OFFSET) { if (bottom > midpoint) smoothScrollBy(top, SCROLL_DURATION); else smoothScrollBy(bottom, SCROLL_DURATION); } } else mPreviousScrollState = mNewState; } } private class MonthView extends View { private long mStartTime; private float mAnimProgress; private boolean mRunning; private int mTouchedDay = -1; private int mMonth; private int mYear; private int mMaxDay; private int mFirstDayCol; private int mMinAvailDay = -1; private int mMaxAvailDay = -1; private int mToday = -1; private int mSelectedDay = -1; private int mPreviousSelectedDay = -1; private String mMonthText; public MonthView(Context context) { super(context); setWillNotDraw(false); } public void setMonth(int month, int year){ if(mMonth != month || mYear != year){ mMonth = month; mYear = year; calculateMonthView(); invalidate(); } } public void setSelectedDay(int day, boolean animation){ if(mSelectedDay != day){ mPreviousSelectedDay = mSelectedDay; mSelectedDay = day; if(animation) startAnimation(); else invalidate(); } } public void setToday(int day){ if(mToday != day){ mToday = day; invalidate(); } } public void setAvailableDay(int min, int max){ if(mMinAvailDay != min || mMaxAvailDay != max){ mMinAvailDay = min; mMaxAvailDay = max; invalidate(); } } private void calculateMonthView(){ mCalendar.set(Calendar.DAY_OF_MONTH, 1); mCalendar.set(Calendar.MONTH, mMonth); mCalendar.set(Calendar.YEAR, mYear); mMaxDay = mCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); int dayOfWeek = mCalendar.get(Calendar.DAY_OF_WEEK); mFirstDayCol = dayOfWeek < mFirstDayOfWeek ? dayOfWeek + 7 - mFirstDayOfWeek : dayOfWeek - mFirstDayOfWeek; mMonthText = mCalendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()) + " " + String.format(YEAR_FORMAT, mYear); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(mMonthRealWidth, mMonthRealHeight); } @Override protected void onDraw(Canvas canvas) { //draw month text mPaint.setTextSize(mTextSize); mPaint.setTypeface(mTypeface); float x = 3.5f * mDayWidth + getPaddingLeft(); float y = mDayPadding * 2 + mDayBaseHeight + getPaddingTop(); mPaint.setFakeBoldText(true); mPaint.setColor(mTextColor); canvas.drawText(mMonthText, x, y, mPaint); //draw selection float paddingLeft = getPaddingLeft(); float paddingTop = mDayPadding * 2 + mDayBaseHeight + getPaddingTop(); if(mSelectedDay > 0){ int col = (mFirstDayCol + mSelectedDay - 1) % 7; int row = (mFirstDayCol + mSelectedDay - 1) / 7 + 1; x = (col + 0.5f) * mDayWidth + paddingLeft; y = (row + 0.5f) * mDayHeight + paddingTop; float radius = mRunning ? mInInterpolator.getInterpolation(mAnimProgress) * mSelectionRadius : mSelectionRadius; mPaint.setColor(mSelectionColor); canvas.drawCircle(x, y, radius, mPaint); } if(mRunning && mPreviousSelectedDay > 0){ int col = (mFirstDayCol + mPreviousSelectedDay - 1) % 7; int row = (mFirstDayCol + mPreviousSelectedDay - 1) / 7 + 1; x = (col + 0.5f) * mDayWidth + paddingLeft; y = (row + 0.5f) * mDayHeight + paddingTop; float radius = (1f - mOutInterpolator.getInterpolation(mAnimProgress)) * mSelectionRadius; mPaint.setColor(mSelectionColor); canvas.drawCircle(x, y, radius, mPaint); } //draw label mPaint.setFakeBoldText(false); mPaint.setColor(mTextLabelColor); paddingTop += (mDayHeight + mDayBaseHeight) / 2f; for(int i = 0; i < 7; i++){ x = (i + 0.5f) * mDayWidth + paddingLeft; y = paddingTop; int index = (i + mFirstDayOfWeek - 1) % 7; canvas.drawText(mLabels[index], x, y, mPaint); } //draw date text int col = mFirstDayCol; int row = 1; int maxDay = mMaxAvailDay > 0 ? Math.min(mMaxAvailDay, mMaxDay) : mMaxDay; for(int day = 1; day <= mMaxDay; day++){ if(day == mSelectedDay) mPaint.setColor(mTextHighlightColor); else if(day < mMinAvailDay || day > maxDay) mPaint.setColor(mTextDisableColor); else if(day == mToday) mPaint.setColor(mSelectionColor); else mPaint.setColor(mTextColor); x = (col + 0.5f) * mDayWidth + paddingLeft; y = row * mDayHeight + paddingTop; canvas.drawText(getDayText(day), x, y, mPaint); col++; if(col == 7) { col = 0; row++; } } } private int getTouchedDay(float x, float y){ float paddingTop = mDayPadding * 2 + mDayBaseHeight + getPaddingTop() + mDayHeight; if(x < getPaddingLeft() || x > getWidth() - getPaddingRight() || y < paddingTop || y > getHeight() - getPaddingBottom()) return -1; int col = (int)Math.floor((x - getPaddingLeft()) / mDayWidth); int row = (int)Math.floor((y - paddingTop) / mDayHeight); int maxDay = mMaxAvailDay > 0 ? Math.min(mMaxAvailDay, mMaxDay) : mMaxDay; int day = row * 7 + col - mFirstDayCol + 1; if(day < 0 || day < mMinAvailDay || day > maxDay) return -1; return day; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mTouchedDay = getTouchedDay(event.getX(), event.getY()); return true; case MotionEvent.ACTION_UP: if(getTouchedDay(event.getX(), event.getY()) == mTouchedDay && mTouchedDay > 0) { mAdapter.setDate(mTouchedDay, mMonth, mYear, true); mTouchedDay = -1; } return true; case MotionEvent.ACTION_CANCEL: mTouchedDay = -1; return true; } return true; } private void resetAnimation(){ mStartTime = SystemClock.uptimeMillis(); mAnimProgress = 0f; } private void startAnimation() { if(getHandler() != null){ resetAnimation(); mRunning = true; getHandler().postAtTime(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); } invalidate(); } private void stopAnimation() { mRunning = false; mAnimProgress = 1f; if(getHandler() != null) getHandler().removeCallbacks(mUpdater); invalidate(); } private final Runnable mUpdater = new Runnable() { @Override public void run() { update(); } }; private void update(){ long curTime = SystemClock.uptimeMillis(); mAnimProgress = Math.min(1f, (float)(curTime - mStartTime) / mAnimDuration); if(mAnimProgress == 1f) stopAnimation(); if(mRunning) { if(getHandler() != null) getHandler().postAtTime(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); else stopAnimation(); } invalidate(); } } private class MonthAdapter extends BaseAdapter{ private int mDay = -1; private int mMonth = -1; private int mYear = -1; private int mMinDay = -1; private int mMinMonth = -1; private int mMinYear = -1; private int mMaxDay = -1; private int mMaxMonth = -1; private int mMaxYear = -1; private int mToday; private int mTodayMonth; private int mTodayYear; private int mMinMonthValue; private int mMaxMonthValue; public void setDateRange(int minDay, int minMonth, int minYear, int maxDay, int maxMonth, int maxYear){ int minMonthValue = minDay < 0 || minMonth < 0 || minYear < 0 ? 0 : minYear * 12 + minMonth; int maxMonthValue = maxDay < 0 || maxMonth < 0 || maxYear < 0 ? Integer.MAX_VALUE - 1: maxYear * 12 + maxMonth; if(minDay != mMinDay || mMinMonthValue != minMonthValue || maxDay != mMaxDay || mMaxMonthValue != maxMonthValue){ mMinDay = minDay; mMinMonth = minMonth; mMinYear = minYear; mMaxDay = maxDay; mMaxMonth = maxMonth; mMaxYear = maxYear; mMinMonthValue = minMonthValue; mMaxMonthValue = maxMonthValue; notifyDataSetChanged(); } } public void setDate(int day, int month, int year, boolean animation){ if(mMonth != month || mYear != year) { MonthView v = (MonthView)getChildAt(positionOfMonth(mMonth, mYear) - getFirstVisiblePosition()); if (v != null) v.setSelectedDay(-1, false); int oldDay = mDay; int oldMonth = mMonth; int oldYear = mYear; mDay = day; mMonth = month; mYear = year; v = (MonthView)getChildAt(positionOfMonth(mMonth, mYear) - getFirstVisiblePosition()); if(v != null) v.setSelectedDay(mDay, animation); if(mOnDateChangedListener != null) mOnDateChangedListener.onDateChanged(oldDay, oldMonth, oldYear, mDay, mMonth, mYear); } else if(day != mDay){ int oldDay = mDay; mDay = day; MonthView v = (MonthView)getChildAt(positionOfMonth(mMonth, mYear) - getFirstVisiblePosition()); if(v != null) v.setSelectedDay(mDay, animation); if(mOnDateChangedListener != null) mOnDateChangedListener.onDateChanged(oldDay, mMonth, mYear, mDay, mMonth, mYear); } } public int positionOfMonth(int month, int year){ return year * 12 + month - mMinMonthValue; } public int getDay(){ return mDay; } public int getMonth(){ return mMonth; } public int getYear(){ return mYear; } private void calToday(){ mCalendar.setTimeInMillis(System.currentTimeMillis()); mToday = mCalendar.get(Calendar.DAY_OF_MONTH); mTodayMonth = mCalendar.get(Calendar.MONTH); mTodayYear = mCalendar.get(Calendar.YEAR); } @Override public int getCount() { return mMaxMonthValue - mMinMonthValue + 1; } @Override public Object getItem(int position) { return position + mMinMonthValue; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { MonthView v = (MonthView)convertView; if(v == null){ v = new MonthView(parent.getContext()); v.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom); } calToday(); int monthValue = (Integer)getItem(position); int year = monthValue / 12; int month = monthValue % 12; int minDay = month == mMinMonth && year == mMinYear ? mMinDay : -1; int maxDay = month == mMaxMonth && year == mMaxYear ? mMaxDay : -1; int today = mTodayMonth == month && mTodayYear == year ? mToday : -1; int day = month == mMonth && year == mYear ? mDay : -1; v.setMonth(month, year); v.setToday(today); v.setAvailableDay(minDay, maxDay); v.setSelectedDay(day, false); return v; } } }