package com.mm.android.deviceaddmodule.mobilecommon.widget.calendar.month; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.RectF; import android.graphics.Typeface; import android.view.MotionEvent; import android.view.View; import com.mm.android.deviceaddmodule.R; import com.mm.android.deviceaddmodule.mobilecommon.utils.UIUtils; import com.mm.android.deviceaddmodule.mobilecommon.widget.calendar.CalendarUtils; import java.security.InvalidParameterException; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class MonthSelectView extends View { public static final String COLOR_NORMAL_DAY = "#ff999999"; public static final String COLOR_ENABLE_DAY = "#ff999999"; public static final String COLOR_SELECTED_BG_THIS = "#f18d00"; public static final String COLOR_SELECTED_BG_LAST = "#7396f7"; public static final String COLOR_SELECTED_BG1 = "#f76163"; public static final String COLOR_SELECTED_BG2 = "#2e9b95"; public static final String COLOR_SELECTED_BG3 = "#4ea7f2"; public static final String TYPE_FACE_SANS_SERIF = "Arial Regular"; public static final int TEXT_SIZE_DAY = 16; public static final int TEXT_SIZE_MONTH = 16; public static final int TEXT_SIZE_NAME = 10; public static final int HEIGHT_HEADER_MONTH = 50; public static final int DIMEN_SELECTED_DAY_RADIUS = 22; public static final int HEIGHT_CALENDAR = 270; public static final String VIEW_PARAMS_HEIGHT = "height"; public static final String VIEW_PARAMS_YEAR = "year"; private static final int SELECTED_CIRCLE_ALPHA = 128; private static int DEFAULT_HEIGHT = 32; private static final int DEFAULT_NUM_ROWS = 4; private static int DAY_SELECTED_CIRCLE_SIZE; private static int DAY_SEPARATOR_WIDTH = 1; private static int MINI_DAY_NUMBER_TEXT_SIZE; private static int MIN_HEIGHT = 10; private static int MONTH_HEADER_SIZE; private static int MONTH_LABEL_TEXT_SIZE; private int mPadding = 0; private String mDayOfWeekTypeface; private String mMonthTitleTypeface; private Paint mMonthNumPaint; private Paint mMonthTitlePaint; private Paint mSelectedCirclePaint; private Paint mLinePaint; private int mEnableDayTextColor; private int mMonthTextColor; private int mDayTextColor; private int mDayNumColor; private int mSelectedColorThis; private int mSelectedColorLast; private final StringBuilder mStringBuilder; private int mNumDays = 3; private int mNumCells = mNumDays; private Boolean mDrawRect; private int mRowHeight = DEFAULT_HEIGHT; private int mWidth; private int mYear; // 可以选择的最小时间 private Calendar minCalendar; // 当前时间 private Calendar currentCalendar; // 月标题使用的Calendar private final Calendar mTitleCalendar; private int mNumRows = DEFAULT_NUM_ROWS; private OnDayClickListener mOnDayClickListener; private Map selectedMonthList; private Set selectedColors; public MonthSelectView(Context context, TypedArray typedArray) { super(context); mSelectedColorThis = typedArray.getColor(R.styleable.DayPickerView_colorSelectedBgThis, Color.parseColor(COLOR_SELECTED_BG_THIS)); mSelectedColorLast = typedArray.getColor(R.styleable.DayPickerView_colorSelectedBgLast, Color.parseColor(COLOR_SELECTED_BG_LAST)); mDrawRect = typedArray.getBoolean(R.styleable.DayPickerView_drawRoundRect, false); mTitleCalendar = Calendar.getInstance(); mDayOfWeekTypeface = TYPE_FACE_SANS_SERIF; mMonthTitleTypeface = TYPE_FACE_SANS_SERIF; mEnableDayTextColor = typedArray.getColor(R.styleable.DayPickerView_colorEnableDay, Color.parseColor(COLOR_ENABLE_DAY)); mMonthTextColor = typedArray.getColor(R.styleable.DayPickerView_colorMonthName, Color.parseColor(COLOR_NORMAL_DAY)); mDayTextColor = typedArray.getColor(R.styleable.DayPickerView_colorDayName, Color.parseColor(COLOR_NORMAL_DAY)); mDayNumColor = typedArray.getColor(R.styleable.DayPickerView_colorNormalDay, Color.parseColor(COLOR_NORMAL_DAY)); mStringBuilder = new StringBuilder(50); MINI_DAY_NUMBER_TEXT_SIZE = typedArray.getDimensionPixelSize(R.styleable.DayPickerView_textSizeDay, UIUtils.sp2px(context, TEXT_SIZE_DAY)); MONTH_LABEL_TEXT_SIZE = typedArray.getDimensionPixelSize(R.styleable.DayPickerView_textSizeMonth, UIUtils.sp2px(context, TEXT_SIZE_MONTH)); MONTH_HEADER_SIZE = typedArray.getDimensionPixelOffset(R.styleable.DayPickerView_headerMonthHeight, UIUtils.dp2px(context, HEIGHT_HEADER_MONTH)); DAY_SELECTED_CIRCLE_SIZE = typedArray.getDimensionPixelSize(R.styleable.DayPickerView_selectedDayRadius, UIUtils.dp2px(context, DIMEN_SELECTED_DAY_RADIUS)); mRowHeight = ((typedArray.getDimensionPixelSize(R.styleable.DayPickerView_calendarHeight, UIUtils.dp2px(context, HEIGHT_CALENDAR)) - MONTH_HEADER_SIZE) / 6); initView(); } private void drawMonthTitle(Canvas canvas) { int x = (mWidth + 2 * mPadding) / 2; int y = MONTH_HEADER_SIZE / 2 + (MONTH_LABEL_TEXT_SIZE / 3); canvas.drawText(getYearString(), x, y, mMonthTitlePaint); canvas.drawLine(0, MONTH_HEADER_SIZE, mWidth , MONTH_HEADER_SIZE, mLinePaint); } private String getYearString() { int year = mTitleCalendar.get(Calendar.YEAR); return getContext().getResources().getString(R.string.device_manager_report_month_select_year, year); } private void onDayClick(SelectedMonth selectedMonth) { if (mOnDayClickListener == null) { return; } dayClick(selectedMonth); } private void dayClick(SelectedMonth selectedMonth) { if (selectedMonthList.containsKey(CalendarUtils.getKeyBySelectedMonth(selectedMonth))) { SelectedMonth c = selectedMonthList.get(CalendarUtils.getKeyBySelectedMonth(selectedMonth)); if (selectedMonth.equals(c)) { if (!selectedMonth.isLastMonth() && !selectedMonth.isThisMonth()) { selectedMonth.setColor(c.getColor()); selectedColors.add(selectedMonth.getColor()); } } } else { if (!selectedMonth.isLastMonth() && !selectedMonth.isThisMonth()) { Iterator it = selectedColors.iterator(); if (it.hasNext()) { selectedMonth.setColor(it.next()); it.remove(); } } } mOnDayClickListener.onDayClick(this, selectedMonth); } protected void drawMonthNums(Canvas canvas) { int y = (mRowHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH + MONTH_HEADER_SIZE; int lineY = mRowHeight + MONTH_HEADER_SIZE - DAY_SEPARATOR_WIDTH; int paddingDay = (mWidth - 2 * mPadding) / (2 * mNumDays); int dayOffset = 0; int month = 0; //取12月的文字长度(这里偷懒不再遍历最长的文字,直接取12月份的) float textWidth = mMonthNumPaint.measureText(getContext().getResources().getString(R.string.device_manager_report_month_select_month, 12)) - 10; while (month < mNumCells) { int x = paddingDay * (1 + dayOffset * 2) + mPadding; if (selectedMonthList.containsKey(CalendarUtils.getKeyByYearMonth(mYear, month))) { setModeOfDaySelectedBgColor(month); drawSelectedBg(canvas, x, y, textWidth); } Calendar calendar = Calendar.getInstance(); calendar.set(mYear, month, 1); if (selectedMonthList.containsKey(CalendarUtils.getKeyByYearMonth(mYear, month))){ //选中的白色 mMonthNumPaint.setColor(Color.WHITE); } else if (CalendarUtils.isInTime(calendar.getTime(), minCalendar.getTime(), currentCalendar.getTime(), "yyyy-MM")) { //范围内的正常色 mMonthNumPaint.setColor(mDayNumColor); } else { // 超过范围的日期置灰 mMonthNumPaint.setColor(mEnableDayTextColor); } canvas.drawText(getContext().getResources().getString(R.string.device_manager_report_month_select_month, month + 1), x, y, mMonthNumPaint); dayOffset++; if (dayOffset == mNumDays) { canvas.drawLine(0, lineY, mWidth , lineY, mLinePaint); dayOffset = 0; y += mRowHeight; lineY += mRowHeight; } month++; } } // 设置选中数字的背景颜色 private void setModeOfDaySelectedBgColor(int month) { SelectedMonth c = selectedMonthList.get(CalendarUtils.getKeyByYearMonth(mYear, month)); if (c.isThisMonth()) { c.setColor(mSelectedColorThis); } else if (c.isLastMonth()) { c.setColor(mSelectedColorLast); } mSelectedCirclePaint.setColor(c.getColor()); } // 画数字背景 private void drawSelectedBg(Canvas canvas, int x, int y, float textWidth) { if (mDrawRect) { RectF rectF = new RectF(x - DAY_SELECTED_CIRCLE_SIZE, (y - MINI_DAY_NUMBER_TEXT_SIZE / 3) - DAY_SELECTED_CIRCLE_SIZE, x + DAY_SELECTED_CIRCLE_SIZE, (y - MINI_DAY_NUMBER_TEXT_SIZE / 3) + DAY_SELECTED_CIRCLE_SIZE); canvas.drawRoundRect(rectF, 10.0f, 10.0f, mSelectedCirclePaint); } else { int centerX = x; //文字中心X int centerY = y - MINI_DAY_NUMBER_TEXT_SIZE / 3; //文字中心Y int offset = 5; //由于设置了抗锯齿,防止部分手机上出现拼接出现间隙。重叠绘制一小部分 RectF center = new RectF(centerX - textWidth/2, centerY - DAY_SELECTED_CIRCLE_SIZE, centerX + textWidth/2, centerY + DAY_SELECTED_CIRCLE_SIZE); canvas.drawRect(center, mSelectedCirclePaint); //左半边圆弧 RectF ovalLeft = new RectF(centerX + offset - textWidth/2 - DAY_SELECTED_CIRCLE_SIZE, centerY - DAY_SELECTED_CIRCLE_SIZE, centerX + offset - textWidth/2 + DAY_SELECTED_CIRCLE_SIZE, centerY + DAY_SELECTED_CIRCLE_SIZE); canvas.drawArc(ovalLeft, 90, 180, true, mSelectedCirclePaint); //右半边圆弧 RectF ovalRight = new RectF(centerX - offset + textWidth/2 - DAY_SELECTED_CIRCLE_SIZE , centerY - DAY_SELECTED_CIRCLE_SIZE, centerX - offset + textWidth/2 + DAY_SELECTED_CIRCLE_SIZE, centerY + DAY_SELECTED_CIRCLE_SIZE); canvas.drawArc(ovalRight, -90, 180, true, mSelectedCirclePaint); } } public SelectedMonth getDayFromLocation(float x, float y) { int padding = mPadding; if ((x < padding) || (x > mWidth - mPadding)) { return null; } int yDay = (int) (y - MONTH_HEADER_SIZE) / mRowHeight; int month = ((int) ((x - padding) * mNumDays / (mWidth - padding - mPadding))) + yDay * mNumDays; return new SelectedMonth(mYear, month); } protected void initView() { mMonthTitlePaint = new Paint(); // mMonthTitlePaint.setFakeBoldText(true); mMonthTitlePaint.setAntiAlias(true); mMonthTitlePaint.setTextSize(MONTH_LABEL_TEXT_SIZE); mMonthTitlePaint.setTypeface(Typeface.create(mMonthTitleTypeface, Typeface.NORMAL)); mMonthTitlePaint.setColor(mMonthTextColor); mMonthTitlePaint.setTextAlign(Align.CENTER); mMonthTitlePaint.setStyle(Style.FILL); mSelectedCirclePaint = new Paint(); mSelectedCirclePaint.setFakeBoldText(true); mSelectedCirclePaint.setAntiAlias(true); mSelectedCirclePaint.setTextAlign(Align.CENTER); mSelectedCirclePaint.setStyle(Style.FILL); mSelectedCirclePaint.setAlpha(SELECTED_CIRCLE_ALPHA); mMonthNumPaint = new Paint(); mMonthNumPaint.setAntiAlias(true); mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); mMonthNumPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.NORMAL)); mMonthNumPaint.setStyle(Style.FILL); mMonthNumPaint.setTextAlign(Align.CENTER); mMonthNumPaint.setFakeBoldText(false); mLinePaint = new Paint(); mLinePaint.setAntiAlias(true); mLinePaint.setStyle(Style.FILL); mLinePaint.setStrokeWidth(UIUtils.dip2px(getContext(), 0.5f)); mLinePaint.setColor(Color.parseColor("#eeeeee")); } protected void onDraw(Canvas canvas) { drawMonthTitle(canvas); drawMonthNums(canvas); } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows + MONTH_HEADER_SIZE); } protected void onSizeChanged(int w, int h, int oldw, int oldh) { mWidth = w; } public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { SelectedMonth selectedMonth = getDayFromLocation(event.getX(), event.getY()); if (selectedMonth != null) { Calendar calendar = Calendar.getInstance(); calendar.set(selectedMonth.getYear(), selectedMonth.getMonth(), 1); // 超过范围不能点击 if (!calendar.before(minCalendar) && !calendar.after(currentCalendar)) { onDayClick(selectedMonth); } } } return true; } public void reuse() { mNumRows = DEFAULT_NUM_ROWS; requestLayout(); } public void setMonthParams(HashMap params) { if (!params.containsKey(VIEW_PARAMS_YEAR)) { throw new InvalidParameterException("You must specify year for this view"); } setTag(params); if (params.containsKey(VIEW_PARAMS_HEIGHT)) { mRowHeight = params.get(VIEW_PARAMS_HEIGHT); if (mRowHeight < MIN_HEIGHT) { mRowHeight = MIN_HEIGHT; } } mYear = params.get(VIEW_PARAMS_YEAR); mTitleCalendar.set(Calendar.YEAR, mYear); mNumCells = 12; mNumRows = 4; } public void setSelectedMonthList(Map selectedMonthList) { this.selectedMonthList = selectedMonthList; } public void setSelectedColors(Set selectedColors) { this.selectedColors = selectedColors; } public void setCurrentCalendar(Calendar calendar) { this.currentCalendar = calendar; } public void setMinCalendar(Calendar calendar) { this.minCalendar = calendar; } public void setOnDayClickListener(OnDayClickListener onDayClickListener) { mOnDayClickListener = onDayClickListener; } public static abstract interface OnDayClickListener { void onDayClick(MonthSelectView simpleMonthView, SelectedMonth selectedMonth); } }