参考了一些网上的资料,针对自己需求做了一些修改。没有难点,只是细节特别多。

package xxx.view;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;/*** 自定义日历控件* Created with Android Studio* Author:* Date:2017/12/21*/
public class MyCalendar extends View {private static final int SINGLE_SELECT_TYPE = 1;//单选模式private static final int DOUBLE_SELECT_TYPE = 2;//多选模式public static final String FORMAT_DATE_DIVIDE = "/";private static final int RANGE_START = 1;private static final int RANGE_END = 2;private static final int RANGE_INSIDE = 3;private static final int RANGE_OUTSIDE = 4;private int mTitleTextColor;//标题的颜色private int mSubTitleTextColor;//小标题的颜色private float mTitleTextSize;//标题的字体大小private float mSubTitleTextSize;//小标题的字体大小private int mTitleArrowLeftId, mTitleArrowRightId;private float mMonthHorizontalSpace;private float mTitleVerticalSpac;private float mSubTitleVerticalSpac;private int mTextColorWeek;//星期的颜色private float mTextSizeWeek;//星期的字体大小private int mPastDayTxtColor;//过去的日期文本的颜色private int mDayTextColor;//日期文本的颜色private float mTextSizeDay;//日期文本大小private int mSelectTextColor;//选中的文本的颜色private int mSelectBgColor;//选中背景private int mSelectRangeBgColor;//选中区间背景色private float mSelectRadius;//日期圆形背景的半径private float mLineSpac;//行间距private Paint mPaint = new Paint();//文字画笔private Paint mBgPaint = new Paint();//背景画笔private Paint mArrowPaint = new Paint();//画箭头画笔private float mTitleHeight;//顶部标题高度private float mWeekHeight;//星期高度private float mSubTitleHeight;//年月小标题高度private float mDayHeight;//日期高度private float oneHeight;//一行日期的高度,包含间距private int mColumnWidth;//每列宽度private Date mUpdateMonthDate; //当前的月份private boolean isCurrentMonth;//展示的月份是否是当前月private int mCurrentDay;//当前日private int mUpdateYear;//当前日历所在年份private int mUpdateMonth;//当前日历所在月份private int mLastSelectDay;//上一次选中的日期(避免造成重复回调请求)private int mSelectDay;//选中的日期private int mDayOfMonth;//月份天数private int mFirstDayIdx;//当月第一天位置索引,第一天是不同星期,起点也就不同private int mFirstLineNum, mLastLineNum; //第一行、最后一行能展示多少日期private int mLineNum;//日期行数private int mArrowLeftStart;//左箭头响应触摸的起始位置private int mArrowRightStart;//右箭头响应触摸的起始位置private int mArrowWidth;//箭头图标宽度private String[] mWeekArray = new String[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};private long mStartMills;//选中区域的起始点private long mEndMills;//选中区域的结束点private IClickListener mListener;private boolean showTitle;//是否显示标题private boolean showWeek;//是否显示星期private boolean showSubTitle;//是否显示年月小标题private float mPadTop, mPadBottom;private GestureDetector mGestureDetector;private int mType = SINGLE_SELECT_TYPE;public MyCalendar(Context context) {this(context, null);}public MyCalendar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyCalendar(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCalendar, defStyleAttr, 0);mTitleArrowLeftId = a.getResourceId(R.styleable.MyCalendar_arrowLeftResId, 0);showTitle = a.getBoolean(R.styleable.MyCalendar_showTitle, true);showWeek = a.getBoolean(R.styleable.MyCalendar_showWeek, true);showSubTitle = a.getBoolean(R.styleable.MyCalendar_showSubTitle, false);mTitleArrowRightId = a.getResourceId(R.styleable.MyCalendar_arrowRightResId, 0);mMonthHorizontalSpace = a.getDimension(R.styleable.MyCalendar_titleHozSpace, 20);mTitleVerticalSpac = a.getDimension(R.styleable.MyCalendar_titleVerticalSpace, 20);mSubTitleVerticalSpac = a.getDimension(R.styleable.MyCalendar_subTitleVerticalSpace, 20);mTitleTextColor = a.getColor(R.styleable.MyCalendar_titleTextColor, Color.BLACK);mSubTitleTextColor = a.getColor(R.styleable.MyCalendar_subTitleTextColor, Color.BLACK);mTitleTextSize = a.getDimension(R.styleable.MyCalendar_titleTextSize, 100);mPadTop = a.getDimension(R.styleable.MyCalendar_padTop, 10);mPadBottom = a.getDimension(R.styleable.MyCalendar_padBottom, 10);mSubTitleTextSize = a.getDimension(R.styleable.MyCalendar_subTitleTextSize, 100);mTextColorWeek = a.getColor(R.styleable.MyCalendar_weekTextColor, Color.BLACK);mTextSizeWeek = a.getDimension(R.styleable.MyCalendar_weekTextSize, 70);mDayTextColor = a.getColor(R.styleable.MyCalendar_dayTextColor, Color.GRAY);mPastDayTxtColor = a.getColor(R.styleable.MyCalendar_pastDayTextColor, Color.GRAY);mTextSizeDay = a.getDimension(R.styleable.MyCalendar_dayTextSize, 70);mSelectTextColor = a.getColor(R.styleable.MyCalendar_selectTextColor, Color.YELLOW);mSelectBgColor = a.getColor(R.styleable.MyCalendar_selectBgColor, Color.YELLOW);mSelectRangeBgColor = a.getColor(R.styleable.MyCalendar_selectRangeBgColor, Color.YELLOW);mSelectRadius = a.getDimension(R.styleable.MyCalendar_selectRadius, 20);mLineSpac = a.getDimension(R.styleable.MyCalendar_daylineSpace, 20);a.recycle();init();}public void setType(int type) {mType = type;}/** 计算相关常量,构造方法中调用 */private void init() {mGestureDetector = new GestureDetector(getContext(), new GestureCallback());mPaint.setAntiAlias(true); //抗锯齿mBgPaint.setAntiAlias(true); //抗锯齿mPaint.setTextSize(mTitleTextSize);mTitleHeight = getFontHeight(mPaint) + 2 * mTitleVerticalSpac;//标题高度mPaint.setTextSize(mTextSizeWeek);mWeekHeight = getFontHeight(mPaint);//星期高度mPaint.setTextSize(mSubTitleTextSize);mSubTitleHeight = getFontHeight(mPaint) + 2 * mSubTitleVerticalSpac;//小标题高度mPaint.setTextSize(mTextSizeDay);mDayHeight = getFontHeight(mPaint);//单行日期高度oneHeight = mLineSpac + mDayHeight;/*每行高度 = 行间距 + 日期字体高度*/updateMonth(new Date());//默认当前月份}private void computeRowsAndColumns(Calendar calendar) {mUpdateYear = calendar.get(Calendar.YEAR);mUpdateMonth = calendar.get(Calendar.MONTH) + 1;mDayOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);/*该月天数*/mFirstDayIdx = calendar.get(Calendar.DAY_OF_WEEK) - 1;/*第一行1号显示在什么位置(星期几)*/mLineNum = 1;mFirstLineNum = 7 - mFirstDayIdx;/*第一行能展示的天数*/mLastLineNum = 0;/*最后一行能展示的天数*/int remainDays = mDayOfMonth - mFirstLineNum;while (remainDays > 7) {mLineNum++;remainDays -= 7;}if (remainDays > 0) {mLineNum++;mLastLineNum = remainDays;}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthSize = MeasureSpec.getSize(widthMeasureSpec);//获取宽的尺寸,宽度 = 填充父窗体mColumnWidth = widthSize / 7;int height = (int) (mLineNum * oneHeight + mPadTop + mPadBottom);height = (int) computeY(height);setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), height);}@Overrideprotected void onDraw(Canvas canvas) {if (showTitle) {drawTitle(canvas);}if (showWeek) {drawWeek(canvas);}if (showSubTitle) {drawSubTitle(canvas);}drawDay(canvas);}/** 画年月小标题 */private void drawSubTitle(Canvas canvas) {mPaint.setTextSize(mSubTitleTextSize);mPaint.setColor(mSubTitleTextColor);float titleStart = getWidth() / 14;float y = mSubTitleVerticalSpac + getFontLeading(mPaint);if (showTitle) {y += mTitleHeight;}if (showWeek) {y += mWeekHeight;}String subTitle = getMonthYear(mUpdateMonthDate);canvas.drawText(subTitle, titleStart, y, mPaint);//画年月标题}/** 绘制年月、左右箭头 */private void drawTitle(Canvas canvas) {mPaint.setTextSize(mTitleTextSize);mPaint.setColor(mTitleTextColor);float textLen = getFontlength(mPaint, getMonthStr(mUpdateMonthDate));float titleStart = (getWidth() - textLen) / 2;float y = mPadTop + mTitleVerticalSpac + getFontLeading(mPaint);canvas.drawText(getMonthStr(mUpdateMonthDate), titleStart, y, mPaint);//画年月标题if (mTitleArrowLeftId != 0) {//画左箭头Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mTitleArrowLeftId);int arrowHeight = bitmap.getHeight();mArrowWidth = bitmap.getWidth();mArrowLeftStart = (int) (titleStart - 2 * mMonthHorizontalSpace - mArrowWidth);float left = mArrowLeftStart + mMonthHorizontalSpace;float top = mPadTop + (mTitleHeight - arrowHeight) / 2;canvas.drawBitmap(bitmap, left, top, mArrowPaint);}if (mTitleArrowRightId != 0) {//画右箭头Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mTitleArrowRightId);int arrowHeight = bitmap.getHeight();mArrowRightStart = (int) (titleStart + textLen);float left = mArrowRightStart + mMonthHorizontalSpace;float top = mPadTop + (mTitleHeight - arrowHeight) / 2;canvas.drawBitmap(bitmap, left, top, mArrowPaint);}}/** 绘制星期 */private void drawWeek(Canvas canvas) {mPaint.setTextSize(mTextSizeWeek);mPaint.setColor(mTextColorWeek);float y = mPadTop + getFontLeading(mPaint);if (showTitle) {y += mTitleHeight;}for (int i = 0; i < mWeekArray.length; i++) {int len = (int) getFontlength(mPaint, mWeekArray[i]);float x = i * mColumnWidth + (mColumnWidth - len) / 2;canvas.drawText(mWeekArray[i], x, y, mPaint);}}/** 绘制日期 */private void drawDay(Canvas canvas) {float lineTop = computeY(mPadTop);//某行开始绘制的Y坐标,第一行开始的y坐标为标题高度+星期部分高度for (int line = 0; line < mLineNum; line++) {if (line == 0) {//第一行drawDay(canvas, lineTop, mFirstLineNum, 0, mFirstDayIdx);} else if (line == mLineNum - 1) {//最后一行lineTop += oneHeight;int overDay = mFirstLineNum + (line - 1) * 7;drawDay(canvas, lineTop, mLastLineNum, overDay, 0);} else {//满行lineTop += oneHeight;int overDay = mFirstLineNum + (line - 1) * 7;drawDay(canvas, lineTop, 7, overDay, 0);}}}private float computeY(float y) {if (showTitle) {y += mTitleHeight;}if (showWeek) {y += mWeekHeight;}if (showSubTitle) {y += mSubTitleHeight;}return y;}/*** 绘制某一行的日期** @param top        顶部坐标* @param count      此行需要绘制的日期数量* @param overDay    已经绘制过的日期,从overDay+1开始绘制* @param startIndex 此行第一个日期的星期索引*/private void drawDay(Canvas canvas, float top, int count, int overDay, int startIndex) {mPaint.setTextSize(mTextSizeDay);float dayTextLeading = getFontLeading(mPaint);for (int i = 0; i < count; i++) {int left = (startIndex + i) * mColumnWidth;int day = (overDay + i + 1);float centerX = left + mColumnWidth / 2;float centerY = top + mLineSpac + mDayHeight / 2;drawSelectRange(canvas, centerX, centerY, day, left);String dayStr = day + "";int dayTxtLen = (int) getFontlength(mPaint, dayStr);int x = left + (mColumnWidth - dayTxtLen) / 2;canvas.drawText(dayStr, x, top + mLineSpac + dayTextLeading, mPaint);}}/** 画选中状态 */private void drawSelectRange(Canvas canvas, float centerX, float centerY, int day, int left) {switch (beyondRange(day)) {case RANGE_START:if (mEndMills > mStartMills) {mBgPaint.setColor(mSelectRangeBgColor);Path startPath = new Path();float startY = centerY + mSelectRadius;startPath.moveTo(centerX, startY);RectF startRect = new RectF(centerX - mSelectRadius, centerY - mSelectRadius,centerX + mSelectRadius, centerY + mSelectRadius);startPath.arcTo(startRect, 90, 180);startPath.lineTo(centerX + mColumnWidth / 2, centerY - mSelectRadius);startPath.lineTo(centerX + mColumnWidth / 2, centerY + mSelectRadius);startPath.close();canvas.drawPath(startPath, mBgPaint);}mBgPaint.setColor(mSelectBgColor);canvas.drawCircle(centerX, centerY, mSelectRadius, mBgPaint);mPaint.setColor(mSelectTextColor);break;case RANGE_END:mBgPaint.setColor(mSelectRangeBgColor);Path endPath = new Path();float endY = centerY - mSelectRadius;endPath.moveTo(centerX, endY);RectF endRect = new RectF(centerX - mSelectRadius, centerY - mSelectRadius,centerX + mSelectRadius, centerY + mSelectRadius);endPath.arcTo(endRect, -90, 180);endPath.lineTo(centerX - mColumnWidth / 2, centerY + mSelectRadius);endPath.lineTo(centerX - mColumnWidth / 2, centerY - mSelectRadius);endPath.close();canvas.drawPath(endPath, mBgPaint);mBgPaint.setColor(mSelectBgColor);canvas.drawCircle(centerX, centerY, mSelectRadius, mBgPaint);mPaint.setColor(mSelectTextColor);break;case RANGE_INSIDE:mBgPaint.setColor(mSelectRangeBgColor);RectF rect = new RectF(left, centerY - mSelectRadius, left + mColumnWidth,centerY + mSelectRadius);canvas.drawRect(rect, mBgPaint);mPaint.setColor(mSelectTextColor);break;default:mPaint.setColor(mDayTextColor);if (isCurrentMonth && day < mCurrentDay) {/*不可选的日期颜色(今天之前的日期),按需求展示本月及之后12个月份,那么当前月为最小的月份,如果不是当前月,那么一定是今天之后的日期*/mPaint.setColor(mPastDayTxtColor);} else {//普通日期文字颜色mPaint.setColor(mDayTextColor);}if (mSelectDay == day) {//绘制选中的日期mPaint.setColor(mSelectTextColor);mBgPaint.setColor(mSelectBgColor);canvas.drawCircle(centerX, centerY, mSelectRadius, mBgPaint);}break;}}/** 相对于选中区间的位置(在区间内、区间外、区间头部、区间尾部) */private int beyondRange(int day) {Date todayDate = dayStr2Date(mUpdateYear + FORMAT_DATE_DIVIDE + mUpdateMonth + FORMAT_DATE_DIVIDE + day);if (todayDate == null) {return RANGE_OUTSIDE;}long todayMills = todayDate.getTime();if (mStartMills == todayMills) {return RANGE_START;} else if (mStartMills > todayMills) {return RANGE_OUTSIDE;} else {if (mEndMills < todayMills) {return RANGE_OUTSIDE;} else if (mEndMills == todayMills) {return RANGE_END;} else {return RANGE_INSIDE;}}}/** 获取月份标题 */private String getMonthStr(Date month) {SimpleDateFormat df = new SimpleDateFormat("yyyy/MM", Locale.getDefault());return df.format(month);}private Date str2Date(String str) {try {SimpleDateFormat df = new SimpleDateFormat("yyyy/MM", Locale.getDefault());return df.parse(str);} catch (Exception e) {e.printStackTrace();return null;}}private Date dayStr2Date(String str) {try {SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault());return df.parse(str);} catch (Exception e) {e.printStackTrace();return null;}}@Overridepublic boolean onTouchEvent(MotionEvent event) {return mGestureDetector.onTouchEvent(event);}/** 焦点滑动 */private void touchFocusMove(final PointF point, boolean isEndEvent) {if (isTouchTitle(point)) {//事件在标题上if (!isEndEvent || mListener == null) {//标题只有在事件结束后才响应return;}if (isTouchLeftArrow(point)) {//点击左箭头monthChange(-1);} else if (isTouchRightArrow(point)) {//点击右箭头monthChange(1);}} else if (isTouchDay(point)) {//触摸的是日期if (!isEndEvent) {return;}touchDay(point, isEndEvent);}}/** 是否触摸的是左箭头 */private boolean isTouchLeftArrow(final PointF point) {return point.x >= mArrowLeftStart && point.x < (mArrowLeftStart + 2 * mMonthHorizontalSpace+ mArrowWidth);}/** 是否触摸的是右箭头 */private boolean isTouchRightArrow(final PointF point) {return point.x > mArrowRightStart && point.x < (mArrowRightStart + 2 * mMonthHorizontalSpace+ mArrowWidth);}/** 是否触摸的是标题(年月、箭头) */private boolean isTouchTitle(final PointF point) {if (!showTitle) {return false;}return point.y > mPadTop && point.y <= mPadTop + mTitleHeight;}/** 是否触摸的是日期 */private boolean isTouchDay(final PointF point) {return point.y > computeY(mPadTop);}/** 事件点在 日期区域 范围内 */private void touchDay(final PointF point, boolean isEndEvent) {float top = computeY(mPadTop);float diffY = point.y - top;int focusLine = (int) (diffY / oneHeight);if (diffY % oneHeight > 0) {focusLine++;}boolean availability = focusLine <= mLineNum;//触摸是否在日期范围内if (availability) {int xIdx = (int) Math.ceil(point.x / mColumnWidth);//根据X坐标找到具体的焦点日期if (xIdx <= 0) {xIdx = 1;}if (xIdx > 7) {xIdx = 7;}if (focusLine == 1) {//第一行if (xIdx <= mFirstDayIdx) {//到开始空位updateSelectedDay(mSelectDay, true);} else {updateSelectedDay(xIdx - mFirstDayIdx, isEndEvent);}} else if (focusLine == mLineNum) {//最后一行if (xIdx > mLastLineNum) {//到结尾空位updateSelectedDay(mSelectDay, true);} else {updateSelectedDay(mFirstLineNum + (focusLine - 2) * 7 + xIdx, isEndEvent);}} else {updateSelectedDay(mFirstLineNum + (focusLine - 2) * 7 + xIdx, isEndEvent);}} else {//超出日期区域后,视为事件结束,响应最后一个选择日期的回调updateSelectedDay(mSelectDay, true);}}/** 设置选中的日期 */private void updateSelectedDay(int day, boolean eventEnd) {if (isCurrentMonth && day < mCurrentDay) {//所在月份是当前月,且触摸的日期是今天之前,则不可选中return;}Calendar calendar = Calendar.getInstance();calendar.setTime(mUpdateMonthDate);int year = calendar.get(Calendar.YEAR);int month = calendar.get(Calendar.MONTH) + 1;Date date = dayStr2Date(year + FORMAT_DATE_DIVIDE + month + FORMAT_DATE_DIVIDE + day);if (date == null) {return;}if (mType == SINGLE_SELECT_TYPE) {mSelectDay = day;invalidate();if (mListener != null && eventEnd && mLastSelectDay != mSelectDay) {mLastSelectDay = mSelectDay;mListener.onDayClick(date.getTime());}} else {if (mStartMills != 0) {if (date.getTime() < mStartMills) {mStartMills = date.getTime();} else {mEndMills = date.getTime();}invalidate();} /*else {mStartMills = date.getTime();invalidate();}*/}}public void updateSelectedDay(long selectMills) {if (selectMills == 0) {return;}Calendar calendar = Calendar.getInstance();calendar.setTime(new Date(selectMills));mSelectDay = calendar.get(Calendar.DAY_OF_MONTH);invalidate();}/** 月份增减 */private void monthChange(int change) {Calendar calendar = Calendar.getInstance();calendar.setTime(mUpdateMonthDate);calendar.add(Calendar.MONTH, change);updateMonth(calendar.getTime());invalidate();}private float getFontlength(Paint paint, String str) {return paint.measureText(str);}/** 返回指定笔的文字高度 */private float getFontHeight(Paint paint) {Paint.FontMetrics fm = paint.getFontMetrics();return fm.descent - fm.ascent;}/** 返回指定笔离文字顶部的基准距离 */private float getFontLeading(Paint paint) {Paint.FontMetrics fm = paint.getFontMetrics();return fm.leading - fm.ascent;}/** 设置月份 */public void updateMonth(Date updateMonthDate) {Calendar calendar = Calendar.getInstance();calendar.setTime(new Date());mCurrentDay = calendar.get(Calendar.DAY_OF_MONTH);//当前日期Date currentMonthDate = dayStr2Date(day2Str(new Date()));//当前月份mUpdateMonthDate = dayStr2Date(day2Str(updateMonthDate));if (currentMonthDate != null && currentMonthDate.getTime() == mUpdateMonthDate.getTime()) {isCurrentMonth = true;mSelectDay = mCurrentDay;//当月默认选中当前日} else {isCurrentMonth = false;mSelectDay = 0;}calendar.setTime(mUpdateMonthDate);computeRowsAndColumns(calendar);}private String day2Str(Date date) {SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd", Locale.getDefault());return format.format(date);}public void clearSelect() {mSelectDay = 0;mLastSelectDay = 0;invalidate();}public void setSelectRange(long startMills, long endMills) {if (startMills > endMills) {return;}mStartMills = startMills;mEndMills = endMills;invalidate();}public void showWeek(boolean showWeek) {this.showWeek = showWeek;invalidate();}public void setStartMills(long startMills) {mStartMills = startMills;invalidate();}public void setEndMills(long endMills) {mEndMills = endMills;invalidate();}public long getStartMills() {return mStartMills;}public long getEndMills() {return mEndMills;}public boolean isStartMills() {return mStartMills == 0;}public void setOnClickListener(IClickListener listener) {this.mListener = listener;}public class GestureCallback extends GestureDetector.SimpleOnGestureListener {private PointF mFocusPoint = new PointF();//焦点坐标@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {mFocusPoint.set(e.getX(), e.getY());touchFocusMove(mFocusPoint, true);return super.onSingleTapConfirmed(e);}@Overridepublic boolean onDown(MotionEvent e) {return true;//true,否则无法触发onSingleTap、onFling等方法}}/** Desc 2017*/public static String getMonthYear(Date date) {if (date == null) {date = new Date();}long countryId = LocaleManager.Country.ID;CountryItemInfo country = DefaultCountryManager.getInstance().getCurrentCountryInfo();if (country != null) {countryId = country.id;}Locale locale = LocaleManager.getLocale(countryId);DateFormat df = new SimpleDateFormat("MMM", locale);String month = df.format(date);Calendar calendar = Calendar.getInstance();calendar.setTime(date);int year = calendar.get(Calendar.YEAR);return month + " " + year;}/** Desc 28,2017*/public static String getMonthDayYear(Date date) {if (date == null) {date = new Date();}long countryId = LocaleManager.Country.ID;CountryItemInfo country = DefaultCountryManager.getInstance().getCurrentCountryInfo();if (country != null) {countryId = country.id;}Locale locale = LocaleManager.getLocale(countryId);DateFormat df = new SimpleDateFormat("MMM dd,yyyy", locale);return df.format(date);}public interface IClickListener {void onDayClick(long mills);}
}

attr.xml

<!--自定义日历组件--><declare-styleable name="AkulakuCalendar"><attr name="padTop" format="dimension" /><attr name="padBottom" format="dimension" /><attr name="showTitle" format="boolean" />           <!--是否显示标题--><attr name="showWeek" format="boolean" />           <!--是否显示星期--><attr name="showSubTitle" format="boolean" />           <!--是否显示年月小标题--><attr name="titleTextColor" format="color" />           <!--标题字体颜色--><attr name="titleTextSize" format="dimension" />        <!--标题字体大小--><attr name="titleHozSpace" format="dimension" /><attr name="titleVerticalSpace" format="dimension" />    <!--标题月份上下间隔--><attr name="subTitleTextColor" format="color" />           <!--小标题字体颜色--><attr name="subTitleTextSize" format="dimension" />        <!--小标题字体大小--><attr name="subTitleVerticalSpace" format="dimension" />    <!--小标题月份上下间隔--><attr name="weekTextColor" format="color" />            <!--星期字体颜色--><attr name="weekTextSize" format="dimension" />         <!--星期字体大小--><attr name="dayTextSize" format="dimension" />          <!--日期字体大小--><attr name="dayTextColor" format="color" />             <!--日期字体颜色--><attr name="daylineSpace" format="dimension" />             <!--日期行间距--><attr name="pastDayTextColor" format="color" />             <!--过去的日期字体颜色--><attr name="arrowLeftResId" format="reference" />            <!--月份箭头--><attr name="arrowRightResId" format="reference" />            <!--月份箭头--><attr name="selectTextColor" format="color" />          <!--选中日期字体颜色--><attr name="selectBgColor" format="color" />                 <!--选中日期背景--><attr name="selectRangeBgColor" format="color" />                 <!--选中日期区间背景--><attr name="selectRadius" format="dimension" />         <!--选中日期背景半径--></declare-styleable> 

效果图:单选效果、多选效果

Android自定义日历控件(附效果图)相关推荐

  1. Android自定义日历控件,自带农历节假日,已经开源,即取即用~

    关注本人的更多博客:http://www.cnblogs.com/liushilin/ 该自定义日历控件已经开源:github地址 可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多, ...

  2. android程序日历layout,Android使用GridLayout绘制自定义日历控件

    效果图 思路:就是先设置Gridlayout的行列数,然后往里面放置一定数目的自定义日历按钮控件,最后实现日历逻辑就可以了. 步骤: 第一步:自定义日历控件(初步) 第二步:实现自定义单个日期按钮控件 ...

  3. android+高仿+日历,android高仿钉钉和小米的自定义日历控件(支持阴历和阳历,左右无限翻页viewpager)...

    收藏 0 简介 这是一个高仿钉钉和小米的日历控件,支持快速滑动,界面缓存.想要定制化UI,使用起来非常简单,就像使用ListView一样 一些特点: 可以自定义日历控件UI 支持快速滑动 支持农历和阳 ...

  4. 自定义日历控件,可以根据需求定制属于自己的日历

    自定义日历控件,可以根据需求定制属于自己的日历 效果图 最近笔者的朋友需要写一个关于考勤的日历,效果如下,但在网上找了半天都找不到合适的,于是乎就向笔者求助,笔者本来觉得在晚上随便找个日历demo,然 ...

  5. Android考勤日历控件

    工作这么久,第一次在CSDN上写技术博客,望大家多多支持: 本篇博文给大家分享一个考勤日历控件,这里有个需求:要求显示当前月的日期,左右可以切换月份来查看日期.可以通过不同的颜色表示每天的考勤状态: ...

  6. java自定义日历控件_【无私分享】修订版干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~...

    可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧. 但是看了的小伙伴就很心急了,说楼主上传到gitHub的东西有问题,楼主下载来看了看,基本都没问题吧,没弄好的小伙伴 ...

  7. 【无私分享】修订版干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~...

    可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧. 没有看的小伙伴如果有兴趣的话可以去看看:http://www.cnblogs.com/liushilin/p/57 ...

  8. Android自定义时间控件不可选择未来时间

    本文出自:http://blog.csdn.net/dt235201314/article/details/78718066 Android自定义时间控件选择开始时间到结束时间 Android自定义时 ...

  9. 自定义日历控件背景样式

    #自定义日历控件的使用 一.DatePicker日期选择类的使用 DatePicker常用xml属性: XML属性 描述 android:calendarViewShown 设置该日期选择是否显示Ca ...

  10. Android 自定义组合控件小结

    Android 自定义组合控件小结 引言 接触Android UI开发的这段时间以来,对自定义组合控件有了一定的了解,为此小结一下,本文小结内容主要讨论的是如何使用Android SDK提供的布局和控 ...

最新文章

  1. 解锁营销自动化行为触发,神策数据《营销自动化应用基准报告 2021》助力企业增长
  2. 一文搞定时间复杂度和空间复杂度
  3. project-population
  4. BX、DI、SI、BP总结
  5. vector的应用练习
  6. python读取txt文件每一行_Python3基础 file for+list 读取txt文本 并 一行一行的输出(低效率)...
  7. Docker系列教程06-实战:修改Nginx首页
  8. k3s 部署, 使用注意事项
  9. idea mysql删除_IntelliJ IDEA 配置Mysql5.7 带图文详解 视频讲解
  10. hdu-3333-Turing Tree(树状数组)
  11. 【雷达通信】基于matlab GUI多算法雷达一维恒虚警检测CFAR【含Matlab源码 874期】
  12. svn update出现database is locked
  13. 计算机网络技术摘要,计算机网络论文摘要
  14. Matlab与Excel文件的数据交换
  15. psql: could not connect to server: No such file or directory
  16. wifi mouse linux,WiFi Mouse Pro
  17. 纳税信用等级怎么划分 激励惩戒措施有哪些
  18. springmvc的配置文件
  19. 数美科技 | 黄牛也武装到牙齿,航司怎么样打赢这场无形战争?
  20. 打造自己的Javascript工具类库

热门文章

  1. element-ui 下载后本地使用
  2. 《人月神话》经典摘录
  3. mand-mobile实现md-selector多选加搜索
  4. 深入分析免流(非小白教程)
  5. dfuse Labs——展示 dfuse 的力量
  6. 数据整理—dplyr包(mutate系列)
  7. 如何快速合并PDF文件?几个方法教你合并PDF
  8. 开源工作流引擎 Workflow Core 的研究和使用教程
  9. 作为一枚程序员,每天必用的软件有哪些?
  10. 3.多边形曲线简化之Douglas-Peucker算法