package com.zenkun.datetimepicker.date; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Typeface; import android.text.format.DateUtils; import android.text.format.Time; import android.view.MotionEvent; import android.view.View; import java.security.InvalidParameterException; import java.text.DateFormatSymbols; import java.util.Calendar; import java.util.Formatter; import java.util.HashMap; import java.util.Locale; import com.android.datetimepicker.R; import com.zenkun.datetimepicker.Utils; public class SimpleMonthView extends View { protected static int DAY_SELECTED_CIRCLE_SIZE; protected static int DAY_SEPARATOR_WIDTH = 1; protected static int DEFAULT_HEIGHT = 32; protected static int MINI_DAY_NUMBER_TEXT_SIZE; protected static int MIN_HEIGHT = 10; protected static int MONTH_DAY_LABEL_TEXT_SIZE; protected static int MONTH_HEADER_SIZE; protected static int MONTH_LABEL_TEXT_SIZE; protected static float mScale = 0.0F; private final Calendar mCalendar; private final Calendar mDayLabelCalendar; private int mDayOfWeekStart = 0; private String mDayOfWeekTypeface; protected int mDayTextColor; protected int mFirstJulianDay = -1; protected int mFirstMonth = -1; private final Formatter mFormatter; protected boolean mHasToday = false; protected int mLastMonth = -1; protected int mMonth; protected Paint mMonthDayLabelPaint; protected Paint mMonthNumPaint; protected int mMonthTitleBGColor; protected Paint mMonthTitleBGPaint; protected int mMonthTitleColor; protected Paint mMonthTitlePaint; private String mMonthTitleTypeface; protected int mNumCells = this.mNumDays; protected int mNumDays = 7; private int mNumRows = 6; private OnDayClickListener mOnDayClickListener; protected int mPadding = 0; protected int mRowHeight = DEFAULT_HEIGHT; protected Paint mSelectedCirclePaint; protected int mSelectedDay = -1; protected int mSelectedLeft = -1; protected int mSelectedRight = -1; private final StringBuilder mStringBuilder; protected int mToday = -1; protected int mTodayNumberColor; protected int mWeekStart = 1; protected int mWidth; protected int mYear; private DateFormatSymbols mDateFormatSymbols = new DateFormatSymbols(); public SimpleMonthView(Context context) { super(context); Resources resources = context.getResources(); this.mDayLabelCalendar = Calendar.getInstance(); this.mCalendar = Calendar.getInstance(); this.mDayOfWeekTypeface = resources.getString(R.string.day_of_week_label_typeface); this.mMonthTitleTypeface = resources.getString(R.string.sans_serif); this.mDayTextColor = resources.getColor(R.color.date_picker_text_normal); this.mTodayNumberColor = resources.getColor(R.color.blue); this.mMonthTitleColor = resources.getColor(R.color.white); this.mMonthTitleBGColor = resources.getColor(R.color.circle_background); this.mStringBuilder = new StringBuilder(50); this.mFormatter = new Formatter(this.mStringBuilder, Locale.getDefault()); MINI_DAY_NUMBER_TEXT_SIZE = resources.getDimensionPixelSize(R.dimen.day_number_size); MONTH_LABEL_TEXT_SIZE = resources.getDimensionPixelSize(R.dimen.month_label_size); MONTH_DAY_LABEL_TEXT_SIZE = resources.getDimensionPixelSize(R.dimen.month_day_label_text_size); MONTH_HEADER_SIZE = resources.getDimensionPixelOffset(R.dimen.month_list_item_header_height); DAY_SELECTED_CIRCLE_SIZE = resources.getDimensionPixelSize(R.dimen.day_number_select_circle_radius); this.mRowHeight = ((resources.getDimensionPixelOffset(R.dimen.date_picker_view_animator_height) - MONTH_HEADER_SIZE) / 6); initView(); } private int calculateNumRows() { int dayOffset = findDayOffset(); int nbRows = (dayOffset + this.mNumCells) / this.mNumDays; int plusOne = 0; if ((dayOffset + this.mNumCells) % this.mNumDays > 0) { plusOne = 1; } return plusOne + nbRows; } private void drawMonthDayLabels(Canvas canvas) { int y = MONTH_HEADER_SIZE - MONTH_DAY_LABEL_TEXT_SIZE / 2; int space = (this.mWidth - 2 * this.mPadding) / (2 * this.mNumDays); for (int day = 0; day < this.mNumDays; day++) { int dayOfWeek = (day + this.mWeekStart) % this.mNumDays; int x = space * (1 + day * 2) + this.mPadding; this.mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek); canvas.drawText(mDateFormatSymbols.getShortWeekdays()[this.mDayLabelCalendar.get(Calendar.DAY_OF_WEEK)].toUpperCase(Locale.getDefault()), x, y, this.mMonthDayLabelPaint); } } private void drawMonthTitle(Canvas canvas) { int x = (this.mWidth + 2 * this.mPadding) / 2; int y = (MONTH_HEADER_SIZE - MONTH_DAY_LABEL_TEXT_SIZE) / 2 + MONTH_LABEL_TEXT_SIZE / 3; canvas.drawText(getMonthAndYearString(), x, y, this.mMonthTitlePaint); } private int findDayOffset() { int off; if (this.mDayOfWeekStart < this.mWeekStart) { off = this.mDayOfWeekStart + this.mNumDays; } else { off = this.mDayOfWeekStart; } return off - this.mWeekStart; } private String getMonthAndYearString() { this.mStringBuilder.setLength(0); long dateInMillis = this.mCalendar.getTimeInMillis(); return DateUtils.formatDateRange(getContext(), dateInMillis, dateInMillis, 52).toString(); } private void onDayClick(SimpleMonthAdapter.CalendarDay calendarDay) { if (this.mOnDayClickListener != null) this.mOnDayClickListener.onDayClick(this, calendarDay); } private boolean sameDay(int monthDay, Time time) { return (this.mYear == time.year) && (this.mMonth == time.month) && (monthDay == time.monthDay); } protected void drawMonthNums(Canvas canvas) { int y = (this.mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH + MONTH_HEADER_SIZE; int paddingDay = (this.mWidth - 2 * this.mPadding) / (2 * this.mNumDays); int dayOffset = findDayOffset(); int day = 1; while (day <= this.mNumCells) { int x = paddingDay * (1 + dayOffset * 2) + this.mPadding; if (this.mSelectedDay == day) canvas.drawCircle(x, y - MINI_DAY_NUMBER_TEXT_SIZE / 3, DAY_SELECTED_CIRCLE_SIZE, this.mSelectedCirclePaint); if ((this.mHasToday) && (this.mToday == day)) this.mMonthNumPaint.setColor(this.mTodayNumberColor); else this.mMonthNumPaint.setColor(this.mDayTextColor); canvas.drawText(String.format("%d", day), x, y, this.mMonthNumPaint); dayOffset++; if (dayOffset == this.mNumDays) { dayOffset = 0; y += this.mRowHeight; } day++; } } public SimpleMonthAdapter.CalendarDay getDayFromLocation(float x, float y) { int padding = this.mPadding; if ((x < padding) || (x > this.mWidth - this.mPadding)) { return null; } ; int yDay = (int) (y - MONTH_HEADER_SIZE) / this.mRowHeight; int day = 1 + ((int) ((x - padding) * this.mNumDays / (this.mWidth - padding - this.mPadding)) - findDayOffset()) + yDay * this.mNumDays; return new SimpleMonthAdapter.CalendarDay(this.mYear, this.mMonth, day); } protected void initView() { this.mMonthTitlePaint = new Paint(); this.mMonthTitlePaint.setFakeBoldText(true); this.mMonthTitlePaint.setAntiAlias(true); this.mMonthTitlePaint.setTextSize(MONTH_LABEL_TEXT_SIZE); this.mMonthTitlePaint.setTypeface(Typeface.create(this.mMonthTitleTypeface, 1)); this.mMonthTitlePaint.setColor(this.mDayTextColor); this.mMonthTitlePaint.setTextAlign(Paint.Align.CENTER); this.mMonthTitlePaint.setStyle(Paint.Style.FILL); this.mMonthTitleBGPaint = new Paint(); this.mMonthTitleBGPaint.setFakeBoldText(true); this.mMonthTitleBGPaint.setAntiAlias(true); this.mMonthTitleBGPaint.setColor(this.mMonthTitleBGColor); this.mMonthTitleBGPaint.setTextAlign(Paint.Align.CENTER); this.mMonthTitleBGPaint.setStyle(Paint.Style.FILL); this.mSelectedCirclePaint = new Paint(); this.mSelectedCirclePaint.setFakeBoldText(true); this.mSelectedCirclePaint.setAntiAlias(true); this.mSelectedCirclePaint.setColor(this.mTodayNumberColor); this.mSelectedCirclePaint.setTextAlign(Paint.Align.CENTER); this.mSelectedCirclePaint.setStyle(Paint.Style.FILL); this.mSelectedCirclePaint.setAlpha(60); this.mMonthDayLabelPaint = new Paint(); this.mMonthDayLabelPaint.setAntiAlias(true); this.mMonthDayLabelPaint.setTextSize(MONTH_DAY_LABEL_TEXT_SIZE); this.mMonthDayLabelPaint.setColor(this.mDayTextColor); this.mMonthDayLabelPaint.setTypeface(Typeface.create(this.mDayOfWeekTypeface, 0)); this.mMonthDayLabelPaint.setStyle(Paint.Style.FILL); this.mMonthDayLabelPaint.setTextAlign(Paint.Align.CENTER); this.mMonthDayLabelPaint.setFakeBoldText(true); this.mMonthNumPaint = new Paint(); this.mMonthNumPaint.setAntiAlias(true); this.mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); this.mMonthNumPaint.setStyle(Paint.Style.FILL); this.mMonthNumPaint.setTextAlign(Paint.Align.CENTER); this.mMonthNumPaint.setFakeBoldText(false); } protected void onDraw(Canvas canvas) { drawMonthTitle(canvas); drawMonthDayLabels(canvas); drawMonthNums(canvas); } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(View.MeasureSpec.getSize(widthMeasureSpec), this.mRowHeight * this.mNumRows + MONTH_HEADER_SIZE); } protected void onSizeChanged(int w, int h, int oldw, int oldh) { this.mWidth = w; } public boolean onTouchEvent(MotionEvent motionEvent) { if (motionEvent.getAction() == MotionEvent.ACTION_UP) { SimpleMonthAdapter.CalendarDay calendarDay = getDayFromLocation(motionEvent.getX(), motionEvent.getY()); if (calendarDay != null) onDayClick(calendarDay); } return true; } public void reuse() { this.mNumRows = 6; requestLayout(); } public void setMonthParams(HashMap<String, Integer> monthParams) { if ((!monthParams.containsKey("month")) && (!monthParams.containsKey("year"))) throw new InvalidParameterException("You must specify the month and year for this view"); setTag(monthParams); if (monthParams.containsKey("height")) { this.mRowHeight = ((Integer) monthParams.get("height")).intValue(); if (this.mRowHeight < MIN_HEIGHT) this.mRowHeight = MIN_HEIGHT; } if (monthParams.containsKey("selected_day")) this.mSelectedDay = ((Integer) monthParams.get("selected_day")).intValue(); this.mMonth = ((Integer) monthParams.get("month")).intValue(); this.mYear = ((Integer) monthParams.get("year")).intValue(); Time time = new Time(Time.getCurrentTimezone()); time.setToNow(); this.mHasToday = false; this.mToday = -1; this.mCalendar.set(Calendar.MONTH, this.mMonth); this.mCalendar.set(Calendar.YEAR, this.mYear); this.mCalendar.set(Calendar.DAY_OF_MONTH, 1); this.mDayOfWeekStart = this.mCalendar.get(Calendar.DAY_OF_WEEK); if (monthParams.containsKey("week_start")) { this.mWeekStart = ((Integer) monthParams.get("week_start")).intValue(); } else { this.mWeekStart = this.mCalendar.getFirstDayOfWeek(); } this.mNumCells = Utils.getDaysInMonth(this.mMonth, this.mYear); for (int day = 0; day < this.mNumCells; day++) { int monthDay = day + 1; if (sameDay(monthDay, time)) { this.mHasToday = true; this.mToday = monthDay; } } this.mNumRows = calculateNumRows(); } public void setOnDayClickListener(OnDayClickListener onDayClickListener) { this.mOnDayClickListener = onDayClickListener; } public static abstract interface OnDayClickListener { public abstract void onDayClick(SimpleMonthView simpleMonthView, SimpleMonthAdapter.CalendarDay calendarDay); } }