Java tutorial
/* * Copyright (C) 2014 The Android Open Source Project * Copyright 2015 Vikram Kakkar * * 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 com.miuhouse.yourcompany.student.view.widget.date.datepicker; import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; import android.os.Build; import android.os.Bundle; import android.support.v4.view.ViewCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.support.v4.widget.ExploreByTouchHelper; import android.text.TextPaint; import android.text.format.DateFormat; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import android.widget.TextView; import com.miuhouse.yourcompany.student.R; import com.miuhouse.yourcompany.student.utils.L; import com.miuhouse.yourcompany.student.view.widget.date.hepers.DateTimePatternHelper; import com.miuhouse.yourcompany.student.view.widget.date.utilities.Config; import com.miuhouse.yourcompany.student.view.widget.date.utilities.SUtils; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Locale; /** * A calendar-like view displaying a specified month and the appropriate selectable day numbers * within the specified month. */ public class SimpleMonthView extends View { private static final String TAG = SimpleMonthView.class.getSimpleName(); private static final int DAYS_IN_WEEK = 7; private static final int MAX_WEEKS_IN_MONTH = 6; private static final int DEFAULT_SELECTED_DAY = -1; private static final int DEFAULT_WEEK_START = Calendar.SUNDAY; private static final String DEFAULT_TITLE_FORMAT = "MMMMy"; private static final String DAY_OF_WEEK_FORMAT; @SuppressWarnings("FieldCanBeLocal") private final int DRAW_RECT = 0; @SuppressWarnings("FieldCanBeLocal") private final int DRAW_RECT_WITH_CURVE_ON_LEFT = 1; @SuppressWarnings("FieldCanBeLocal") private final int DRAW_RECT_WITH_CURVE_ON_RIGHT = 2; static { // Deals with the change in usage of `EEEEE` pattern. // See method `SimpleDateFormat#appendDayOfWeek(...)` for more details. if (SUtils.isApi_18_OrHigher()) { DAY_OF_WEEK_FORMAT = "EEEEE"; } else { DAY_OF_WEEK_FORMAT = "E"; } } private final TextPaint mMonthPaint = new TextPaint(); private final TextPaint mDayOfWeekPaint = new TextPaint(); private final TextPaint mDayPaint = new TextPaint(); private final Paint mDaySelectorPaint = new Paint(); private final Paint mDayHighlightPaint = new Paint(); private final Paint mDayRangeSelectorPaint = new Paint(); private final Calendar mCalendar = Calendar.getInstance(); private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance(); // private MonthViewTouchHelper mTouchHelper; private SimpleDateFormat mTitleFormatter; private SimpleDateFormat mDayOfWeekFormatter; private NumberFormat mDayFormatter; // Desired dimensions. private int mDesiredMonthHeight; private int mDesiredDayOfWeekHeight; private int mDesiredDayHeight; private int mDesiredCellWidth; private int mDesiredDaySelectorRadius; private CharSequence mTitle; private int mMonth; private int mYear; // Dimensions as laid out. private int mMonthHeight; private int mDayOfWeekHeight; private int mDayHeight; private int mCellWidth; private int mDaySelectorRadius; private int mPaddedWidth; private int mPaddedHeight; /** * The day of month for the selected day, or -1 if no day is selected. */ // private int mActivatedDay = -1; private final ActivatedDays mActivatedDays = new ActivatedDays(); /** * The day of month for today, or -1 if the today is not in the current * month. */ private int mToday = DEFAULT_SELECTED_DAY; /** * The first day of the week (ex. Calendar.SUNDAY). */ private int mWeekStart = DEFAULT_WEEK_START; /** * The number of days (ex. 28) in the current month. */ private int mDaysInMonth; /** * The day of week (ex. Calendar.SUNDAY) for the first day of the current * month. */ private int mDayOfWeekStart; /** * The day of month for the first (inclusive) enabled day. */ private int mEnabledDayStart = 1; /** * The day of month for the last (inclusive) enabled day. */ private int mEnabledDayEnd = 31; /** * Optional listener for handling day click actions. */ private OnDayClickListener mOnDayClickListener; private OnRangeClickListener mOnRangeClickListener; private ColorStateList mDayTextColor; // private int mTouchedItem = -1; private Context mContext; private int mTouchSlopSquared; private float mPaddingRangeIndicator; private boolean disable; private boolean rangeSelect; private int position; private boolean isFill; private ArrayList<Integer> calendars = new ArrayList<>(); public SimpleMonthView(Context context) { this(context, null); } public SimpleMonthView(Context context, AttributeSet attrs) { this(context, attrs, R.attr.spMonthViewStyle); } public SimpleMonthView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public SimpleMonthView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { mContext = getContext(); mTouchSlopSquared = ViewConfiguration.get(mContext).getScaledTouchSlop() * ViewConfiguration.get(mContext).getScaledTouchSlop(); final Resources res = mContext.getResources(); mDesiredMonthHeight = res.getDimensionPixelSize(R.dimen.sp_date_picker_month_height); mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.sp_date_picker_day_of_week_height); mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.sp_date_picker_day_height); mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.sp_date_picker_day_width); mDesiredDaySelectorRadius = res.getDimensionPixelSize(R.dimen.sp_date_picker_day_selector_radius); mPaddingRangeIndicator = res.getDimensionPixelSize(R.dimen.sp_month_view_range_padding); // Set up accessibility components. // mTouchHelper = new MonthViewTouchHelper(this); // ViewCompat.setAccessibilityDelegate(this, mTouchHelper); ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); final Locale locale = res.getConfiguration().locale; String titleFormat; if (SUtils.isApi_18_OrHigher()) { titleFormat = DateFormat.getBestDateTimePattern(locale, DEFAULT_TITLE_FORMAT); } else { titleFormat = DateTimePatternHelper.getBestDateTimePattern(locale, DateTimePatternHelper.PATTERN_MMMMy); } mTitleFormatter = new SimpleDateFormat(titleFormat, locale); mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale); mDayFormatter = NumberFormat.getIntegerInstance(locale); initPaints(res); } /** * Applies the specified text appearance resource to a paint, returning the * text color if one is set in the text appearance. * * @param p the paint to modify * @param resId the resource ID of the text appearance * @return the text color, if available */ private ColorStateList applyTextAppearance(Paint p, int resId) { // Workaround for inaccessible R.styleable.TextAppearance_* TextView tv = new TextView(mContext); if (SUtils.isApi_23_OrHigher()) { tv.setTextAppearance(resId); } else { //noinspection deprecation tv.setTextAppearance(mContext, resId); } p.setTypeface(tv.getTypeface()); p.setTextSize(tv.getTextSize()); final ColorStateList textColor = tv.getTextColors(); if (textColor != null) { final int enabledColor = textColor.getColorForState(ENABLED_STATE_SET, 0); p.setColor(enabledColor); } return textColor; } public int getMonthHeight() { return mMonthHeight; } public int getCellWidth() { return mCellWidth; } public void setMonthTextAppearance(int resId) { applyTextAppearance(mMonthPaint, resId); invalidate(); } public void setDayOfWeekTextAppearance(int resId) { applyTextAppearance(mDayOfWeekPaint, resId); invalidate(); } public void setDayTextAppearance(int resId) { final ColorStateList textColor = applyTextAppearance(mDayPaint, resId); if (textColor != null) { mDayTextColor = textColor; } invalidate(); } public CharSequence getTitle() { if (mTitle == null) { mTitle = mTitleFormatter.format(mCalendar.getTime()); } return mTitle; } public ActivatedDays getmActivatedDays() { return mActivatedDays; } public ArrayList<Integer> getCalendars() { return calendars; } /** * Sets up the text and style properties for painting. */ private void initPaints(Resources res) { final String monthTypeface = res.getString(R.string.sp_date_picker_month_typeface); final String dayOfWeekTypeface = res.getString(R.string.sp_date_picker_day_of_week_typeface); final String dayTypeface = res.getString(R.string.sp_date_picker_day_typeface); final int monthTextSize = res.getDimensionPixelSize(R.dimen.sp_date_picker_month_text_size); final int dayOfWeekTextSize = res.getDimensionPixelSize(R.dimen.sp_date_picker_day_of_week_text_size); final int dayTextSize = res.getDimensionPixelSize(R.dimen.sp_date_picker_day_text_size); mMonthPaint.setAntiAlias(true); mMonthPaint.setTextSize(monthTextSize); mMonthPaint.setTypeface(Typeface.create(monthTypeface, 0)); mMonthPaint.setTextAlign(Paint.Align.CENTER); mMonthPaint.setStyle(Paint.Style.FILL); mDayOfWeekPaint.setAntiAlias(true); mDayOfWeekPaint.setTextSize(dayOfWeekTextSize); mDayOfWeekPaint.setTypeface(Typeface.create(dayOfWeekTypeface, 0)); mDayOfWeekPaint.setTextAlign(Paint.Align.CENTER); mDayOfWeekPaint.setStyle(Paint.Style.FILL); mDaySelectorPaint.setAntiAlias(true); mDaySelectorPaint.setStyle(Paint.Style.FILL); mDayHighlightPaint.setAntiAlias(true); mDayHighlightPaint.setStyle(Paint.Style.FILL); mDayRangeSelectorPaint.setAntiAlias(true); mDayRangeSelectorPaint.setStyle(Paint.Style.FILL); mDayPaint.setAntiAlias(true); mDayPaint.setTextSize(dayTextSize); mDayPaint.setTypeface(Typeface.create(dayTypeface, 0)); mDayPaint.setTextAlign(Paint.Align.CENTER); mDayPaint.setStyle(Paint.Style.FILL); } void setMonthTextColor(ColorStateList monthTextColor) { final int enabledColor = monthTextColor.getColorForState(ENABLED_STATE_SET, 0); mMonthPaint.setColor(enabledColor); invalidate(); } void setDayOfWeekTextColor(ColorStateList dayOfWeekTextColor) { final int enabledColor = dayOfWeekTextColor.getColorForState(ENABLED_STATE_SET, 0); mDayOfWeekPaint.setColor(enabledColor); invalidate(); } void setDayTextColor(ColorStateList dayTextColor) { mDayTextColor = dayTextColor; invalidate(); } void setDaySelectorColor(ColorStateList dayBackgroundColor) { final int activatedColor = dayBackgroundColor .getColorForState(SUtils.resolveStateSet(SUtils.STATE_ENABLED | SUtils.STATE_ACTIVATED), 0); mDaySelectorPaint.setColor(activatedColor); mDayRangeSelectorPaint.setColor(activatedColor); mDayRangeSelectorPaint.setAlpha(100); invalidate(); } void setDayHighlightColor(ColorStateList dayHighlightColor) { final int pressedColor = dayHighlightColor .getColorForState(SUtils.resolveStateSet(SUtils.STATE_ENABLED | SUtils.STATE_PRESSED), 0); mDayHighlightPaint.setColor(pressedColor); invalidate(); } public void setOnDayClickListener(OnDayClickListener listener) { mOnDayClickListener = listener; } public void setOnRangeClickListener(OnRangeClickListener listener) { mOnRangeClickListener = listener; } // @Override // public boolean dispatchHoverEvent(MotionEvent event) { // // First right-of-refusal goes the touch exploration helper. // return mTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event); // } // private CheckForTap mPendingCheckForTap; private int mInitialTarget = -1; private int mDownX, mDownY; private boolean isStillAClick(int x, int y) { return (((x - mDownX) * (x - mDownX)) + ((y - mDownY) * (y - mDownY))) <= mTouchSlopSquared; } private boolean isDisable; @Override public boolean onTouchEvent(MotionEvent event) { final int x = (int) (event.getX() + 0.5f); final int y = (int) (event.getY() + 0.5f); final int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mDownX = x; mDownY = y; mInitialTarget = getDayAtLocation(mDownX, mDownY); // if (mInitialTarget < 0) { return false; } break; case MotionEvent.ACTION_MOVE: rangeSelect = true; if (!isStillAClick(x, y)) { mInitialTarget = -1; } break; case MotionEvent.ACTION_UP: rangeSelect = false; disable = false; isDisable = onDayClicked(mInitialTarget); // Fall through. case MotionEvent.ACTION_CANCEL: // mInitialTarget = -1; if (!isDisable) { invalidate(); } else { isDisable = false; } break; } return true; } @Override protected void onDraw(Canvas canvas) { Log.i("TAG", "onDraw"); if (Config.DEBUG) { Log.i(TAG, "onDraw(Canvas)"); } final int paddingLeft = getPaddingLeft(); final int paddingTop = getPaddingTop(); canvas.translate(paddingLeft, paddingTop); // drawMonth(canvas); drawDaysOfWeek(canvas); drawDays(canvas); canvas.translate(-paddingLeft, -paddingTop); } private void drawMonth(Canvas canvas) { final float x = mPaddedWidth / 2f; // Vertically centered within the month header height. final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent(); final float y = (mMonthHeight - lineHeight) / 2f; canvas.drawText(getTitle().toString(), x, y, mMonthPaint); } private void drawDaysOfWeek(Canvas canvas) { final TextPaint p = mDayOfWeekPaint; final int headerHeight = mMonthHeight; final int rowHeight = mDayOfWeekHeight; final int colWidth = mCellWidth; // 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 colCenterRtl; if (SUtils.isLayoutRtlCompat(this)) { colCenterRtl = mPaddedWidth - colCenter; } else { colCenterRtl = colCenter; } final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK; final String label = getDayOfWeekLabel(dayOfWeek); canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p); } } private String getDayOfWeekLabel(int dayOfWeek) { mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek); return mDayOfWeekFormatter.format(mDayOfWeekLabelCalendar.getTime()); } /** * Draws the month days. */ @SuppressWarnings("ConstantConditions") private void drawDays(Canvas canvas) { final TextPaint p = mDayPaint; final int headerHeight = mMonthHeight + mDayOfWeekHeight; final float rowHeight = mDayHeight; final float colWidth = mCellWidth; // Text is vertically centered within the row height. final float halfLineHeight = (p.ascent() + p.descent()) / 2f; // int rowCenter = headerHeight + rowHeight / 2; float rowCenter = headerHeight + rowHeight / 2f; //mDaysInMonth303129 for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) { final float colCenter = colWidth * col + colWidth / 2f; //final int colCenterRtl; final float colCenterRtl; if (SUtils.isLayoutRtlCompat(this)) { colCenterRtl = mPaddedWidth - colCenter; } else { colCenterRtl = colCenter; } int stateMask = 0; final boolean isDayEnabled = isDayEnabled(day); if (isDayEnabled) { stateMask |= SUtils.STATE_ENABLED; } final boolean isDayInActivatedRange = mActivatedDays.isValid() && mActivatedDays.isActivated(day); final boolean isSelected = mActivatedDays.isSelected(day); final boolean isSelectedAndTogether = mActivatedDays.isSelectedAndTogether(day); if (isSelectedAndTogether) { if ((!disable && !isDisable) || isFill) { stateMask |= SUtils.STATE_ACTIVATED; canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDaySelectorPaint); } } if (!isSelected && isDayInActivatedRange) { stateMask |= SUtils.STATE_ACTIVATED; int bgShape = DRAW_RECT; if (mActivatedDays.isSingleDay()) { if (mActivatedDays.isStartOfMonth()) { bgShape = DRAW_RECT_WITH_CURVE_ON_RIGHT; } else { bgShape = DRAW_RECT_WITH_CURVE_ON_LEFT; } } else if (mActivatedDays.isStartingDayOfRange(day)) { bgShape = DRAW_RECT_WITH_CURVE_ON_LEFT; } else if (mActivatedDays.isEndingDayOfRange(day)) { bgShape = DRAW_RECT_WITH_CURVE_ON_RIGHT; } // Use height to constrain the protrusion of the arc boolean constrainProtrusion = colWidth > (rowHeight - (2 * mPaddingRangeIndicator)); float horDistFromCenter = constrainProtrusion ? rowHeight / 2f - mPaddingRangeIndicator : colWidth / 2f; switch (bgShape) { case DRAW_RECT_WITH_CURVE_ON_LEFT: int leftRectArcLeft = (int) (colCenterRtl - horDistFromCenter) % 2 == 1 ? (int) (colCenterRtl - horDistFromCenter) + 1 : (int) (colCenterRtl - horDistFromCenter); int leftRectArcRight = (int) (colCenterRtl + horDistFromCenter) % 2 == 1 ? (int) (colCenterRtl + horDistFromCenter) + 1 : (int) (colCenterRtl + horDistFromCenter); RectF leftArcRect = new RectF(leftRectArcLeft, rowCenter - rowHeight / 2f + mPaddingRangeIndicator, leftRectArcRight, rowCenter + rowHeight / 2f - mPaddingRangeIndicator); canvas.drawArc(leftArcRect, 90, 180, true, mDayRangeSelectorPaint); canvas.drawRect(new RectF(leftArcRect.centerX(), rowCenter - rowHeight / 2f + mPaddingRangeIndicator, colCenterRtl + colWidth / 2f, rowCenter + rowHeight / 2f - mPaddingRangeIndicator), mDayRangeSelectorPaint); break; case DRAW_RECT_WITH_CURVE_ON_RIGHT: int rightRectArcLeft = (int) (colCenterRtl - horDistFromCenter) % 2 == 1 ? (int) (colCenterRtl - horDistFromCenter) + 1 : (int) (colCenterRtl - horDistFromCenter); int rightRectArcRight = (int) (colCenterRtl + horDistFromCenter) % 2 == 1 ? (int) (colCenterRtl + horDistFromCenter) + 1 : (int) (colCenterRtl + horDistFromCenter); RectF rightArcRect = new RectF(rightRectArcLeft, rowCenter - rowHeight / 2f + mPaddingRangeIndicator, rightRectArcRight, rowCenter + rowHeight / 2f - mPaddingRangeIndicator); canvas.drawArc(rightArcRect, 270, 180, true, mDayRangeSelectorPaint); canvas.drawRect(new RectF(colCenterRtl - colWidth / 2f, rowCenter - rowHeight / 2f + mPaddingRangeIndicator, rightArcRect.centerX(), rowCenter + rowHeight / 2f - mPaddingRangeIndicator), mDayRangeSelectorPaint); break; default: canvas.drawRect(new RectF(colCenterRtl - colWidth / 2f, rowCenter - rowHeight / 2f + mPaddingRangeIndicator, colCenterRtl + colWidth / 2f, rowCenter + rowHeight / 2f - mPaddingRangeIndicator), mDayRangeSelectorPaint); break; } } final boolean isDayToday = mToday == day; final int dayTextColor; if (isDayToday) { if (mActivatedDays.isClick && isSelected) { final int[] stateSet = SUtils.resolveStateSet(stateMask); dayTextColor = mDayTextColor.getColorForState(stateSet, 0); } else { dayTextColor = mDaySelectorPaint.getColor(); } } else { final int[] stateSet = SUtils.resolveStateSet(stateMask); if (!mActivatedDays.isSingleDay()) { if (stateMask == 1) { dayTextColor = mContext.getResources().getColor(R.color.textDarkfour); } else { dayTextColor = mDayTextColor.getColorForState(stateSet, 0); } } else { dayTextColor = mDayTextColor.getColorForState(stateSet, 0); } } p.setColor(dayTextColor); canvas.drawText(mDayFormatter.format(day), colCenterRtl, rowCenter - halfLineHeight, p); col++; if (col == DAYS_IN_WEEK) { col = 0; rowCenter += rowHeight; } if (mDaysInMonth == day) mOnRangeClickListener.onRangeSelected(calendars, position); } } private boolean isDayEnabled(int day) { return day >= mEnabledDayStart && day <= mEnabledDayEnd; } private boolean isValidDayOfMonth(int day) { return day >= 1 && day <= mDaysInMonth; } private static boolean isValidDayOfWeek(int day) { return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY; } private static boolean isValidMonth(int month) { return month >= Calendar.JANUARY && month <= Calendar.DECEMBER; } public void selectAllDays() { setSelectedDays(1, SUtils.getDaysInMonth(mMonth, mYear), SelectedDate.Type.RANGE, false); } public void setSelectedDays(int selectedDayStart, int selectedDayEnd, int togetherDay, SelectedDate.Type selectedDateType) { mActivatedDays.together = togetherDay; setSelectedDays(selectedDayStart, selectedDayEnd, selectedDateType, false); } public void setSelectedDays(int selectedDayStart, int selectedDayEnd, SelectedDate.Type selectedDateType, boolean isReset) { mActivatedDays.startingDay = selectedDayStart; mActivatedDays.endingDay = selectedDayEnd; mActivatedDays.selectedDateType = selectedDateType; mActivatedDays.isClick = true; // Invalidate cached accessibility information. if (isReset) { calendars.clear(); } // mTouchHelper.invalidateRoot(); invalidate(); } /** * Sets the first day of the week. * * @param weekStart which day the week should start on, valid values are * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY} */ public void setFirstDayOfWeek(int weekStart) { if (isValidDayOfWeek(weekStart)) { mWeekStart = weekStart; } else { mWeekStart = mCalendar.getFirstDayOfWeek(); } // Invalidate cached accessibility information. // mTouchHelper.invalidateRoot(); invalidate(); } /** * Sets all the parameters for displaying this week. * <p/> * Parameters have a default value and will only update if a new value is * included, except for focus month, which will always default to no focus * month if no value is passed in. The only required parameter is the week * start. * * @param month the month * @param year the year * @param weekStart which day the week should start on, valid values are * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY} * @param enabledDayStart the first enabled day * @param enabledDayEnd the last enabled day * @param selectedDayStart the start of the selected date range, or -1 for no selection * @param selectedDayEnd the end of the selected date range, or -1 for no selection * @param selectedDateType RANGE or SINGLE * <p/> * DayPickerPagerAdapter instantiateItem * disable true ????? */ void setMonthParams(int month, int year, int weekStart, int enabledDayStart, int enabledDayEnd, int selectedDayStart, int selectedDayEnd, SelectedDate.Type selectedDateType, boolean disable, int position) { this.position = position; setMonthParams(month, year, weekStart, enabledDayStart, enabledDayEnd, selectedDayStart, selectedDayEnd, selectedDateType, disable); } void setMonthParams(int month, int year, int weekStart, int enabledDayStart, int enabledDayEnd, int selectedDayStart, int selectedDayEnd, SelectedDate.Type selectedDateType, boolean disable) { if (isValidMonth(month)) { mMonth = month; } mYear = year; this.disable = disable; mCalendar.set(Calendar.MONTH, mMonth); mCalendar.set(Calendar.YEAR, mYear); mCalendar.set(Calendar.DAY_OF_MONTH, 1); mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK); if (isValidDayOfWeek(weekStart)) { mWeekStart = weekStart; } else { mWeekStart = mCalendar.getFirstDayOfWeek(); } // Figure out what day today is. final Calendar today = Calendar.getInstance(); mToday = -1; mDaysInMonth = SUtils.getDaysInMonth(mMonth, mYear); for (int i = 0; i < mDaysInMonth; i++) { final int day = i + 1; if (sameDay(day, today)) { mToday = day; } } mEnabledDayStart = SUtils.constrain(enabledDayStart, 1, mDaysInMonth); mEnabledDayEnd = SUtils.constrain(enabledDayEnd, mEnabledDayStart, mDaysInMonth); // Invalidate the old title. mTitle = null; mActivatedDays.startingDay = selectedDayStart; mActivatedDays.endingDay = selectedDayEnd; mActivatedDays.selectedDateType = selectedDateType; mActivatedDays.isClick = false; } public void setCalendar(List<Integer> mCalendars) { if (isFill) { disable = false; } rangeSelect = true; calendars.clear(); calendars.addAll(mCalendars); invalidate(); } public void setCalendar(List<Integer> mCalendars, boolean isFill) { Log.i("TAG", "mCalendars=" + mCalendars.size()); this.isFill = isFill; rangeSelect = true; calendars.clear(); calendars.addAll(mCalendars); invalidate(); } public void setCalendars(List<Integer> mCalendars, boolean isFill) { this.isFill = isFill; rangeSelect = true; calendars.clear(); calendars.addAll(mCalendars); // invalidate(); } public boolean isRangeSelect() { return rangeSelect; } public void setRangeSelect(boolean rangeSelect) { this.rangeSelect = rangeSelect; } private boolean sameDay(int day, Calendar today) { return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH) && day == today.get(Calendar.DAY_OF_MONTH); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int preferredHeight = mDesiredDayHeight * MAX_WEEKS_IN_MONTH + mDesiredDayOfWeekHeight + mDesiredMonthHeight + getPaddingTop() + getPaddingBottom(); final int preferredWidth = mDesiredCellWidth * DAYS_IN_WEEK + (SUtils.isApi_17_OrHigher() ? getPaddingStart() : getPaddingLeft()) + (SUtils.isApi_17_OrHigher() ? getPaddingEnd() : getPaddingRight()); final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec); final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec); setMeasuredDimension(resolvedWidth, resolvedHeight); } @Override public void onRtlPropertiesChanged(/*@ResolvedLayoutDir*/ int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); requestLayout(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (!changed) { return; } // Let's initialize a completely reasonable number of variables. final int w = right - left; final int h = bottom - top; final int paddingLeft = getPaddingLeft(); final int paddingTop = getPaddingTop(); final int paddingRight = getPaddingRight(); final int paddingBottom = getPaddingBottom(); final int paddedRight = w - paddingRight; final int paddedBottom = h - paddingBottom; final int paddedWidth = paddedRight - paddingLeft; final int paddedHeight = paddedBottom - paddingTop; if (paddedWidth == mPaddedWidth || paddedHeight == mPaddedHeight) { return; } mPaddedWidth = paddedWidth; mPaddedHeight = paddedHeight; // We may have been laid out smaller than our preferred size. If so, // scale all dimensions to fit. final int measuredPaddedHeight = getMeasuredHeight() - paddingTop - paddingBottom; final float scaleH = paddedHeight / (float) measuredPaddedHeight; final int monthHeight = (int) (mDesiredMonthHeight * scaleH); final int cellWidth = mPaddedWidth / DAYS_IN_WEEK; mMonthHeight = monthHeight; mDayOfWeekHeight = (int) (mDesiredDayOfWeekHeight * scaleH); mDayHeight = (int) (mDesiredDayHeight * scaleH); mCellWidth = cellWidth; // Compute the largest day selector radius that's still within the clip // bounds and desired selector radius. final int maxSelectorWidth = cellWidth / 2 + Math.min(paddingLeft, paddingRight); final int maxSelectorHeight = mDayHeight / 2 + paddingBottom; mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius, Math.min(maxSelectorWidth, maxSelectorHeight)); // Invalidate cached accessibility information. // mTouchHelper.invalidateRoot(); } private int findDayOffset() { final int offset = mDayOfWeekStart - mWeekStart; if (mDayOfWeekStart < mWeekStart) { return offset + DAYS_IN_WEEK; } return offset; } /** * 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. * * @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 */ //private int getDayAtLocation(int x, int y) { public int getDayAtLocation(int x, int y) { final int paddedX = x - getPaddingLeft(); if (paddedX < 0 || paddedX >= mPaddedWidth) { return -1; } final int headerHeight = mMonthHeight + mDayOfWeekHeight; final int paddedY = y - getPaddingTop(); if (paddedY < headerHeight || paddedY >= mPaddedHeight) { return -1; } // Adjust for RTL after applying padding. final int paddedXRtl; if (SUtils.isLayoutRtlCompat(this)) { paddedXRtl = mPaddedWidth - paddedX; } else { paddedXRtl = paddedX; } final int row = (paddedY - headerHeight) / mDayHeight; final int col = (paddedXRtl * DAYS_IN_WEEK) / mPaddedWidth; final int index = col + row * DAYS_IN_WEEK; final int day = index + 1 - findDayOffset(); if (!isValidDayOfMonth(day)) { return -1; } return day; } /** * Calculates the bounds of the specified day. * * @param id the day of the month * @param outBounds the rect to populate with bounds */ private boolean getBoundsForDay(int id, Rect outBounds) { if (!isValidDayOfMonth(id)) { return false; } final int index = id - 1 + findDayOffset(); // Compute left edge, taking into account RTL. final int col = index % DAYS_IN_WEEK; final int colWidth = mCellWidth; final int left; if (SUtils.isLayoutRtlCompat(this)) { left = getWidth() - getPaddingRight() - (col + 1) * colWidth; } else { 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 top = getPaddingTop() + headerHeight + row * rowHeight; outBounds.set(left, top, left + colWidth, top + rowHeight); return true; } /** * 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 boolean onDayClicked(int day) { Log.i("TAG", "day==" + day); if (!isValidDayOfMonth(day) || !isDayEnabled(day)) { return false; } Log.i("TAG", "onDayClicke"); if (mOnDayClickListener != null) { final Calendar date = Calendar.getInstance(); date.set(mYear, mMonth, day); return mOnDayClickListener.onDayClick(this, date); } // This is a no-op if accessibility is turned off. // mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED); return true; } public Calendar composeDate(int day) { if (!isValidDayOfMonth(day) || !isDayEnabled(day)) { return null; } final Calendar date = Calendar.getInstance(); date.set(mYear, mMonth, day); return date; } public class ActivatedDays { public int startingDay = -1, endingDay = -1, together = -1; public boolean isClick; public SelectedDate.Type selectedDateType; @SuppressWarnings("unused") public void reset() { startingDay = endingDay = -1; } public boolean isValid() { return startingDay != -1 && endingDay != -1; } public boolean isActivated(int day) { return day >= startingDay && day <= endingDay; } public boolean isStartingDayOfRange(int day) { return day == startingDay; } public boolean isEndingDayOfRange(int day) { return day == endingDay; } public boolean isSingleDay() { return startingDay == endingDay; } public boolean isSelected(int day) { return selectedDateType == SelectedDate.Type.SINGLE && startingDay == day && endingDay == day; } public boolean isSelectedAndTogether(int day) { if (isTogether(day)) { Log.i("TAG", "calendars=" + calendars.contains(day)); if (calendars.contains(day) && rangeSelect) { rangeSelect = false; return true; } else if (calendars.contains(day) && !rangeSelect) { Log.i("TAG", "mInitialTarget=" + mInitialTarget); if (mInitialTarget != -1) { calendars.remove(new Integer(day)); mActivatedDays.together = -1; return false; } else { return true; } } else { calendars.add(day); return true; } } else if (calendars.contains(day) && day != -1) { return true; } else { return false; } } public boolean isTogether(int day) { return (together != -1 && together == day && selectedDateType == SelectedDate.Type.TOGETHER); } // experimental @SuppressWarnings("unused") public int getSelectedDay() { if (selectedDateType == SelectedDate.Type.SINGLE && startingDay == endingDay) { return startingDay; } return -1; } @SuppressWarnings("unused") public boolean hasSelectedDay() { return selectedDateType == SelectedDate.Type.SINGLE && startingDay == endingDay && startingDay != -1; } /** * Kind of a hack. Used in conjunction with isSingleDay() to determine * the side on which the curved surface will fall. * We assume that if this is the starting day of * this month, its also the end of selected date range. If this returns false, * we consider the selectedDay to be the beginning of selected date range. * * @return true if startingDay is the first day of the month, false otherwise. */ public boolean isStartOfMonth() { return startingDay == 1; } } /** * Handles callbacks when the user clicks on a time object. */ public interface OnDayClickListener { boolean onDayClick(SimpleMonthView view, Calendar day); } public interface OnRangeClickListener { void onRangeSelected(ArrayList<Integer> calenders, int position); } @Override protected void onAttachedToWindow() { Log.i("TAG", "onAttachedToWindow"); super.onAttachedToWindow(); } }